Skip to content

Commit 8f1918f

Browse files
hclee1Naim
authored andcommitted
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>
1 parent 69c6267 commit 8f1918f

2 files changed

Lines changed: 72 additions & 12 deletions

File tree

fs/ntfs/attrib.c

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,35 @@ struct runlist_element *ntfs_attr_find_vcn_nolock(struct ntfs_inode *ni, const s
570570
return ERR_PTR(err);
571571
}
572572

573+
static u32 ntfs_resident_attr_min_value_length(const __le32 type)
574+
{
575+
switch (type) {
576+
case AT_STANDARD_INFORMATION:
577+
return offsetof(struct standard_information, ver) +
578+
sizeof(((struct standard_information *)0)->ver.v1.reserved12);
579+
case AT_ATTRIBUTE_LIST:
580+
return offsetof(struct attr_list_entry, name);
581+
case AT_FILE_NAME:
582+
return offsetof(struct file_name_attr, file_name);
583+
case AT_OBJECT_ID:
584+
return sizeof(struct guid);
585+
case AT_SECURITY_DESCRIPTOR:
586+
return sizeof(struct security_descriptor_relative);
587+
case AT_VOLUME_INFORMATION:
588+
return sizeof(struct volume_information);
589+
case AT_INDEX_ROOT:
590+
return sizeof(struct index_root);
591+
case AT_REPARSE_POINT:
592+
return offsetof(struct reparse_point, reparse_data);
593+
case AT_EA_INFORMATION:
594+
return sizeof(struct ea_information);
595+
case AT_EA:
596+
return offsetof(struct ea_attr, ea_name) + 1;
597+
default:
598+
return 0;
599+
}
600+
}
601+
573602
/*
574603
* ntfs_attr_find - find (next) attribute in mft record
575604
* @type: attribute type to find
@@ -712,38 +741,69 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
712741
continue;
713742
}
714743
}
744+
745+
/* Validate attribute's value offset/length */
746+
if (!a->non_resident) {
747+
u32 min_len;
748+
u32 value_length = le32_to_cpu(a->data.resident.value_length);
749+
u16 value_offset = le16_to_cpu(a->data.resident.value_offset);
750+
751+
if (value_length > le32_to_cpu(a->length) ||
752+
value_offset > le32_to_cpu(a->length) - value_length)
753+
break;
754+
755+
min_len = ntfs_resident_attr_min_value_length(a->type);
756+
if (min_len && value_length < min_len) {
757+
ntfs_error(vol->sb,
758+
"Too small %#x resident attribute value in MFT record %lld\n",
759+
le32_to_cpu(a->type), (long long)ctx->ntfs_ino->mft_no);
760+
break;
761+
}
762+
} else {
763+
u32 min_len;
764+
u16 mp_offset;
765+
766+
min_len = offsetof(struct attr_record, data.non_resident.initialized_size) +
767+
sizeof(a->data.non_resident.initialized_size);
768+
if (le32_to_cpu(a->length) < min_len)
769+
break;
770+
771+
mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset);
772+
if (mp_offset < min_len ||
773+
mp_offset > le32_to_cpu(a->length))
774+
break;
775+
}
776+
715777
/*
716778
* The names match or @name not present and attribute is
717779
* unnamed. If no @val specified, we have found the attribute
718780
* and are done.
719781
*/
720-
if (!val)
782+
if (!val || a->non_resident)
721783
return 0;
722784
/* @val is present; compare values. */
723785
else {
724-
register int rc;
786+
u32 value_length = le32_to_cpu(a->data.resident.value_length);
787+
int rc;
725788

726789
rc = memcmp(val, (u8 *)a + le16_to_cpu(
727790
a->data.resident.value_offset),
728-
min_t(u32, val_len, le32_to_cpu(
729-
a->data.resident.value_length)));
791+
min_t(u32, val_len, value_length));
730792
/*
731793
* If @val collates before the current attribute's
732794
* value, there is no matching attribute.
733795
*/
734796
if (!rc) {
735-
register u32 avl;
736-
737-
avl = le32_to_cpu(a->data.resident.value_length);
738-
if (val_len == avl)
797+
if (val_len == value_length)
739798
return 0;
740-
if (val_len < avl)
799+
if (val_len < value_length)
741800
return -ENOENT;
742801
} else if (rc < 0)
743802
return -ENOENT;
744803
}
745804
}
746-
ntfs_error(vol->sb, "Inode is corrupt. Run chkdsk.");
805+
ntfs_error(vol->sb, "mft %#llx, type %#x is corrupt. Run chkdsk.",
806+
(long long)ctx->ntfs_ino->mft_no, le32_to_cpu(type));
747807
NVolSetErrors(vol);
748808
return -EIO;
749809
}

fs/ntfs/reparse.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ static int ntfs_set_ntfs_reparse_data(struct ntfs_inode *ni, char *value, size_t
450450
xrni = xr->idx_ni;
451451

452452
if (!ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED, 0)) {
453-
u8 dummy = 0;
453+
struct reparse_point rp = {0, };
454454

455455
/*
456456
* 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
463463
goto out;
464464
}
465465

466-
err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, &dummy, 0);
466+
err = ntfs_attr_add(ni, AT_REPARSE_POINT, AT_UNNAMED, 0, (u8 *)&rp, sizeof(rp));
467467
if (err) {
468468
ntfs_index_ctx_put(xr);
469469
goto out;

0 commit comments

Comments
 (0)