* [PATCH] ntfs: validate $FILE_NAME length before converting the name
@ 2026-06-04 5:39 Bryam Vargas
2026-06-04 9:01 ` Hyunchul Lee
0 siblings, 1 reply; 3+ messages in thread
From: Bryam Vargas @ 2026-06-04 5:39 UTC (permalink / raw)
To: Namjae Jeon, Hyunchul Lee; +Cc: linux-fsdevel, linux-kernel, Bryam Vargas
ntfs_is_extended_system_file() walks a base inode's $FILE_NAME attributes
and, for a name whose parent is $Extend, converts the on-disk name to the
local charset via ntfs_attr_name_get() -> ntfs_ucstonls(). It checks that
the resident value [file_name_attr, +value_length) lies within the
attribute, but never checks that file_name_length (an on-disk u8) fits
inside that value.
ntfs_ucstonls() then reads file_name_length __le16 characters starting at
file_name_attr->file_name (offset 66 in the __packed struct), so a crafted
$FILE_NAME whose value_length covers just the fixed header but whose
file_name_length is 255 drives an out-of-bounds read of up to ~508 bytes
past the resident value -- and, when the MFT record buffer is a tight
kmalloc (mft_record_size equal to a power-of-two slab bucket), past the
allocation itself.
Reject the attribute when file_name_length does not fit within
value_length before the name is read.
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
Reproducer omitted on the public list -- available to the maintainers on
request. This is a crafted-image filesystem bug; posting publicly per the
security@kernel.org guidance on the sibling fs/ntfs attribute-list fixes that
such bugs fall outside the kernel security process's threat model.
The over-read was observed under KASAN by mounting a crafted NTFS image
(fs/ntfs built as a module against v7.1-rc5): a $FILE_NAME on a system file in
$Extend with value_length 68 and file_name_length 255 trips, during inode
read, a slab-out-of-bounds read of size 2 in utf16s_to_utf8s() along the chain
ntfs_ucstonls <- ntfs_attr_name_get <- ntfs_is_extended_system_file <-
ntfs_read_locked_inode <- ntfs_iget <- ntfs_fill_super
off the kmalloc-1k MFT record buffer (mft_record_size 1024, allocated by
map_mft_record_folio). A benign mkntfs image is clean on the same kernel
(control). With this bound in place the crafted $FILE_NAME is rejected with
-EIO ("Corrupt file name attribute") before the conversion, while a benign
name (value_length == 66 + file_name_length * 2) still passes. The bound is arch-independent: struct
file_name_attr is __packed, so offsetof(.., file_name) == 66 and the check
decides every case identically built -m32/-m64.
The only other reader of file_name_length for name conversion is under
#ifdef DEBUG in fs/ntfs/namei.c (not built in production); it can be guarded
the same way if desired.
The flaw is original to the fs/ntfs driver: ntfs_is_extended_system_file()
landed with the driver itself (linkinjeon/ntfs, v7.1 merge window). A Fixes:
tag against that introducing commit is appropriate -- I'd appreciate you
pinning the exact SHA from your tree, as it is not unambiguously citable from
a mainline clone. checkpatch clean.
fs/ntfs/inode.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 360bebd1ee3f..d1a79c276502 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -581,6 +581,17 @@ static int ntfs_is_extended_system_file(struct ntfs_attr_search_ctx *ctx)
p2 = (u8 *)file_name_attr + le32_to_cpu(attr->data.resident.value_length);
if (p2 < (u8 *)attr || p2 > p)
goto err_corrupt_attr;
+ /*
+ * The name is converted below; file_name_length is an on-disk
+ * u8 that is not otherwise cross-checked, so make sure the name
+ * fits within the resident value before it is read.
+ */
+ if (le32_to_cpu(attr->data.resident.value_length) <
+ offsetof(struct file_name_attr, file_name) ||
+ offsetof(struct file_name_attr, file_name) +
+ file_name_attr->file_name_length * sizeof(__le16) >
+ le32_to_cpu(attr->data.resident.value_length))
+ goto err_corrupt_attr;
/* This attribute is ok, but is it in the $Extend directory? */
if (MREF_LE(file_name_attr->parent_directory) == FILE_Extend) {
unsigned char *s;
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] ntfs: validate $FILE_NAME length before converting the name
2026-06-04 5:39 [PATCH] ntfs: validate $FILE_NAME length before converting the name Bryam Vargas
@ 2026-06-04 9:01 ` Hyunchul Lee
2026-06-04 10:17 ` Bryam Vargas
0 siblings, 1 reply; 3+ messages in thread
From: Hyunchul Lee @ 2026-06-04 9:01 UTC (permalink / raw)
To: Bryam Vargas; +Cc: Namjae Jeon, linux-fsdevel, linux-kernel
Hi Bryam,
2026년 6월 4일 (목) 오후 2:39, Bryam Vargas <hexlabsecurity@proton.me>님이 작성:
>
> ntfs_is_extended_system_file() walks a base inode's $FILE_NAME attributes
> and, for a name whose parent is $Extend, converts the on-disk name to the
> local charset via ntfs_attr_name_get() -> ntfs_ucstonls(). It checks that
> the resident value [file_name_attr, +value_length) lies within the
> attribute, but never checks that file_name_length (an on-disk u8) fits
> inside that value.
>
> ntfs_ucstonls() then reads file_name_length __le16 characters starting at
> file_name_attr->file_name (offset 66 in the __packed struct), so a crafted
> $FILE_NAME whose value_length covers just the fixed header but whose
> file_name_length is 255 drives an out-of-bounds read of up to ~508 bytes
> past the resident value -- and, when the MFT record buffer is a tight
> kmalloc (mft_record_size equal to a power-of-two slab bucket), past the
> allocation itself.
>
> Reject the attribute when file_name_length does not fit within
> value_length before the name is read.
>
> Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
> ---
> Reproducer omitted on the public list -- available to the maintainers on
> request. This is a crafted-image filesystem bug; posting publicly per the
> security@kernel.org guidance on the sibling fs/ntfs attribute-list fixes that
> such bugs fall outside the kernel security process's threat model.
>
> The over-read was observed under KASAN by mounting a crafted NTFS image
> (fs/ntfs built as a module against v7.1-rc5): a $FILE_NAME on a system file in
> $Extend with value_length 68 and file_name_length 255 trips, during inode
> read, a slab-out-of-bounds read of size 2 in utf16s_to_utf8s() along the chain
>
> ntfs_ucstonls <- ntfs_attr_name_get <- ntfs_is_extended_system_file <-
> ntfs_read_locked_inode <- ntfs_iget <- ntfs_fill_super
>
> off the kmalloc-1k MFT record buffer (mft_record_size 1024, allocated by
> map_mft_record_folio). A benign mkntfs image is clean on the same kernel
> (control). With this bound in place the crafted $FILE_NAME is rejected with
> -EIO ("Corrupt file name attribute") before the conversion, while a benign
> name (value_length == 66 + file_name_length * 2) still passes. The bound is arch-independent: struct
> file_name_attr is __packed, so offsetof(.., file_name) == 66 and the check
> decides every case identically built -m32/-m64.
>
> The only other reader of file_name_length for name conversion is under
> #ifdef DEBUG in fs/ntfs/namei.c (not built in production); it can be guarded
> the same way if desired.
>
> The flaw is original to the fs/ntfs driver: ntfs_is_extended_system_file()
> landed with the driver itself (linkinjeon/ntfs, v7.1 merge window). A Fixes:
> tag against that introducing commit is appropriate -- I'd appreciate you
> pinning the exact SHA from your tree, as it is not unambiguously citable from
> a mainline clone. checkpatch clean.
>
> fs/ntfs/inode.c | 11 +++++++++++
> 1 file changed, 11 insertions(+)
>
> diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
> index 360bebd1ee3f..d1a79c276502 100644
> --- a/fs/ntfs/inode.c
> +++ b/fs/ntfs/inode.c
> @@ -581,6 +581,17 @@ static int ntfs_is_extended_system_file(struct ntfs_attr_search_ctx *ctx)
> p2 = (u8 *)file_name_attr + le32_to_cpu(attr->data.resident.value_length);
> if (p2 < (u8 *)attr || p2 > p)
> goto err_corrupt_attr;
> + /*
> + * The name is converted below; file_name_length is an on-disk
> + * u8 that is not otherwise cross-checked, so make sure the name
> + * fits within the resident value before it is read.
> + */
> + if (le32_to_cpu(attr->data.resident.value_length) <
> + offsetof(struct file_name_attr, file_name) ||
> + offsetof(struct file_name_attr, file_name) +
> + file_name_attr->file_name_length * sizeof(__le16) >
> + le32_to_cpu(attr->data.resident.value_length))
> + goto err_corrupt_attr;
There is already a patch that validates this in ntfs_attr_lookup().
Please refer to the link below:
https://lore.kernel.org/all/20260530143514.3083601-2-charsyam@gmail.com
> /* This attribute is ok, but is it in the $Extend directory? */
> if (MREF_LE(file_name_attr->parent_directory) == FILE_Extend) {
> unsigned char *s;
> --
> 2.43.0
>
--
Thanks,
Hyunchul
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] ntfs: validate $FILE_NAME length before converting the name
2026-06-04 9:01 ` Hyunchul Lee
@ 2026-06-04 10:17 ` Bryam Vargas
0 siblings, 0 replies; 3+ messages in thread
From: Bryam Vargas @ 2026-06-04 10:17 UTC (permalink / raw)
To: Hyunchul Lee; +Cc: Namjae Jeon, DaeMyung Kang, linux-fsdevel, linux-kernel
Hi Hyunchul,
> There is already a patch that validates this in ntfs_attr_lookup().
> https://lore.kernel.org/all/20260530143514.3083601-2-charsyam@gmail.com
Right -- DaeMyung Kang's "[PATCH v4 1/6] ntfs: validate attribute values on
lookup" already rejects a $FILE_NAME whose file_name_length overruns the
resident value_length (ntfs_file_name_attr_value_is_valid()), and does it in
ntfs_attr_lookup() so every caller is covered, not just
ntfs_is_extended_system_file(). That subsumes this patch -- please drop it.
I'll leave the fs/ntfs attribute-value hardening to that series.
Bryam
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-06-04 10:17 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-04 5:39 [PATCH] ntfs: validate $FILE_NAME length before converting the name Bryam Vargas
2026-06-04 9:01 ` Hyunchul Lee
2026-06-04 10:17 ` Bryam Vargas
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox