mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
ntfs: add bound checking to ntfs_attr_find
Add bound validations in ntfs_attr_find to ensure attribute value offsets and lengths are safe to access. It verifies that resident attributes meet type-specific minimum length requirements and check the mapping_pairs_offset boundaries for non-resident attributes. Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
77f58db739
commit
6ceb4cc81e
|
|
@ -570,6 +570,35 @@ struct runlist_element *ntfs_attr_find_vcn_nolock(struct ntfs_inode *ni, const s
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static u32 ntfs_resident_attr_min_value_length(const __le32 type)
|
||||
{
|
||||
switch (type) {
|
||||
case AT_STANDARD_INFORMATION:
|
||||
return offsetof(struct standard_information, ver) +
|
||||
sizeof(((struct standard_information *)0)->ver.v1.reserved12);
|
||||
case AT_ATTRIBUTE_LIST:
|
||||
return offsetof(struct attr_list_entry, name);
|
||||
case AT_FILE_NAME:
|
||||
return offsetof(struct file_name_attr, file_name);
|
||||
case AT_OBJECT_ID:
|
||||
return sizeof(struct guid);
|
||||
case AT_SECURITY_DESCRIPTOR:
|
||||
return sizeof(struct security_descriptor_relative);
|
||||
case AT_VOLUME_INFORMATION:
|
||||
return sizeof(struct volume_information);
|
||||
case AT_INDEX_ROOT:
|
||||
return sizeof(struct index_root);
|
||||
case AT_REPARSE_POINT:
|
||||
return offsetof(struct reparse_point, reparse_data);
|
||||
case AT_EA_INFORMATION:
|
||||
return sizeof(struct ea_information);
|
||||
case AT_EA:
|
||||
return offsetof(struct ea_attr, ea_name) + 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ntfs_attr_find - find (next) attribute in mft record
|
||||
* @type: attribute type to find
|
||||
|
|
@ -712,38 +741,69 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
|
|||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate attribute's value offset/length */
|
||||
if (!a->non_resident) {
|
||||
u32 min_len;
|
||||
u32 value_length = le32_to_cpu(a->data.resident.value_length);
|
||||
u16 value_offset = le16_to_cpu(a->data.resident.value_offset);
|
||||
|
||||
if (value_length > le32_to_cpu(a->length) ||
|
||||
value_offset > le32_to_cpu(a->length) - value_length)
|
||||
break;
|
||||
|
||||
min_len = ntfs_resident_attr_min_value_length(a->type);
|
||||
if (min_len && value_length < min_len) {
|
||||
ntfs_error(vol->sb,
|
||||
"Too small %#x resident attribute value in MFT record %lld\n",
|
||||
le32_to_cpu(a->type), (long long)ctx->ntfs_ino->mft_no);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
u32 min_len;
|
||||
u16 mp_offset;
|
||||
|
||||
min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
|
||||
sizeof(a->data.non_resident.initialized_size);
|
||||
if (le32_to_cpu(a->length) < min_len)
|
||||
break;
|
||||
|
||||
mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
|
||||
if (mp_offset < min_len ||
|
||||
mp_offset > le32_to_cpu(a->length))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* The names match or @name not present and attribute is
|
||||
* unnamed. If no @val specified, we have found the attribute
|
||||
* and are done.
|
||||
*/
|
||||
if (!val)
|
||||
if (!val || a->non_resident)
|
||||
return 0;
|
||||
/* @val is present; compare values. */
|
||||
else {
|
||||
register int rc;
|
||||
u32 value_length = le32_to_cpu(a->data.resident.value_length);
|
||||
int rc;
|
||||
|
||||
rc = memcmp(val, (u8 *)a + le16_to_cpu(
|
||||
a->data.resident.value_offset),
|
||||
min_t(u32, val_len, le32_to_cpu(
|
||||
a->data.resident.value_length)));
|
||||
min_t(u32, val_len, value_length));
|
||||
/*
|
||||
* If @val collates before the current attribute's
|
||||
* value, there is no matching attribute.
|
||||
*/
|
||||
if (!rc) {
|
||||
register u32 avl;
|
||||
|
||||
avl = le32_to_cpu(a->data.resident.value_length);
|
||||
if (val_len == avl)
|
||||
if (val_len == value_length)
|
||||
return 0;
|
||||
if (val_len < avl)
|
||||
if (val_len < value_length)
|
||||
return -ENOENT;
|
||||
} else if (rc < 0)
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
ntfs_error(vol->sb, "Inode is corrupt. Run chkdsk.");
|
||||
ntfs_error(vol->sb, "mft %#llx, type %#x is corrupt. Run chkdsk.",
|
||||
(long long)ctx->ntfs_ino->mft_no, le32_to_cpu(type));
|
||||
NVolSetErrors(vol);
|
||||
return -EIO;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,7 +450,7 @@ static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t
|
|||
xrni = xr->idx_ni;
|
||||
|
||||
if (!ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED, 0)) {
|
||||
u8 dummy = 0;
|
||||
struct reparse_point rp = {0, };
|
||||
|
||||
/*
|
||||
* no reparse data attribute : add one,
|
||||
|
|
@ -463,7 +463,7 @@ static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t
|
|||
goto out;
|
||||
}
|
||||
|
||||
err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, &dummy, 0);
|
||||
err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, (u8 *)&rp, sizeof(rp));
|
||||
if (err) {
|
||||
ntfs_index_ctx_put(xr);
|
||||
goto out;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user