mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
ntfs: validate non-resident attribute offsets
ntfs_attr_update_meta() shifts the attribute name when converting between
non-sparse and sparse attributes. Converting to sparse also adds the
compressed_size field before the name and mapping pairs, requiring eight
additional bytes in the attribute record.
However, the validator does not check that name_offset is within safe
boundaries for these operations or that the additional space is available.
A malicious MFT record could set name_offset such that:
1. The name is positioned at the very end of a non-sparse attribute.
Converting to sparse would shift the name forward by 8 bytes,
writing beyond the attribute boundary.
2. The name overlaps with the mapping pairs, causing corruption during
conversion.
Add validation to ensure:
- For named attributes, name_offset is within valid bounds
- Name does not extend beyond the attribute or overlap with mapping pairs
- For non-sparse, non-compressed attributes, eight bytes are available
after mapping_pairs_offset for the compressed_size field
The space check also covers unnamed attributes, for which name_offset = 0
is valid and no name range needs to be checked.
Fixes: 7e2a1c554bc4 ("ntfs: Fix min_len for compressed/sparse attributes in ntfs_non_resident_attr_value_is_valid()")
Cc: stable@vger.kernel.org
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
Co-developed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
121dedb3b6
commit
a83e82b0ec
|
|
@ -693,6 +693,8 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
|
|||
u32 attr_len;
|
||||
u32 min_len;
|
||||
u16 mp_offset;
|
||||
u16 name_offset;
|
||||
u32 name_end;
|
||||
|
||||
attr_len = le32_to_cpu(a->length);
|
||||
min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
|
||||
|
|
@ -706,7 +708,27 @@ static bool ntfs_non_resident_attr_value_is_valid(const struct attr_record *a)
|
|||
return false;
|
||||
|
||||
mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
|
||||
return mp_offset >= min_len && mp_offset <= attr_len;
|
||||
if (mp_offset < min_len || mp_offset > attr_len)
|
||||
return false;
|
||||
|
||||
if (a->name_length) {
|
||||
name_offset = le16_to_cpu(a->name_offset);
|
||||
|
||||
if (name_offset < min_len || name_offset >= attr_len)
|
||||
return false;
|
||||
|
||||
name_end = name_offset + a->name_length * sizeof(__le16);
|
||||
if (name_end > attr_len || name_end > mp_offset)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Ensure there's room for the compressed_size field if needed. */
|
||||
if (!(a->flags & (ATTR_IS_SPARSE | ATTR_COMPRESSION_MASK)) &&
|
||||
attr_len - mp_offset <
|
||||
sizeof(a->data.non_resident.compressed_size))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ntfs_attr_value_is_valid(struct ntfs_volume *vol,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user