Commit 0c0b282d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ntfs fixes from Namjae Jeon:

 - Check the index depth limit via ntfs_icx_parent_inc(), avoiding
   context corruption from excessively deep child chains

 - Switch security descriptor allocation to kzalloc() to avoid leaking
   uninitialized memory

 - Prevent an inconsistent state where vol->volume_label becomes NULL on
   allocation failure

 - Validate MFT records by verifying that attrs_offset sits within
   bytes_in_use

 - Fix an off-by-one boundary comparison, correctly catching the
   out-of-range MFT record number

 - Validate the attribute name offset and length bounds prior to
   AT_UNUSED enumeration

 - Check for a valid left neighbor before runlist merges to prevent an
   8byte out-of-bounds write on crafted volumes

 - Add the missing record comparison against $MFTMirr during mount

 - Fix wrong inode lookup when writing extent MFT records

 - Redirty folio on memory allocation failure in ntfs_write_mft_block()

 - Capture and propagate $MFTMirr sync errors during writeback

 - Ensure MFT mirror and synchronous writes wait for I/O completion

 - Fix buffer overflow/heap over-read in ntfs_bdev_write() when cluster
   size is smaller than PAGE_SIZE

 - Fix use-after-free in ntfs_inode_sync_filename() when parent index
   inode is evicted while still holding its mrec_lock

 - Update resident attribute length validation to match $AttrDef

 - Fix refcount underflow and UAF of the global upcase table

 - Fix two smatch warnings

* tag 'ntfs-for-7.1-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs:
  ntfs: restore $MFT mirror contents check
  ntfs: fix empty_buf and ra lifetime bugs in ntfs_empty_logfile()
  ntfs: validate attribute name bounds before returning it
  ntfs: fix MFT bitmap scan 2^32 boundary check
  ntfs: validate MFT attrs_offset against bytes_in_use
  ntfs: fix missing kstrdup() error check in ntfs_write_volume_label()
  ntfs: avoid leaking uninitialised bytes in new security descriptors
  ntfs: fix out-of-bounds write in ntfs_index_walk_down()
  ntfs: fix out-of-bounds write in ntfs_rl_collapse_range() merge path
  ntfs: fix variable dereferenced before check ni in ntfs_attr_open()
  ntfs: fix default_upcase refcount underflow and UAF on fs_context teardown
  ntfs: match ntfs_resident_attr_min_value_length with $AttrDef
  ntfs: avoid use-after-free of index inode in ntfs_inode_sync_filename()
  ntfs: fix copy length in ntfs_bdev_write() for non-page-aligned start
  ntfs: wait for sync mft writes to complete
  ntfs: capture mft mirror sync errors in ntfs_write_mft_block()
  ntfs: redirty folio when ntfs_write_mft_block() runs out of memory
  ntfs: use base mft_no when looking up base inode for extent record
  ntfs: fix variable dereferenced before check ni and attr in ntfs_attrlist_entry_add()
parents 650d2133 2beaa98b
Loading
Loading
Loading
Loading
+22 −24
Original line number Diff line number Diff line
@@ -583,24 +583,13 @@ static u32 ntfs_resident_attr_min_value_length(const __le32 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);
		return offsetof(struct file_name_attr, file_name) +
			sizeof(__le16) * 1;
	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;
	}
