From: liubaolin <liubaolin12138@163.com>
To: Xingyuan Mo <hdthky0@gmail.com>,
Namjae Jeon <linkinjeon@kernel.org>,
Hyunchul Lee <hyc.lee@gmail.com>
Cc: "open list:NTFS FILESYSTEM" <ntfs@lists.linux.dev>,
open list <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH] ntfs: fix out-of-bounds write race in NTFS_IOC_LIST_STREAMS
Date: Thu, 5 Nov 2026 17:26:48 +0800 [thread overview]
Message-ID: <470adc85-93ad-4e75-9e72-ab35362373bd@163.com> (raw)
In-Reply-To: <20261105073353.433854-1-hdthky0@gmail.com>
在 2026/11/5 15:34, Xingyuan Mo 写道:
> NTFS_IOC_LIST_STREAMS walks the base inode's MFT attributes twice:
> a first pass sizes the output buffer, a second pass fills it, both
> times filtering out streams that ntfs_stream_unlinked() reports as
> unlinked. That filter is the culprit: it treats a stream inode that
> is no longer in the inode hash as live, which is precisely the state
> a stream being deleted ends up in.
>
> When a base file is deleted while one of its named streams is still
> open, ntfs_cleanup_deleted_inode() sets NInoStreamUnlinked on the
> stream inode and removes it from the inode hash after dropping the
> MFT record lock. Nothing serializes this against the ioctl's two
> passes, so if the flag and the unhash land between them, the first
> pass excludes the stream while the second pass includes it, and the
> ioctl writes an entry the allocation never accounted for.
>
> The per-entry bounds check cannot catch this divergence, because
> "offset > required - entry_size" underflows when entry_size exceeds
> required. With a 1-character stream sizing the buffer at 48 bytes and
> a 250-character stream surfacing only in the second pass, a 544-byte
> entry is written into the 48-byte kmalloc-64 allocation.
>
> Close the hole in two steps:
>
> - Reject base inodes that are being deleted before walking their
> attributes, like the other named stream entry points in this file
> already do. This also stops the listing from reporting streams of a
> deleted base file as live, which is reproducible even without the
> race.
>
> - Make the bounds check underflow-safe so any remaining divergence
> between the passes can only produce -EOVERFLOW, never an
> out-of-bounds write.
>
> KASAN report from the reproducer (timestamps stripped):
>
> BUG: KASAN: slab-out-of-bounds in ntfs_ioctl_list_streams+0x7a9/0xa00
> Write of size 500 at addr ffff88800b1931a8 by task ntfs_ads_race/305
> CPU: 2 UID: 0 PID: 305 Comm: ntfs_ads_race
> Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> Call Trace:
> <TASK>
> dump_stack_lvl+0x73/0xb0
> print_report+0xd1/0x610
> kasan_report+0xff/0x130
> kasan_check_range+0x10c/0x1c0
> __asan_memcpy+0x3f/0x70
> ntfs_ioctl_list_streams+0x7a9/0xa00
> ntfs_ioctl+0x3b6/0x420
> __x64_sys_ioctl+0xd1/0x120
> x64_sys_call+0x2a5/0x2220
> do_syscall_64+0x109/0x5e0
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
> </TASK>
>
> Allocated by task 305:
> kasan_save_stack+0x3d/0x60
> kasan_save_track+0x18/0x40
> kasan_save_alloc_info+0x3b/0x50
> __kasan_kmalloc+0x9c/0xa0
> __kvmalloc_node_noprof+0x2dd/0x800
> ntfs_ioctl_list_streams+0x57f/0xa00
> ntfs_ioctl+0x3b6/0x420
> __x64_sys_ioctl+0xd1/0x120
> x64_sys_call+0x2a5/0x2220
> do_syscall_64+0x109/0x5e0
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
>
> The buggy address belongs to the object at ffff88800b193180
> which belongs to the cache kmalloc-64 of size 64
> The buggy address is located 40 bytes inside of
> allocated 48-byte region [ffff88800b193180, ffff88800b1931b0)
>
> Fixes: 48996054b706 ("ntfs: add named stream ioctls support")
> Signed-off-by: Xingyuan Mo <hdthky0@gmail.com>
> ---
> fs/ntfs/named_stream.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ntfs/named_stream.c b/fs/ntfs/named_stream.c
> index 95a3c05f9821..70c589207713 100644
> --- a/fs/ntfs/named_stream.c
> +++ b/fs/ntfs/named_stream.c
> @@ -1632,6 +1632,10 @@ int ntfs_ioctl_list_streams(struct file *filp, unsigned long arg)
> ubuf = (void __user *)arg + sizeof(hdr);
>
> mutex_lock(&ni->mrec_lock);
> + if (NInoBeingDeleted(ni) || !inode->i_nlink) {
> + mutex_unlock(&ni->mrec_lock);
> + return -ENOENT;
> + }
> actx = ntfs_attr_get_search_ctx(ni, NULL);
> if (!actx) {
> mutex_unlock(&ni->mrec_lock);
> @@ -1731,7 +1735,7 @@ int ntfs_ioctl_list_streams(struct file *filp, unsigned long arg)
> }
>
> entry_size = ALIGN(name_offset + name_len, 8);
> - if (offset > required - entry_size) {
> + if (offset > required || entry_size > required - offset) {
> ret = -EOVERFLOW;
> kfree(sn);
> sn = NULL;
Looks good to me.
Reviewed-by: Baolin Liu <liubaolin@kylinos.cn>
next prev parent reply other threads:[~2026-11-05 9:27 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 7:34 Xingyuan Mo
2026-11-05 9:26 ` liubaolin [this message]
2026-11-05 12:33 ` Namjae Jeon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=470adc85-93ad-4e75-9e72-ab35362373bd@163.com \
--to=liubaolin12138@163.com \
--cc=hdthky0@gmail.com \
--cc=hyc.lee@gmail.com \
--cc=linkinjeon@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ntfs@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®