* [PATCH v2 1/3] ntfs: validate resident attribute lists and harden the validator
@ 2026-06-03 17:59 Bryam Vargas
2026-06-03 18:00 ` [PATCH v2 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas
` (2 more replies)
0 siblings, 3 replies; 10+ messages in thread
From: Bryam Vargas @ 2026-06-03 17:59 UTC (permalink / raw)
To: Namjae Jeon, Hyunchul Lee; +Cc: linux-fsdevel, linux-kernel
A base inode's $ATTRIBUTE_LIST is sanity-checked by load_attribute_list()
only on the non-resident path; ntfs_read_locked_inode() copies a *resident*
attribute list into ni->attr_list with a plain memcpy() and no validation
at all. Every subsequent walk of ni->attr_list -- ntfs_external_attr_find(),
ntfs_inode_attach_all_extents(), ntfs_attrlist_need() -- then trusts the
entries are well-formed and reads attr_list_entry fixed-header fields
(lowest_vcn at offset 8, mft_reference at offset 16, and the name) with
bounds that assume validation already happened. A crafted resident
attribute list therefore reaches those walks unvalidated and can drive
out-of-bounds reads of the attribute-list buffer.
load_attribute_list() itself reads ale->name_offset (offset 7),
ale->mft_reference (offset 16) and the name length under only an
"al < al_start + size" bound, so its own validation loop can over-read the
fixed header of a truncated trailing entry by a few bytes.
Factor the per-entry validation into ntfs_attr_list_is_valid(), which
requires each entry's fixed header (offsetof(struct attr_list_entry, name))
to be in range before any field is dereferenced and that the entries tile
the buffer exactly. Use it both in load_attribute_list() (replacing the
open-coded loop, closing its own over-read) and on the resident path in
ntfs_read_locked_inode() (which previously skipped validation entirely).
Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me>
---
v2: dropped the redundant Reported-by (it duplicated Signed-off-by); reproducer
omitted on the public list -- available to the maintainers on request.
Posting publicly per security@kernel.org guidance that crafted-image
filesystem bugs fall outside the kernel security process's threat model.
Root cause behind the two out-of-bounds reads in patches 2/3 and 3/3: those
harden the individual read sites; this validates the list centrally so the
other walks (ntfs_inode_attach_all_extents, ntfs_attrlist_need) are covered
too. Verified under KASAN: the crafted attribute list that previously oopsed
ntfs_external_attr_find() is now rejected at load with
"ntfs_read_locked_inode(): Corrupt attribute list." and no out-of-bounds
access; a benign mkntfs image still mounts. checkpatch clean.
fs/ntfs/attrib.c | 56 ++++++++++++++++++++++++++++++++++++++++--------------
fs/ntfs/attrib.h | 2 ++
fs/ntfs/inode.c | 6 ++++++
3 files changed, 49 insertions(+), 15 deletions(-)
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 421c6cdcbb53..36c1df25a622 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -843,11 +843,49 @@ char *ntfs_attr_name_get(const struct ntfs_volume *vol, const __le16 *uname,
return NULL;
}
+/*
+ * ntfs_attr_list_is_valid - sanity check an in-memory $ATTRIBUTE_LIST
+ * @al_start: start of the attribute list buffer
+ * @size: length of the attribute list in bytes
+ *
+ * Verify that [@al_start, @al_start + @size) is a well-formed sequence of
+ * attr_list_entry records. Each record's fixed header must lie within the
+ * buffer before any of its fields are dereferenced, its length must cover
+ * the fixed header plus the name, and the records must tile the buffer
+ * exactly. Return true if valid, false otherwise.
+ */
+bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size)
+{
+ const u8 *al = al_start;
+ const u8 *al_end = al_start + size;
+
+ while (al < al_end) {
+ const struct attr_list_entry *ale =
+ (const struct attr_list_entry *)al;
+ u16 ale_len;
+
+ /* The fixed header must be in bounds before it is parsed. */
+ if (al + offsetof(struct attr_list_entry, name) > al_end)
+ return false;
+ ale_len = le16_to_cpu(ale->length);
+ if (ale->name_offset != sizeof(struct attr_list_entry))
+ return false;
+ if ((u32)ale->name_offset +
+ (u32)ale->name_length * sizeof(__le16) > ale_len ||
+ al + ale_len > al_end)
+ return false;
+ if (ale->type == AT_UNUSED)
+ return false;
+ if (MSEQNO_LE(ale->mft_reference) == 0)
+ return false;
+ al += ale_len;
+ }
+ return al == al_end;
+}
+
int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size)
{
struct inode *attr_vi = NULL;
- u8 *al;
- struct attr_list_entry *ale;
if (!al_start || size <= 0)
return -EINVAL;
@@ -869,19 +907,7 @@ int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size
}
iput(attr_vi);
- for (al = al_start; al < al_start + size; al += le16_to_cpu(ale->length)) {
- ale = (struct attr_list_entry *)al;
- if (ale->name_offset != sizeof(struct attr_list_entry))
- break;
- if (le16_to_cpu(ale->length) <= ale->name_offset + ale->name_length ||
- al + le16_to_cpu(ale->length) > al_start + size)
- break;
- if (ale->type == AT_UNUSED)
- break;
- if (MSEQNO_LE(ale->mft_reference) == 0)
- break;
- }
- if (al != al_start + size) {
+ if (!ntfs_attr_list_is_valid(al_start, size)) {
ntfs_error(base_ni->vol->sb, "Corrupt attribute list, mft = %llu",
base_ni->mft_no);
return -EIO;
diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h
index f7acc7986b09..4b11d4b0be14 100644
--- a/fs/ntfs/attrib.h
+++ b/fs/ntfs/attrib.h
@@ -71,6 +71,8 @@ int ntfs_attr_lookup(const __le32 type, const __le16 *name,
const u32 name_len, const u32 ic,
const s64 lowest_vcn, const u8 *val, const u32 val_len,
struct ntfs_attr_search_ctx *ctx);
+bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size);
+
int load_attribute_list(struct ntfs_inode *base_ni,
u8 *al_start, const s64 size);
diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c
index 360bebd1ee3f..a5f7400fd19d 100644
--- a/fs/ntfs/inode.c
+++ b/fs/ntfs/inode.c
@@ -848,6 +848,12 @@ static int ntfs_read_locked_inode(struct inode *vi)
a->data.resident.value_offset),
le32_to_cpu(
a->data.resident.value_length));
+ /* A resident list is not validated on load; check it now. */
+ if (!ntfs_attr_list_is_valid(ni->attr_list,
+ ni->attr_list_size)) {
+ ntfs_error(vi->i_sb, "Corrupt attribute list.");
+ goto unm_err_out;
+ }
}
}
skip_attr_list_load:
--
2.43.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v2 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() 2026-06-03 17:59 [PATCH v2 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas @ 2026-06-03 18:00 ` Bryam Vargas 2026-06-03 18:00 ` [PATCH v2 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() Bryam Vargas 2026-06-04 4:29 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas 2 siblings, 0 replies; 10+ messages in thread From: Bryam Vargas @ 2026-06-03 18:00 UTC (permalink / raw) To: Namjae Jeon, Hyunchul Lee; +Cc: linux-fsdevel, linux-kernel When resolving an attribute lookup with a non-zero @lowest_vcn, ntfs_external_attr_find() peeks at the next $ATTRIBUTE_LIST entry to decide whether to keep searching, but bounds that not-yet-validated entry only with "(u8 *)next_al_entry + 6 < al_end" (which proves just bytes 0..6 are in range) and "(u8 *)next_al_entry + length <= al_end" with an attacker-controlled, non-8-aligned length. It then reads next_al_entry->lowest_vcn (an __le64 at offset 8) and the name at next_al_entry->name_offset, both of which can lie past al_end -- the exact end of the kvmalloc'd attribute-list buffer (allocated at the on-disk attr_list_size, no rounding). A crafted on-disk $ATTRIBUTE_LIST whose last entry sits a few bytes before al_end therefore yields a slab out-of-bounds read when the inode is read. Apply to the look-ahead entry the same bounds the main loop already applies to the current entry: require the fixed header up to offsetof(struct attr_list_entry, name) to be in range, and the name to lie within the buffer, before dereferencing lowest_vcn and the name. Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- v2: dropped the redundant Reported-by; reproducer and KASAN splat omitted on the public list -- available to the maintainers on request. Posting per security@kernel.org guidance that crafted-image filesystem bugs are out of the kernel security process's threat model. Verified by mounting a crafted NTFS image under KASAN (fs/ntfs built as a module against v7.1-rc5): a slab out-of-bounds read of next_al_entry->lowest_vcn during inode read; a benign mkntfs image is KASAN-clean on the same kernel (control). Arch-independent: the on-disk struct is __packed (lowest_vcn at offset 8, fixed header up to offsetof(struct attr_list_entry, name) == 26) and the over-read is an le64; identical geometry built -m32/-m64, so 32- and 64-bit kernels are both affected. Fixes note: this is in the fs/ntfs driver added during the v7.1 merge window. The look-ahead expression is carried verbatim from the legacy fs/ntfs driver (removed in v6.9; present unchanged since v2.6.12), where __ntfs_malloc() rounded the list allocation up to a full kmalloc(PAGE_SIZE) and masked it; the new driver's exact-size kvmalloc(attr_list_size) exposes it. Please add the appropriate Fixes: tag for the commit that introduced ntfs_external_attr_find() in the new driver. fs/ntfs/attrib.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 421c6cdcbb53..aed68b8e69ea 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -1137,9 +1137,14 @@ static int ntfs_external_attr_find(const __le32 type, * we have reached the right one or the search has failed. */ if (lowest_vcn && (u8 *)next_al_entry >= al_start && - (u8 *)next_al_entry + 6 < al_end && + (u8 *)next_al_entry + + offsetof(struct attr_list_entry, name) <= al_end && (u8 *)next_al_entry + le16_to_cpu( next_al_entry->length) <= al_end && + (!next_al_entry->name_length || + (u8 *)next_al_entry + next_al_entry->name_offset + + next_al_entry->name_length * sizeof(__le16) <= + al_end) && le64_to_cpu(next_al_entry->lowest_vcn) <= lowest_vcn && next_al_entry->type == al_entry->type && -- 2.43.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() 2026-06-03 17:59 [PATCH v2 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas 2026-06-03 18:00 ` [PATCH v2 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas @ 2026-06-03 18:00 ` Bryam Vargas 2026-06-04 4:29 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas 2 siblings, 0 replies; 10+ messages in thread From: Bryam Vargas @ 2026-06-03 18:00 UTC (permalink / raw) To: Namjae Jeon, Hyunchul Lee; +Cc: linux-fsdevel, linux-kernel The $MFT attribute-list walk in ntfs_read_inode_mount() validates each entry only with "(u8 *)al_entry + 6 > al_end" and "(u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end", but then reads al_entry->lowest_vcn (an __le64 at offset 8) and al_entry->mft_reference (offset 16) -- fields beyond the 6 bytes proven in range. al_entry->length is attacker-controlled and only required non-zero, so a short entry (e.g. length 8) placed at the tail passes both checks while the lowest_vcn / mft_reference reads fall past al_end. al_end is ni->attr_list + attr_list_size (the on-disk size); the buffer is kvzalloc(round_up(attr_list_size, SECTOR_SIZE)), so the sector rounding usually absorbs the over-read -- but when attr_list_size is a multiple of SECTOR_SIZE there is no slack and a crafted $MFT attribute list produces an out-of-bounds read at mount time. Require the fixed attribute-list-entry header to be in range before dereferencing it, matching the bound ntfs_external_attr_find() already applies to the entries in its own attribute-list walk. Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- v2: dropped the redundant Reported-by; reproducer omitted on the public list (available to the maintainers on request). Posting per security@kernel.org guidance that crafted-image filesystem bugs are out of the security threat model. Sibling of the ntfs_external_attr_find() look-ahead OOB (patch 2/3): the same struct attr_list_entry fixed fields (lowest_vcn at 8, mft_reference at 16) are read here with a weaker bound. The expression is carried from the legacy fs/ntfs driver (removed v6.9; present since v2.6.12), where ntfs_malloc_nofs() rounded every allocation up to kmalloc(PAGE_SIZE) and masked it. Please add the appropriate Fixes: tag for the commit that introduced ntfs_read_inode_mount() in the new driver. fs/ntfs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 360bebd1ee3f..70fb216a1bd0 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -1996,7 +1996,8 @@ int ntfs_read_inode_mount(struct inode *vi) goto em_put_err_out; if (!al_entry->length) goto em_put_err_out; - if ((u8 *)al_entry + 6 > al_end || + if ((u8 *)al_entry + offsetof(struct attr_list_entry, name) > + al_end || (u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end) goto em_put_err_out; next_al_entry = (struct attr_list_entry *)((u8 *)al_entry + -- 2.43.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator 2026-06-03 17:59 [PATCH v2 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas 2026-06-03 18:00 ` [PATCH v2 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas 2026-06-03 18:00 ` [PATCH v2 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() Bryam Vargas @ 2026-06-04 4:29 ` Bryam Vargas 2026-06-04 4:29 ` [PATCH v3 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas ` (2 more replies) 2 siblings, 3 replies; 10+ messages in thread From: Bryam Vargas @ 2026-06-04 4:29 UTC (permalink / raw) To: Namjae Jeon, Hyunchul Lee; +Cc: linux-fsdevel, linux-kernel, Bryam Vargas A base inode's $ATTRIBUTE_LIST is sanity-checked by load_attribute_list() only on the non-resident path; ntfs_read_locked_inode() copies a *resident* attribute list into ni->attr_list with a plain memcpy() and no validation at all. Every subsequent walk of ni->attr_list -- ntfs_external_attr_find(), ntfs_inode_attach_all_extents() and ntfs_attrlist_need() -- then trusts the entries are well-formed and reads attr_list_entry fixed-header fields (lowest_vcn at offset 8, mft_reference at offset 16, and the name) with bounds that assume validation already happened. A crafted resident attribute list therefore reaches those walks unvalidated and can drive out-of-bounds reads of the attribute-list buffer. load_attribute_list() itself reads ale->name_offset (offset 7), ale->mft_reference (offset 16) and the name length under only an "al < al_start + size" bound, so its own validation loop can over-read the fixed header of a truncated trailing entry by a few bytes. Factor the per-entry validation into ntfs_attr_list_entry_is_valid(), which requires each entry's fixed header (offsetof(struct attr_list_entry, name)) to be in range before any field is dereferenced, that ale->length is a multiple of 8 covering the fixed header plus the name, and that the entry is in use and carries a live MFT reference. ntfs_attr_list_is_valid() walks the buffer with it and checks the entries tile it exactly. Use the list validator in load_attribute_list() (replacing the open-coded loop, closing its own over-read) and on the resident path in ntfs_read_locked_inode() (which previously skipped validation entirely); patches 2/3 reuse the per-entry helper at the other two attribute-list walks. Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- v3 (Hyunchul Lee review): - Split the per-entry check into ntfs_attr_list_entry_is_valid() and call it from the whole series (this patch's list walk, the look-ahead in 2/3 and the $MFT walk in 3/3). - Require ale->length to be a multiple of 8 (on-disk attribute-list entries are 8-byte aligned). v2: dropped the redundant Reported-by; reproducer omitted on the public list (available to the maintainers on request). The out-of-bounds reads were confirmed under KASAN on the earlier revision (crafted NTFS image, fs/ntfs built as a module): the crafted attribute list that oopsed ntfs_external_attr_find() is now rejected at load with "ntfs_read_locked_inode(): Corrupt attribute list." and a benign mkntfs image still mounts. The validator is arch-independent -- struct attr_list_entry is __packed, so sizeof() == offsetof(.., name) == 26 and every field offset (length 4, lowest_vcn 8, mft_reference 16) is identical built -m32 and -m64; a clean-room model of this v3 validator decides every benign/crafted vector the same way on both ABIs, including the new 8-byte-alignment rejection. checkpatch clean. fs/ntfs/attrib.c | 78 ++++++++++++++++++++++++++++++++++++++---------- fs/ntfs/attrib.h | 4 +++ fs/ntfs/inode.c | 6 ++++ 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 421c6cdcbb53..abc0add6f0c4 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -843,11 +843,71 @@ char *ntfs_attr_name_get(const struct ntfs_volume *vol, const __le16 *uname, return NULL; } +/* + * ntfs_attr_list_entry_is_valid - sanity check one $ATTRIBUTE_LIST entry + * @ale: the attribute-list entry to check + * @al_end: end of the attribute-list buffer @ale lives in + * + * Verify that @ale is a well-formed attr_list_entry wholly contained in + * [.., @al_end): its fixed header must lie in range before any field is + * dereferenced, its length must be a multiple of 8 that covers the fixed + * header plus the name, the name must lie within the buffer, the entry must + * be in use and carry a live MFT reference. Return true if valid. + */ +bool ntfs_attr_list_entry_is_valid(const struct attr_list_entry *ale, + const u8 *al_end) +{ + const u8 *al = (const u8 *)ale; + u16 ale_len; + + /* The fixed header must be in bounds before it is parsed. */ + if (al + offsetof(struct attr_list_entry, name) > al_end) + return false; + ale_len = le16_to_cpu(ale->length); + /* On-disk entries are 8-byte aligned (see struct attr_list_entry). */ + if (ale_len & 7) + return false; + if (ale->name_offset != sizeof(struct attr_list_entry)) + return false; + if ((u32)ale->name_offset + + (u32)ale->name_length * sizeof(__le16) > ale_len || + al + ale_len > al_end) + return false; + if (ale->type == AT_UNUSED) + return false; + if (MSEQNO_LE(ale->mft_reference) == 0) + return false; + return true; +} + +/* + * ntfs_attr_list_is_valid - sanity check an in-memory $ATTRIBUTE_LIST + * @al_start: start of the attribute list buffer + * @size: length of the attribute list in bytes + * + * Verify that [@al_start, @al_start + @size) is a sequence of valid + * attr_list_entry records (see ntfs_attr_list_entry_is_valid()) that tile the + * buffer exactly. Return true if valid, false otherwise. + */ +bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size) +{ + const u8 *al = al_start; + const u8 *al_end = al_start + size; + + while (al < al_end) { + const struct attr_list_entry *ale = + (const struct attr_list_entry *)al; + + if (!ntfs_attr_list_entry_is_valid(ale, al_end)) + return false; + al += le16_to_cpu(ale->length); + } + return al == al_end; +} + int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size) { struct inode *attr_vi = NULL; - u8 *al; - struct attr_list_entry *ale; if (!al_start || size <= 0) return -EINVAL; @@ -869,19 +929,7 @@ int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size } iput(attr_vi); - for (al = al_start; al < al_start + size; al += le16_to_cpu(ale->length)) { - ale = (struct attr_list_entry *)al; - if (ale->name_offset != sizeof(struct attr_list_entry)) - break; - if (le16_to_cpu(ale->length) <= ale->name_offset + ale->name_length || - al + le16_to_cpu(ale->length) > al_start + size) - break; - if (ale->type == AT_UNUSED) - break; - if (MSEQNO_LE(ale->mft_reference) == 0) - break; - } - if (al != al_start + size) { + if (!ntfs_attr_list_is_valid(al_start, size)) { ntfs_error(base_ni->vol->sb, "Corrupt attribute list, mft = %llu", base_ni->mft_no); return -EIO; diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h index f7acc7986b09..e2224fbfaabe 100644 --- a/fs/ntfs/attrib.h +++ b/fs/ntfs/attrib.h @@ -71,6 +71,10 @@ int ntfs_attr_lookup(const __le32 type, const __le16 *name, const u32 name_len, const u32 ic, const s64 lowest_vcn, const u8 *val, const u32 val_len, struct ntfs_attr_search_ctx *ctx); +bool ntfs_attr_list_entry_is_valid(const struct attr_list_entry *ale, + const u8 *al_end); +bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size); + int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index 360bebd1ee3f..a5f7400fd19d 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -848,6 +848,12 @@ static int ntfs_read_locked_inode(struct inode *vi) a->data.resident.value_offset), le32_to_cpu( a->data.resident.value_length)); + /* A resident list is not validated on load; check it now. */ + if (!ntfs_attr_list_is_valid(ni->attr_list, + ni->attr_list_size)) { + ntfs_error(vi->i_sb, "Corrupt attribute list."); + goto unm_err_out; + } } } skip_attr_list_load: -- 2.43.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() 2026-06-04 4:29 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas @ 2026-06-04 4:29 ` Bryam Vargas 2026-06-04 8:41 ` Hyunchul Lee 2026-06-04 4:29 ` [PATCH v3 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() Bryam Vargas 2026-06-04 8:40 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Hyunchul Lee 2 siblings, 1 reply; 10+ messages in thread From: Bryam Vargas @ 2026-06-04 4:29 UTC (permalink / raw) To: Namjae Jeon, Hyunchul Lee; +Cc: linux-fsdevel, linux-kernel, Bryam Vargas When resolving an attribute lookup with a non-zero @lowest_vcn, ntfs_external_attr_find() peeks at the next $ATTRIBUTE_LIST entry to decide whether to keep searching, but bounds that not-yet-validated entry only with "(u8 *)next_al_entry + 6 < al_end" (which proves just bytes 0..6 are in range) and "(u8 *)next_al_entry + length <= al_end" with an attacker-controlled, non-8-aligned length. It then reads next_al_entry->lowest_vcn (an __le64 at offset 8) and the name at next_al_entry->name_offset, both of which can lie past al_end -- the exact end of the kvmalloc'd attribute-list buffer (allocated at the on-disk attr_list_size, no rounding). A crafted on-disk $ATTRIBUTE_LIST whose last entry sits a few bytes before al_end therefore yields a slab out-of-bounds read when the inode is read. Validate the look-ahead entry with ntfs_attr_list_entry_is_valid() (added in patch 1/3) before dereferencing lowest_vcn and the name, so the same fixed-header, length and name bounds the main attribute-list walk uses now guard this read too. Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- v3 (Hyunchul Lee review): use the extracted ntfs_attr_list_entry_is_valid() helper (added in 1/3) instead of an open-coded bound, so the look-ahead gets the same fixed-header, 8-byte-aligned-length and name checks as the main walk. v2: dropped the redundant Reported-by; reproducer/KASAN splat omitted on the public list (available to the maintainers on request). Confirmed under KASAN on the earlier revision: a slab out-of-bounds read of next_al_entry->lowest_vcn during inode read of a crafted image; a benign mkntfs image is KASAN-clean (control). Arch-independent (lowest_vcn at offset 8, fixed header offsetof(.., name) == 26; identical geometry built -m32/-m64). Please add the appropriate Fixes: tag for the commit that introduced ntfs_external_attr_find() in the new fs/ntfs driver (v7.1 merge window). fs/ntfs/attrib.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index abc0add6f0c4..e425c8d074c5 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -1185,9 +1185,8 @@ static int ntfs_external_attr_find(const __le32 type, * we have reached the right one or the search has failed. */ if (lowest_vcn && (u8 *)next_al_entry >= al_start && - (u8 *)next_al_entry + 6 < al_end && - (u8 *)next_al_entry + le16_to_cpu( - next_al_entry->length) <= al_end && + ntfs_attr_list_entry_is_valid(next_al_entry, + al_end) && le64_to_cpu(next_al_entry->lowest_vcn) <= lowest_vcn && next_al_entry->type == al_entry->type && -- 2.43.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() 2026-06-04 4:29 ` [PATCH v3 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas @ 2026-06-04 8:41 ` Hyunchul Lee 0 siblings, 0 replies; 10+ messages in thread From: Hyunchul Lee @ 2026-06-04 8:41 UTC (permalink / raw) To: Bryam Vargas; +Cc: Namjae Jeon, linux-fsdevel, linux-kernel 2026년 6월 4일 (목) 오후 1:29, Bryam Vargas <hexlabsecurity@proton.me>님이 작성: > > When resolving an attribute lookup with a non-zero @lowest_vcn, > ntfs_external_attr_find() peeks at the next $ATTRIBUTE_LIST entry to > decide whether to keep searching, but bounds that not-yet-validated > entry only with "(u8 *)next_al_entry + 6 < al_end" (which proves just > bytes 0..6 are in range) and "(u8 *)next_al_entry + length <= al_end" > with an attacker-controlled, non-8-aligned length. It then reads > next_al_entry->lowest_vcn (an __le64 at offset 8) and the name at > next_al_entry->name_offset, both of which can lie past al_end -- the > exact end of the kvmalloc'd attribute-list buffer (allocated at the > on-disk attr_list_size, no rounding). A crafted on-disk $ATTRIBUTE_LIST > whose last entry sits a few bytes before al_end therefore yields a slab > out-of-bounds read when the inode is read. > > Validate the look-ahead entry with ntfs_attr_list_entry_is_valid() (added > in patch 1/3) before dereferencing lowest_vcn and the name, so the same > fixed-header, length and name bounds the main attribute-list walk uses now > guard this read too. > > Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Looks good to me. Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> > --- > v3 (Hyunchul Lee review): use the extracted ntfs_attr_list_entry_is_valid() > helper (added in 1/3) instead of an open-coded bound, so the look-ahead > gets the same fixed-header, 8-byte-aligned-length and name checks as the > main walk. > v2: dropped the redundant Reported-by; reproducer/KASAN splat omitted on the > public list (available to the maintainers on request). > > Confirmed under KASAN on the earlier revision: a slab out-of-bounds read of > next_al_entry->lowest_vcn during inode read of a crafted image; a benign > mkntfs image is KASAN-clean (control). Arch-independent (lowest_vcn at > offset 8, fixed header offsetof(.., name) == 26; identical geometry built > -m32/-m64). Please add the appropriate Fixes: tag for the commit that > introduced ntfs_external_attr_find() in the new fs/ntfs driver (v7.1 merge > window). > > fs/ntfs/attrib.c | 5 ++--- > 1 file changed, 2 insertions(+), 3 deletions(-) > > diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c > index abc0add6f0c4..e425c8d074c5 100644 > --- a/fs/ntfs/attrib.c > +++ b/fs/ntfs/attrib.c > @@ -1185,9 +1185,8 @@ static int ntfs_external_attr_find(const __le32 type, > * we have reached the right one or the search has failed. > */ > if (lowest_vcn && (u8 *)next_al_entry >= al_start && > - (u8 *)next_al_entry + 6 < al_end && > - (u8 *)next_al_entry + le16_to_cpu( > - next_al_entry->length) <= al_end && > + ntfs_attr_list_entry_is_valid(next_al_entry, > + al_end) && > le64_to_cpu(next_al_entry->lowest_vcn) <= > lowest_vcn && > next_al_entry->type == al_entry->type && > -- > 2.43.0 > > -- Thanks, Hyunchul ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() 2026-06-04 4:29 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas 2026-06-04 4:29 ` [PATCH v3 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas @ 2026-06-04 4:29 ` Bryam Vargas 2026-06-04 8:44 ` Hyunchul Lee 2026-06-04 8:40 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Hyunchul Lee 2 siblings, 1 reply; 10+ messages in thread From: Bryam Vargas @ 2026-06-04 4:29 UTC (permalink / raw) To: Namjae Jeon, Hyunchul Lee; +Cc: linux-fsdevel, linux-kernel, Bryam Vargas The $MFT attribute-list walk in ntfs_read_inode_mount() validates each entry only with "(u8 *)al_entry + 6 > al_end" and "(u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end", but then reads al_entry->lowest_vcn (an __le64 at offset 8) and al_entry->mft_reference (offset 16) -- fields beyond the 6 bytes proven in range. al_entry->length is attacker-controlled and only required non-zero, so a short entry (e.g. length 8) placed at the tail passes both checks while the lowest_vcn / mft_reference reads fall past al_end. al_end is ni->attr_list + attr_list_size (the on-disk size); the buffer is kvzalloc(round_up(attr_list_size, SECTOR_SIZE)), so the sector rounding usually absorbs the over-read -- but when attr_list_size is a multiple of SECTOR_SIZE there is no slack and a crafted $MFT attribute list produces an out-of-bounds read at mount time. Validate the entry with ntfs_attr_list_entry_is_valid() (added in patch 1/3) before dereferencing it, matching the bound the other attribute-list walks now use. Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> --- v3 (Hyunchul Lee review): validate with the extracted ntfs_attr_list_entry_is_valid() helper (added in 1/3) rather than an open-coded bound, matching the other attribute-list walks. v2: dropped the redundant Reported-by; reproducer omitted on the public list (available to the maintainers on request). Sibling of the ntfs_external_attr_find() look-ahead OOB (2/3): the same struct attr_list_entry fixed fields (lowest_vcn at 8, mft_reference at 16) are read here with a weaker bound. Geometry is arch-independent (identical -m32/-m64). Please add the appropriate Fixes: tag for the commit that introduced ntfs_read_inode_mount() in the new fs/ntfs driver (v7.1 merge window). fs/ntfs/inode.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index a5f7400fd19d..77d7ea4a855b 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2002,8 +2002,7 @@ int ntfs_read_inode_mount(struct inode *vi) goto em_put_err_out; if (!al_entry->length) goto em_put_err_out; - if ((u8 *)al_entry + 6 > al_end || - (u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end) + if (!ntfs_attr_list_entry_is_valid(al_entry, al_end)) goto em_put_err_out; next_al_entry = (struct attr_list_entry *)((u8 *)al_entry + le16_to_cpu(al_entry->length)); -- 2.43.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() 2026-06-04 4:29 ` [PATCH v3 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() Bryam Vargas @ 2026-06-04 8:44 ` Hyunchul Lee 2026-06-08 1:51 ` Bryam Vargas 0 siblings, 1 reply; 10+ messages in thread From: Hyunchul Lee @ 2026-06-04 8:44 UTC (permalink / raw) To: Bryam Vargas; +Cc: Namjae Jeon, linux-fsdevel, linux-kernel 2026년 6월 4일 (목) 오후 1:29, Bryam Vargas <hexlabsecurity@proton.me>님이 작성: > > The $MFT attribute-list walk in ntfs_read_inode_mount() validates each > entry only with "(u8 *)al_entry + 6 > al_end" and > "(u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end", but then reads > al_entry->lowest_vcn (an __le64 at offset 8) and al_entry->mft_reference > (offset 16) -- fields beyond the 6 bytes proven in range. al_entry->length > is attacker-controlled and only required non-zero, so a short entry (e.g. > length 8) placed at the tail passes both checks while the lowest_vcn / > mft_reference reads fall past al_end. > > al_end is ni->attr_list + attr_list_size (the on-disk size); the buffer is > kvzalloc(round_up(attr_list_size, SECTOR_SIZE)), so the sector rounding > usually absorbs the over-read -- but when attr_list_size is a multiple of > SECTOR_SIZE there is no slack and a crafted $MFT attribute list produces an > out-of-bounds read at mount time. > > Validate the entry with ntfs_attr_list_entry_is_valid() (added in patch > 1/3) before dereferencing it, matching the bound the other attribute-list > walks now use. > > Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> > --- > v3 (Hyunchul Lee review): validate with the extracted > ntfs_attr_list_entry_is_valid() helper (added in 1/3) rather than an > open-coded bound, matching the other attribute-list walks. > v2: dropped the redundant Reported-by; reproducer omitted on the public list > (available to the maintainers on request). > > Sibling of the ntfs_external_attr_find() look-ahead OOB (2/3): the same > struct attr_list_entry fixed fields (lowest_vcn at 8, mft_reference at 16) > are read here with a weaker bound. Geometry is arch-independent (identical > -m32/-m64). Please add the appropriate Fixes: tag for the commit that > introduced ntfs_read_inode_mount() in the new fs/ntfs driver (v7.1 merge > window). > > fs/ntfs/inode.c | 3 +-- > 1 file changed, 1 insertion(+), 2 deletions(-) > > diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c > index a5f7400fd19d..77d7ea4a855b 100644 > --- a/fs/ntfs/inode.c > +++ b/fs/ntfs/inode.c > @@ -2002,8 +2002,7 @@ int ntfs_read_inode_mount(struct inode *vi) > goto em_put_err_out; > if (!al_entry->length) > goto em_put_err_out; It seems that some of the above conditions can be removed. > - if ((u8 *)al_entry + 6 > al_end || > - (u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end) > + if (!ntfs_attr_list_entry_is_valid(al_entry, al_end)) > goto em_put_err_out; > next_al_entry = (struct attr_list_entry *)((u8 *)al_entry + > le16_to_cpu(al_entry->length)); > -- > 2.43.0 > > -- Thanks, Hyunchul ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() 2026-06-04 8:44 ` Hyunchul Lee @ 2026-06-08 1:51 ` Bryam Vargas 0 siblings, 0 replies; 10+ messages in thread From: Bryam Vargas @ 2026-06-08 1:51 UTC (permalink / raw) To: Hyunchul Lee; +Cc: Namjae Jeon, linux-fsdevel, linux-kernel On Thu, 4 Jun 2026 17:44:45 +0900 Hyunchul Lee <hyc.lee@gmail.com> wrote: > It seems that some of the above conditions can be removed. Right -- with ntfs_attr_list_entry_is_valid() in place the "if (!al_entry->length)" check just above the call is redundant. The validator forces name_offset == sizeof(struct attr_list_entry) and requires name_offset + name_length * sizeof(__le16) <= length, so length == 0 is already rejected there (and length must be a multiple of 8, so a valid entry is at least 32 bytes and next_al_entry still advances -- no zero-length loop). I'll drop that check in v4. v4 also rebases onto the attribute value-validation series that was just applied: the ntfs_external_attr_find() rework there overlaps the change 2/3 makes, so the series needs to go on top of it. Bryam ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator 2026-06-04 4:29 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas 2026-06-04 4:29 ` [PATCH v3 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas 2026-06-04 4:29 ` [PATCH v3 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() Bryam Vargas @ 2026-06-04 8:40 ` Hyunchul Lee 2 siblings, 0 replies; 10+ messages in thread From: Hyunchul Lee @ 2026-06-04 8:40 UTC (permalink / raw) To: Bryam Vargas; +Cc: Namjae Jeon, linux-fsdevel, linux-kernel 2026년 6월 4일 (목) 오후 1:29, Bryam Vargas <hexlabsecurity@proton.me>님이 작성: > > A base inode's $ATTRIBUTE_LIST is sanity-checked by load_attribute_list() > only on the non-resident path; ntfs_read_locked_inode() copies a *resident* > attribute list into ni->attr_list with a plain memcpy() and no validation > at all. Every subsequent walk of ni->attr_list -- > ntfs_external_attr_find(), ntfs_inode_attach_all_extents() and > ntfs_attrlist_need() -- then trusts the entries are well-formed and reads > attr_list_entry fixed-header fields > (lowest_vcn at offset 8, mft_reference at offset 16, and the name) with > bounds that assume validation already happened. A crafted resident > attribute list therefore reaches those walks unvalidated and can drive > out-of-bounds reads of the attribute-list buffer. > > load_attribute_list() itself reads ale->name_offset (offset 7), > ale->mft_reference (offset 16) and the name length under only an > "al < al_start + size" bound, so its own validation loop can over-read the > fixed header of a truncated trailing entry by a few bytes. > > Factor the per-entry validation into ntfs_attr_list_entry_is_valid(), > which requires each entry's fixed header (offsetof(struct > attr_list_entry, name)) to be in range before any field is dereferenced, > that ale->length is a multiple of 8 covering the fixed header plus the > name, and that the entry is in use and carries a live MFT reference. > ntfs_attr_list_is_valid() walks the buffer with it and checks the entries > tile it exactly. Use the list validator in load_attribute_list() > (replacing the open-coded loop, closing its own over-read) and on the > resident path in ntfs_read_locked_inode() (which previously skipped > validation entirely); patches 2/3 reuse the per-entry helper at the other > two attribute-list walks. > > Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Looks good to me. Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> > --- > v3 (Hyunchul Lee review): > - Split the per-entry check into ntfs_attr_list_entry_is_valid() and call it > from the whole series (this patch's list walk, the look-ahead in 2/3 and > the $MFT walk in 3/3). > - Require ale->length to be a multiple of 8 (on-disk attribute-list entries > are 8-byte aligned). > v2: dropped the redundant Reported-by; reproducer omitted on the public list > (available to the maintainers on request). > > The out-of-bounds reads were confirmed under KASAN on the earlier revision > (crafted NTFS image, fs/ntfs built as a module): the crafted attribute list > that oopsed ntfs_external_attr_find() is now rejected at load with > "ntfs_read_locked_inode(): Corrupt attribute list." and a benign mkntfs > image still mounts. The validator is arch-independent -- struct > attr_list_entry is __packed, so sizeof() == offsetof(.., name) == 26 and > every field offset (length 4, lowest_vcn 8, mft_reference 16) is identical > built -m32 and -m64; a clean-room model of this v3 validator decides every > benign/crafted vector the same way on both ABIs, including the new > 8-byte-alignment rejection. checkpatch clean. > > fs/ntfs/attrib.c | 78 ++++++++++++++++++++++++++++++++++++++---------- > fs/ntfs/attrib.h | 4 +++ > fs/ntfs/inode.c | 6 ++++ > 3 files changed, 73 insertions(+), 15 deletions(-) > > diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c > index 421c6cdcbb53..abc0add6f0c4 100644 > --- a/fs/ntfs/attrib.c > +++ b/fs/ntfs/attrib.c > @@ -843,11 +843,71 @@ char *ntfs_attr_name_get(const struct ntfs_volume *vol, const __le16 *uname, > return NULL; > } > > +/* > + * ntfs_attr_list_entry_is_valid - sanity check one $ATTRIBUTE_LIST entry > + * @ale: the attribute-list entry to check > + * @al_end: end of the attribute-list buffer @ale lives in > + * > + * Verify that @ale is a well-formed attr_list_entry wholly contained in > + * [.., @al_end): its fixed header must lie in range before any field is > + * dereferenced, its length must be a multiple of 8 that covers the fixed > + * header plus the name, the name must lie within the buffer, the entry must > + * be in use and carry a live MFT reference. Return true if valid. > + */ > +bool ntfs_attr_list_entry_is_valid(const struct attr_list_entry *ale, > + const u8 *al_end) > +{ > + const u8 *al = (const u8 *)ale; > + u16 ale_len; > + > + /* The fixed header must be in bounds before it is parsed. */ > + if (al + offsetof(struct attr_list_entry, name) > al_end) > + return false; > + ale_len = le16_to_cpu(ale->length); > + /* On-disk entries are 8-byte aligned (see struct attr_list_entry). */ > + if (ale_len & 7) > + return false; > + if (ale->name_offset != sizeof(struct attr_list_entry)) > + return false; > + if ((u32)ale->name_offset + > + (u32)ale->name_length * sizeof(__le16) > ale_len || > + al + ale_len > al_end) > + return false; > + if (ale->type == AT_UNUSED) > + return false; > + if (MSEQNO_LE(ale->mft_reference) == 0) > + return false; > + return true; > +} > + > +/* > + * ntfs_attr_list_is_valid - sanity check an in-memory $ATTRIBUTE_LIST > + * @al_start: start of the attribute list buffer > + * @size: length of the attribute list in bytes > + * > + * Verify that [@al_start, @al_start + @size) is a sequence of valid > + * attr_list_entry records (see ntfs_attr_list_entry_is_valid()) that tile the > + * buffer exactly. Return true if valid, false otherwise. > + */ > +bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size) > +{ > + const u8 *al = al_start; > + const u8 *al_end = al_start + size; > + > + while (al < al_end) { > + const struct attr_list_entry *ale = > + (const struct attr_list_entry *)al; > + > + if (!ntfs_attr_list_entry_is_valid(ale, al_end)) > + return false; > + al += le16_to_cpu(ale->length); > + } > + return al == al_end; > +} > + > int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size) > { > struct inode *attr_vi = NULL; > - u8 *al; > - struct attr_list_entry *ale; > > if (!al_start || size <= 0) > return -EINVAL; > @@ -869,19 +929,7 @@ int load_attribute_list(struct ntfs_inode *base_ni, u8 *al_start, const s64 size > } > iput(attr_vi); > > - for (al = al_start; al < al_start + size; al += le16_to_cpu(ale->length)) { > - ale = (struct attr_list_entry *)al; > - if (ale->name_offset != sizeof(struct attr_list_entry)) > - break; > - if (le16_to_cpu(ale->length) <= ale->name_offset + ale->name_length || > - al + le16_to_cpu(ale->length) > al_start + size) > - break; > - if (ale->type == AT_UNUSED) > - break; > - if (MSEQNO_LE(ale->mft_reference) == 0) > - break; > - } > - if (al != al_start + size) { > + if (!ntfs_attr_list_is_valid(al_start, size)) { > ntfs_error(base_ni->vol->sb, "Corrupt attribute list, mft = %llu", > base_ni->mft_no); > return -EIO; > diff --git a/fs/ntfs/attrib.h b/fs/ntfs/attrib.h > index f7acc7986b09..e2224fbfaabe 100644 > --- a/fs/ntfs/attrib.h > +++ b/fs/ntfs/attrib.h > @@ -71,6 +71,10 @@ int ntfs_attr_lookup(const __le32 type, const __le16 *name, > const u32 name_len, const u32 ic, > const s64 lowest_vcn, const u8 *val, const u32 val_len, > struct ntfs_attr_search_ctx *ctx); > +bool ntfs_attr_list_entry_is_valid(const struct attr_list_entry *ale, > + const u8 *al_end); > +bool ntfs_attr_list_is_valid(const u8 *al_start, s64 size); > + > int load_attribute_list(struct ntfs_inode *base_ni, > u8 *al_start, const s64 size); > > diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c > index 360bebd1ee3f..a5f7400fd19d 100644 > --- a/fs/ntfs/inode.c > +++ b/fs/ntfs/inode.c > @@ -848,6 +848,12 @@ static int ntfs_read_locked_inode(struct inode *vi) > a->data.resident.value_offset), > le32_to_cpu( > a->data.resident.value_length)); > + /* A resident list is not validated on load; check it now. */ > + if (!ntfs_attr_list_is_valid(ni->attr_list, > + ni->attr_list_size)) { > + ntfs_error(vi->i_sb, "Corrupt attribute list."); > + goto unm_err_out; > + } > } > } > skip_attr_list_load: > -- > 2.43.0 > > -- Thanks, Hyunchul ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-06-08 1:51 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-06-03 17:59 [PATCH v2 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas 2026-06-03 18:00 ` [PATCH v2 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas 2026-06-03 18:00 ` [PATCH v2 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() Bryam Vargas 2026-06-04 4:29 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Bryam Vargas 2026-06-04 4:29 ` [PATCH v3 2/3] ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() Bryam Vargas 2026-06-04 8:41 ` Hyunchul Lee 2026-06-04 4:29 ` [PATCH v3 3/3] ntfs: bound the attribute-list entry in ntfs_read_inode_mount() Bryam Vargas 2026-06-04 8:44 ` Hyunchul Lee 2026-06-08 1:51 ` Bryam Vargas 2026-06-04 8:40 ` [PATCH v3 1/3] ntfs: validate resident attribute lists and harden the validator Hyunchul Lee
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
Powered by JetHome