@@ -672,6 +661,9 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
	__le16 *upcase = vol->upcase;
	u32 upcase_len = vol->upcase_len;
	unsigned int space;
	u16 name_offset;
	u32 attr_len;
	u32 name_size;

	/*
	 * Iterate over attributes in mft record starting at @ctx->attr, or the
@@ -699,6 +691,20 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
			return -ENOENT;
		if (unlikely(!a->length))
			break;
		if (a->name_length) {
			name_offset = le16_to_cpu(a->name_offset);
			attr_len = le32_to_cpu(a->length);
			name_size = a->name_length * sizeof(__le16);

			if (name_offset > attr_len ||
			    attr_len - name_offset < name_size) {
				ntfs_error(vol->sb,
					   "Corrupt attribute name in MFT record %llu\n",
					   ctx->ntfs_ino->mft_no);
				break;
			}
		}

		if (type == AT_UNUSED)
			return 0;
		if (a->type != type)
@@ -712,14 +718,6 @@ static int ntfs_attr_find(const __le32 type, const __le16 *name,
			if (a->name_length)
				return -ENOENT;
		} else {
			if (a->name_length && ((le16_to_cpu(a->name_offset) +
					       a->name_length * sizeof(__le16)) >
						le32_to_cpu(a->length))) {
				ntfs_error(vol->sb, "Corrupt attribute name in MFT record %llu\n",
					   ctx->ntfs_ino->mft_no);
				break;
			}

			if (!ntfs_are_names_equal(name, name_len,
					(__le16 *)((u8 *)a + le16_to_cpu(a->name_offset)),
					a->name_length, ic, upcase, upcase_len)) {
@@ -2924,12 +2922,12 @@ int ntfs_attr_open(struct ntfs_inode *ni, const __le32 type,
	struct ntfs_inode *base_ni;
	int err;

	ntfs_debug("Entering for inode %lld, attr 0x%x.\n",
			(unsigned long long)ni->mft_no, type);

	if (!ni || !ni->vol)
		return -EINVAL;

	ntfs_debug("Entering for inode %lld, attr 0x%x.\n",
			ni->mft_no, type);

	if (NInoAttr(ni))
		base_ni = ni->ext.base_ntfs_ino;
	else
+3 −4
Original line number Diff line number Diff line
@@ -119,15 +119,14 @@ int ntfs_attrlist_entry_add(struct ntfs_inode *ni, struct attr_record *attr)
	struct mft_record *ni_mrec;
	u8 *old_al;

	ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n",
			(long long) ni->mft_no,
			(unsigned int) le32_to_cpu(attr->type));

	if (!ni || !attr) {
		ntfs_debug("Invalid arguments.\n");
		return -EINVAL;
	}

	ntfs_debug("Entering for inode 0x%llx, attr 0x%x.\n",
			ni->mft_no, (unsigned int) le32_to_cpu(attr->type));

	ni_mrec = map_mft_record(ni);
	if (IS_ERR(ni_mrec)) {
		ntfs_debug("Invalid arguments.\n");
+5 −2
Original line number Diff line number Diff line
@@ -97,6 +97,8 @@ int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size
		idx_end++;

	for (; idx < idx_end; idx++, from = 0) {
		u32 len;

		folio = read_mapping_folio(sb->s_bdev->bd_mapping, idx, NULL);
		if (IS_ERR(folio)) {
			ntfs_error(sb, "Unable to read %ld page", idx);
@@ -105,9 +107,10 @@ int ntfs_bdev_write(struct super_block *sb, void *buf, loff_t start, size_t size

		offset = (loff_t)idx << PAGE_SHIFT;
		to = min_t(u32, end - offset, PAGE_SIZE);
		len = to - from;

		memcpy_to_folio(folio, from, buf + buf_off, to);
		buf_off += to;
		memcpy_to_folio(folio, from, buf + buf_off, len);
		buf_off += len;
		folio_mark_uptodate(folio);
		folio_mark_dirty(folio);
		folio_put(folio);
+12 −5
Original line number Diff line number Diff line
@@ -677,11 +677,11 @@ static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_bl

static int ntfs_icx_parent_inc(struct ntfs_index_context *icx)
{
	icx->pindex++;
	if (icx->pindex >= MAX_PARENT_VCN) {
	if (icx->pindex >= MAX_PARENT_VCN - 1) {
		ntfs_error(icx->idx_ni->vol->sb, "Index is over %d level deep", MAX_PARENT_VCN);
		return -EOPNOTSUPP;
	}
	icx->pindex++;
	return 0;
}

@@ -1970,6 +1970,7 @@ struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_ind
{
	struct index_entry *entry;
	struct index_block *ib;
	int err;
	s64 vcn;

	entry = ie;
@@ -1979,14 +1980,20 @@ struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_ind
			ib = kvzalloc(ictx->block_size, GFP_NOFS);
			if (!ib)
				return ERR_PTR(-ENOMEM);
			/* down from level zero */
			/*
			 * Descending from root index (level 0) to the first
			 * child level. is_in_root == true implies pindex == 0,
			 * so advance to level 1.
			 */
			ictx->pindex = 1;
			ictx->ir = NULL;
			ictx->ib = ib;
			ictx->pindex = 1;
			ictx->is_in_root = false;
		} else {
			/* down from non-zero level */
			ictx->pindex++;
			err = ntfs_icx_parent_inc(ictx);
			if (err)
				return ERR_PTR(err);
		}

		ictx->parent_pos[ictx->pindex] = 0;
+3 −3
Original line number Diff line number Diff line
@@ -2582,8 +2582,8 @@ int ntfs_inode_sync_filename(struct ntfs_inode *ni)

		mutex_lock_nested(&index_ni->mrec_lock, NTFS_INODE_MUTEX_PARENT);
		if (NInoBeingDeleted(ni)) {
			iput(index_vi);
			mutex_unlock(&index_ni->mrec_lock);
			iput(index_vi);
			continue;
		}

@@ -2591,8 +2591,8 @@ int ntfs_inode_sync_filename(struct ntfs_inode *ni)
		if (!ictx) {
			ntfs_error(sb, "Failed to get index ctx, inode %llu",
					index_ni->mft_no);
			iput(index_vi);
			mutex_unlock(&index_ni->mrec_lock);
			iput(index_vi);
			continue;
		}

@@ -2601,8 +2601,8 @@ int ntfs_inode_sync_filename(struct ntfs_inode *ni)
			ntfs_debug("Index lookup failed, inode %llu",
					index_ni->mft_no);
			ntfs_index_ctx_put(ictx);
			iput(index_vi);
			mutex_unlock(&index_ni->mrec_lock);
			iput(index_vi);
			continue;
		}
		/* Update flags and file size. */
Loading