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 Reviewed-by: Hyunchul Lee --- v4: rebased onto the "ntfs: validate attribute values on lookup" series (0001-0004) now applied to ntfs-next. That series reworked ntfs_external_attr_find(), but its changes (resident @val matching and the attrlist duplicate check) do not touch this look-ahead block, so this patch is unchanged on top of it. Added the Fixes: tag requested by Hyunchul. Compile-tested on ntfs-next. v3 (Hyunchul Lee review): use the extracted ntfs_attr_list_entry_is_valid() helper 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). 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 2d675bf99ca7..1e0076ef81ef 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -1263,9 +1263,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