summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryam Vargas <hexlabsecurity@proton.me>2026-06-07 23:30:00 +0300
committerNamjae Jeon <linkinjeon@kernel.org>2026-06-08 16:58:00 +0300
commit344b18f389f9934d59c7b0cf3d20541ea2e0da58 (patch)
treed75d55f3709fa7953cb0c23d7821429b19408137
parent7d19e1ffee084c4f7d321a360c14ba43404f7cc8 (diff)
downloadlinux-344b18f389f9934d59c7b0cf3d20541ea2e0da58.tar.xz
ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find()
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. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
-rw-r--r--fs/ntfs/attrib.c5
1 files changed, 2 insertions, 3 deletions
diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c
index 2d675bf99ca7..1e0076ef81ef 100644
--- a/fs/ntfs/attrib.c
+++ b/fs/ntfs/attrib.c
@@ -1263,9 +1263,8 @@ find_attr_list_attr:
* 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 &&