Commit 6ceb4cc8 authored by Hyunchul Lee's avatar Hyunchul Lee Committed by Namjae Jeon
Browse files

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: default avatarHyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: default avatarNamjae Jeon <linkinjeon@kernel.org>
parent 77f58db7
Loading
Loading
Loading
Loading
+70 −10
Original line number Diff line number Diff line
@@ -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;
}
+2 −2
Original line number Diff line number Diff line
@@ -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;