Re: ntfs3: Use after free race condition in ntfs_read_folio()?

Dirk Behme <[email protected]> Wed, 27 May 2026 12:37:48 +0200
Newsgroups dev.linux.lists.ntfs3
Message-ID <[email protected]>
On 27.05.2026 06:34, Dirk Behme wrote:
> Hi,
> 
> on a custom ARM64 board with kernel 6.1.157 we got a report about a data 
> abort Oops [1] in ntfs_read_folio() [2]. The disassembly is [3].
> 
> Analysing this the failing instruction is
> 
> 385f82e8     ldurb    w8, [x23, #-8]
> 
> with x23: ffff8d623ef35800 - 8 matching the fault address ffff8d623ef357f8.
> 
> Matching this to the C code we think the compiler summarises
> 
> struct ntfs_inode *ni = ntfs_i(inode);
> if (is_resident(ni)) {
> 
> to one assembly instruction to access the ni_flags in struct ntfs_inode 
> via the inode [4].
> 
> Getting a data abort trying to access the ni_flags, we assume that the 
> whole struct ntfs_inode has been freed, already. While missing to 
> reset / clear some reference to it. What in sum looks like a use after 
> free race condition accessing struct ntfs_inode in ntfs_read_folio().
> 
> We have searched for something obvious in more recent commits but 
> couldn't find anything what looks related. Have we missed anything?
> 
> What do you think? Any idea?
> 
> Thanks
> 
> Dirk
> 
> [1]
> 
> Unable to handle kernel paging request at virtual address ffff8d623ef357f8
> Mem abort info:
>    ESR = 0x0000000096000004
>    EC = 0x25: DABT (current EL), IL = 32 bits
>    SET = 0, FnV = 0
>    EA = 0, S1PTW = 0
>    FSC = 0x04: level 0 translation fault
> Data abort info:
>    ISV = 0, ISS = 0x00000004
>    CM = 0, WnR = 0
> [ffff8d623ef357f8] address between user and kernel address ranges
> Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP
> Modules linked in: ... ntfs3 ...
> CPU: 3 PID: 9611 Comm: binder:529_A Tainted: G           O       6.1.157 #1
> Hardware name: ARM64 custom board
> pstate: 60400005 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--)
> pc : ntfs_read_folio+0x24/0xe0 [ntfs3]
> lr : filemap_read_folio+0x6c/0x15c
> sp : ffffffc01c05bad0
> x29: ffffffc01c05bad0 x28: 00000000fffffa00 x27: 0000000000080001
> x26: 00000000ffffffff x25: ff00000000000000 x24: ffffff8d623ef4e8
> x23: ffff8d623ef35800 x22: 0000000000000000 x21: ffffffc001591414
> x20: fffffffe378c42c0 x19: fffffffe378c42c0 x18: ffffffc0109a0080
> x17: 000000001b98ee9a x16: 000000001b98ee9a x15: ffffffc00158ed90
> x14: 0000000000000007 x13: 000000000001d3a2 x12: ffffffce57fbe000
> x11: 0000000000000001 x10: 0000000000000001 x9 : 0000000000000100
> x8 : ffffff8d623ef4e7 x7 : 0000000000000000 x6 : 0000000000000000
> x5 : ffffff8ca937808c x4 : ffffffc01c05bb50 x3 : 0000000006100cca
> x2 : fffffffe378c42c0 x1 : fffffffe378c42c0 x0 : 0000000000000000
> Call trace:
>   ntfs_read_folio+0x24/0xe0 [ntfs3]
>   filemap_read_folio+0x6c/0x15c
>   do_read_cache_folio+0x144/0x338
>   read_cache_page+0x1c/0x58
>   ntfs_fill_super+0xde8/0xf20 [ntfs3]
>   get_tree_bdev+0x1c4/0x258
>   ntfs_fs_get_tree+0x18/0x28 [ntfs3]
>   vfs_get_tree+0x48/0xfc
>   do_new_mount+0x154/0x36c
>   path_mount+0x270/0x4e4
>   __arm64_sys_mount+0x1b4/0x3dc
>   invoke_syscall+0x58/0x118
>   el0_svc_common+0xb4/0xf4
>   do_el0_svc+0x24/0x80
>   el0_svc+0x2c/0x90
>   el0t_64_sync_handler+0x68/0xb4
>   el0t_64_sync+0x1a4/0x1a8
> Code: 910003fd f9400c28 aa0103f3 f9400117 (385f82e8)
> ---[ end trace 0000000000000000 ]---
> Kernel panic - not syncing: Oops: Fatal exception
> SMP: stopping secondary CPUs
> 
> [2]
> 
> static int ntfs_read_folio(struct file *file, struct folio *folio)
> {
>      struct page *page = &folio->page;
>      int err;
>      struct address_space *mapping = page->mapping;
>      struct inode *inode = mapping->host;
>      struct ntfs_inode *ni = ntfs_i(inode);
> 
>      if (is_resident(ni)) {
>          ni_lock(ni);
>          err = attr_data_read_resident(ni, page);
>          ni_unlock(ni);
>          if (err != E_NTFS_NONRESIDENT) {
>              unlock_page(page);
>              return err;
>          }
>      }
> 
>      if (is_compressed(ni)) {
>          ni_lock(ni);
>          err = ni_readpage_cmpr(ni, page);
>          ni_unlock(ni);
>          return err;
>      }
> 
>      /* Normal + sparse files. */
>      return mpage_read_folio(folio, ntfs_get_block);
> }
> 
> [3]
> 
> 000000000001e934 <ntfs_read_folio>:
>     1e934:    d503233f     paciasp
>     1e938:    a9bc7bfd     stp    x29, x30, [sp, #-64]!
>     1e93c:    f9000bf7     str    x23, [sp, #16]
>     1e940:    a90257f6     stp    x22, x21, [sp, #32]
>     1e944:    a9034ff4     stp    x20, x19, [sp, #48]
>     1e948:    910003fd     mov    x29, sp
>     1e94c:    f9400c28     ldr    x8, [x1, #24]
>     1e950:    aa0103f3     mov    x19, x1
>     1e954:    f9400117     ldr    x23, [x8]
>     1e958:    385f82e8     ldurb    w8, [x23, #-8]
> 
> [4]
> 
> struct ntfs_inode {
>      ....
> 
>      size_t ni_flags; // NI_FLAG_XXX
> 
>      struct inode vfs_inode;
> };

I just learned that this is the same topic as the previously sent

https://lore.kernel.org/ntfs3/PAVPR10MB71635C5EF1E4FED2FF9C8107B60A2@PAVPR10MB7163.EURPRD10.PROD.OUTLOOK.COM/

Sorry for sending it twice!

An additional observation is

x8 : ffffff8d623ef4e7

being unaligned. Assuming that this is

struct address_space *mapping = page->mapping

we wonder if we are talking about a broken *mapping instead of a use 
after free we assumed initially above.

Sorry again for sending two times,

Dirk