In the Linux kernel, the following vulnerability has been resolved:
ntfs: avoid stale runlist element dereference in fallocate
ntfsattrfallocate() allocates holes and delayed allocations inside initialized size by looking up the current runlist element under ni->runlist.lock. The returned struct runlist_element is only a borrowed pointer into ni->runlist.rl. A writer can replace and free that array after the read lock is dropped, so later reads of rl->lcn, rl->length and rl->vcn can touch freed memory.
The buggy scenario involves two paths, with each column showing the order within that path:
ntfsattrfallocate(): 1. Take ni->runlist.lock for read. 2. Get rl from ntfsattrfindvcnnolock(). 3. Drop ni->runlist.lock. 4. Read rl->lcn, rl->length and rl->vcn.
mmap pagemkwrite: 1. Enter ntfsfilemappagemkwrite(). 2. Reach __ntfswriteiomapbegin() and ntfsattrmapcluster(). 3. Merge allocation state with ntfsrunlistsmerge(). 4. Reallocate ni->runlist.rl in ntfsrlrealloc(), freeing the old array.
Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in ntfsattrfallocate+0xbb8/0xd00
Call Trace: <TASK> dumpstacklvl+0x66/0xa0 printreport+0xce/0x630 ? ntfsattrfallocate+0xbb8/0xd00 ? srsoaliasreturnthunk+0x5/0xfbef5 ? __virtaddrvalid+0x20d/0x410 ? ntfsattrfallocate+0xbb8/0xd00 kasanreport+0xe0/0x110 ? ntfsattrfallocate+0xbb8/0xd00 ntfsattrfallocate+0xbb8/0xd00 ? lockacquire+0x2b8/0x2f0 ? __pfxntfsattrfallocate+0x10/0x10 ? 0xffffffffc0000095 ? downwrite+0x10d/0x1e0 ntfs_fallocate+0x5c9/0x1d00 ? __pfxntfsfallocate+0x10/0x10 ? srsoaliasreturnthunk+0x5/0xfbef5 ? lockacquire+0x2b8/0x2f0 ? srsoaliasreturnthunk+0x5/0xfbef5 ? selinuxfilepermission+0x3a7/0x510 vfsfallocate+0x29d/0xd30 __x64sysfallocate+0xc7/0x150 ? dosyscall64+0x81/0x6a0 dosyscall64+0x115/0x6a0 entrySYSCALL64afterhwframe+0x77/0x7f
Allocated by task 410: kasansavestack+0x33/0x60 kasansavetrack+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kvmallocnodenoprof+0x353/0x920 ntfsrlrealloc+0x3f/0x110 ntfs_runlistsmerge+0xaa3/0x3010 ntfsattrmapcluster+0x4e5/0xf80 ntfsattrfallocate+0x53f/0xd00 ntfsfallocate+0x5c9/0x1d00 vfsfallocate+0x29d/0xd30 __x64sysfallocate+0xc7/0x150 dosyscall64+0x115/0x6a0 entrySYSCALL64afterhwframe+0x77/0x7f
Freed by task 424: kasansavestack+0x33/0x60 kasansavetrack+0x14/0x30 kasansavefree_info+0x3b/0x60 __kasanslabfree+0x5f/0x80 kfree+0x307/0x580 ntfsrlrealloc+0x6f/0x110 ntfsrunlistsmerge+0x7b1/0x3010 ntfsattrmap_cluster+0x4e5/0xf80 __ntfswriteiomapbegin+0x8cd/0x2280 iomapiter+0x6de/0x11e0 iomappagemkwrite+0x391/0x650 ntfsfilemappagemkwrite+0x1ac/0x400 dopage_mkwrite+0x15c/0x280 __handlemmfault+0xd6d/0x1ca0 handlemmfault+0x19c/0x470 douseraddrfault+0x23b/0x9c0 excpagefault+0x5c/0xc0 asmexcpagefault+0x26/0x30
Fix this by copying the needed runlist fields while the read lock is still held and using only those scalar snapshots after unlocking.
After the snapshot, ntfsattrmapcluster() can also find that the range is already mapped and return balloc=false. Only call ntfsdiozerorange() when new clusters were allocated, matching the write iomap path and preserving the zero-newly-allocated-holes behavior.
{
"cna_assigner": "Linux",
"osv_generated_from": "https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/72xxx/CVE-2026-72353.json"
}