Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Hyunchul Lee and committed by
Namjae Jeon
6ceb4cc8 77f58db7

+72 -12
+70 -10
fs/ntfs/attrib.c
··· 570 570 return ERR_PTR(err); 571 571 } 572 572 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 + 573 602 /* 574 603 * ntfs_attr_find - find (next) attribute in mft record 575 604 * @type: attribute type to find ··· 741 712 continue; 742 713 } 743 714 } 715 + 716 + /* Validate attribute's value offset/length */ 717 + if (!a->non_resident) { 718 + u32 min_len; 719 + u32 value_length = le32_to_cpu(a->data.resident.value_length); 720 + u16 value_offset = le16_to_cpu(a->data.resident.value_offset); 721 + 722 + if (value_length > le32_to_cpu(a->length) || 723 + value_offset > le32_to_cpu(a->length) - value_length) 724 + break; 725 + 726 + min_len = ntfs_resident_attr_min_value_length(a->type); 727 + if (min_len && value_length < min_len) { 728 + ntfs_error(vol->sb, 729 + "Too small %#x resident attribute value in MFT record %lld\n", 730 + le32_to_cpu(a->type), (long long)ctx->ntfs_ino->mft_no); 731 + break; 732 + } 733 + } else { 734 + u32 min_len; 735 + u16 mp_offset; 736 + 737 + min_len = offsetof(struct attr_record, data.non_resident.initialized_size) + 738 + sizeof(a->data.non_resident.initialized_size); 739 + if (le32_to_cpu(a->length) < min_len) 740 + break; 741 + 742 + mp_offset = le16_to_cpu(a->data.non_resident.mapping_pairs_offset); 743 + if (mp_offset < min_len || 744 + mp_offset > le32_to_cpu(a->length)) 745 + break; 746 + } 747 + 744 748 /* 745 749 * The names match or @name not present and attribute is 746 750 * unnamed. If no @val specified, we have found the attribute 747 751 * and are done. 748 752 */ 749 - if (!val) 753 + if (!val || a->non_resident) 750 754 return 0; 751 755 /* @val is present; compare values. */ 752 756 else { 753 - register int rc; 757 + u32 value_length = le32_to_cpu(a->data.resident.value_length); 758 + int rc; 754 759 755 760 rc = memcmp(val, (u8 *)a + le16_to_cpu( 756 761 a->data.resident.value_offset), 757 - min_t(u32, val_len, le32_to_cpu( 758 - a->data.resident.value_length))); 762 + min_t(u32, val_len, value_length)); 759 763 /* 760 764 * If @val collates before the current attribute's 761 765 * value, there is no matching attribute. 762 766 */ 763 767 if (!rc) { 764 - register u32 avl; 765 - 766 - avl = le32_to_cpu(a->data.resident.value_length); 767 - if (val_len == avl) 768 + if (val_len == value_length) 768 769 return 0; 769 - if (val_len < avl) 770 + if (val_len < value_length) 770 771 return -ENOENT; 771 772 } else if (rc < 0) 772 773 return -ENOENT; 773 774 } 774 775 } 775 - ntfs_error(vol->sb, "Inode is corrupt. Run chkdsk."); 776 + ntfs_error(vol->sb, "mft %#llx, type %#x is corrupt. Run chkdsk.", 777 + (long long)ctx->ntfs_ino->mft_no, le32_to_cpu(type)); 776 778 NVolSetErrors(vol); 777 779 return -EIO; 778 780 }
+2 -2
fs/ntfs/reparse.c
··· 450 450 xrni = xr->idx_ni; 451 451 452 452 if (!ntfs_attr_exist(ni, AT_REPARSE_POINT, AT_UNNAMED, 0)) { 453 - u8 dummy = 0; 453 + struct reparse_point rp = {0, }; 454 454 455 455 /* 456 456 * no reparse data attribute : add one, ··· 463 463 goto out; 464 464 } 465 465 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)); 467 467 if (err) { 468 468 ntfs_index_ctx_put(xr); 469 469 goto out;