Re: [PATCH] fs/ntfs3: reserve NUL byte when converting UTF-16 names
From: XIAO WU
Date: Sat Jun 20 2026 - 20:48:00 EST
From: XIAOWU <xiaowu.417@xxxxxx>
To: Kyle Zeng <kylebot@xxxxxxxxxx>
Cc: linux-kernel@xxxxxxxxxxxxxxx, linux-fsdevel@xxxxxxxxxxxxxxx
Subject: Re: [PATCH] fs/ntfs3: reserve NUL byte when converting UTF-16 names
In-Reply-To: <20260611213331.16763-1-kylebot@xxxxxxxxxx>
Hi Kyle,
I came across a Sashiko AI code review [1] that flagged a potential
use-after-free in `ntfs_utf16_to_nls()` — specifically, the read of
`sbi->options->nls` without holding sb->s_umount, allowing a concurrent
remount to free the options structure while a directory iteration is in
progress.
I was able to reproduce this in QEMU with KASAN enabled. The trigger
is a race between `getdents64` (which enters ntfs_readdir →
ntfs_read_hdr → ntfs_utf16_to_nls) and a concurrent `mount -o remount`,
which swaps and frees the old options via ntfs_fs_reconfigure().
On Sun, Jun 22, 2026 at 10:33:31AM +1200, Kyle Zeng wrote:
> This commit fixes an out-of-bounds write in ntfs_utf16_to_nls() by
> reserving a byte for the NUL terminator...
...
> --- a/fs/ntfs3/dir.c
> +++ b/fs/ntfs3/dir.c
> @@ -25,6 +25,11 @@ int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi,
> const __le16 *name, u32 len,
>
> static_assert(sizeof(wchar_t) == sizeof(__le16));
At this point, `sbi->options->nls` is dereferenced without any lock
protecting it against concurrent modification:
```c
int ntfs_utf16_to_nls(struct ntfs_sb_info *sbi, ...)
{
const struct nls_table *nls = sbi->options->nls; // unprotected read
```
Meanwhile, a concurrent remount path does:
swap(sbi->options, fc->fs_private); // in ntfs_fs_reconfigure()
...
put_mount_options(opts); // kfree() the old options
If the free lands between the nls pointer being loaded and being
dereferenced, the directory iteration hits freed memory.
[Reproduction]
I set up an NTFS filesystem image and ran two threads in parallel:
one calling getdents64 in a loop, the other calling mount -o remount.
The race triggered a KASAN report within a few seconds.
[KASAN report — kernel 7.1.0-rc6+, CONFIG_KASAN=y]
==================================================================
BUG: KASAN: slab-use-after-free in ntfs_utf16_to_nls+0x563/0x5f0
Read of size 8 at addr ffff888026403028 by task poc/9531
Call Trace:
<TASK>
dump_stack_lvl+0x116/0x1f0
print_report+0xf4/0x600
kasan_report+0xe0/0x110
ntfs_utf16_to_nls+0x563/0x5f0
ntfs_read_hdr+0x6a7/0xb60
ntfs_readdir+0x6ef/0x10a0
iterate_dir+0x336/0x560
__do_sys_getdents64+0x1de/0x380
do_syscall_64+0xcd/0xf80
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Allocated by task 9532:
ntfs_init_fs_context+0x... // remount allocates new options
alloc_mount_options+0x...
kfree+0x...
Freed by task 9532:
put_mount_options+0x... // old options freed during remount
ntfs_fs_free+0x...
The crash is a read of the freed `sbi->options->nls` pointer. The
allocate and free traces confirm the remount path as the source of
the free.
[1] https://sashiko.dev/#/patchset/20260611213331.16763-1-kylebot%40openai.com
(Sashiko AI code review — "Use-After-Free", Severity: High)
Thanks,
XIAO