Commit 82e16f86 authored by Konstantin Komarov's avatar Konstantin Komarov Committed by ZhangPeng
Browse files

fs/ntfs3: Do copy_to_user out of run_lock

stable inclusion
from stable-v6.6.47
commit 5472b587cf71f72fa4be675c30fab677e65c6f63
bugzilla: https://gitee.com/openeuler/kernel/issues/IAHMJO

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=5472b587cf71f72fa4be675c30fab677e65c6f63



--------------------------------

[ Upstream commit d57431c6f511bf020e474026d9f3123d7bfbea8c ]

In order not to call copy_to_user (from fiemap_fill_next_extent)
we allocate memory in the kernel, fill it and copy it to user memory
after up_read(run_lock).

Reported-by: default avatar <syzbot+36bb70085ef6edc2ebb9@syzkaller.appspotmail.com>
Signed-off-by: default avatarKonstantin Komarov <almaz.alexandrovich@paragon-software.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarZhangPeng <zhangpeng362@huawei.com>
parent b4c94b9f
Loading
Loading
Loading
Loading
+72 −3
Original line number Diff line number Diff line
@@ -1896,6 +1896,47 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
	return REPARSE_LINK;
}

/*
 * fiemap_fill_next_extent_k - a copy of fiemap_fill_next_extent
 * but it accepts kernel address for fi_extents_start
 */
static int fiemap_fill_next_extent_k(struct fiemap_extent_info *fieinfo,
				     u64 logical, u64 phys, u64 len, u32 flags)
{
	struct fiemap_extent extent;
	struct fiemap_extent __user *dest = fieinfo->fi_extents_start;

	/* only count the extents */
	if (fieinfo->fi_extents_max == 0) {
		fieinfo->fi_extents_mapped++;
		return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
	}

	if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max)
		return 1;

	if (flags & FIEMAP_EXTENT_DELALLOC)
		flags |= FIEMAP_EXTENT_UNKNOWN;
	if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED)
		flags |= FIEMAP_EXTENT_ENCODED;
	if (flags & (FIEMAP_EXTENT_DATA_TAIL | FIEMAP_EXTENT_DATA_INLINE))
		flags |= FIEMAP_EXTENT_NOT_ALIGNED;

	memset(&extent, 0, sizeof(extent));
	extent.fe_logical = logical;
	extent.fe_physical = phys;
	extent.fe_length = len;
	extent.fe_flags = flags;

	dest += fieinfo->fi_extents_mapped;
	memcpy(dest, &extent, sizeof(extent));

	fieinfo->fi_extents_mapped++;
	if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max)
		return 1;
	return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0;
}

/*
 * ni_fiemap - Helper for file_fiemap().
 *
@@ -1906,6 +1947,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
	      __u64 vbo, __u64 len)
{
	int err = 0;
	struct fiemap_extent __user *fe_u = fieinfo->fi_extents_start;
	struct fiemap_extent *fe_k = NULL;
	struct ntfs_sb_info *sbi = ni->mi.sbi;
	u8 cluster_bits = sbi->cluster_bits;
	struct runs_tree *run;
@@ -1953,6 +1996,18 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
		goto out;
	}

	/*
	 * To avoid lock problems replace pointer to user memory by pointer to kernel memory.
	 */
	fe_k = kmalloc_array(fieinfo->fi_extents_max,
			     sizeof(struct fiemap_extent),
			     GFP_NOFS | __GFP_ZERO);
	if (!fe_k) {
		err = -ENOMEM;
		goto out;
	}
	fieinfo->fi_extents_start = fe_k;

	end = vbo + len;
	alloc_size = le64_to_cpu(attr->nres.alloc_size);
	if (end > alloc_size)
@@ -2041,8 +2096,9 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
			if (vbo + dlen >= end)
				flags |= FIEMAP_EXTENT_LAST;

			err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen,
			err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, dlen,
							flags);

			if (err < 0)
				break;
			if (err == 1) {
@@ -2062,7 +2118,8 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
		if (vbo + bytes >= end)
			flags |= FIEMAP_EXTENT_LAST;

		err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags);
		err = fiemap_fill_next_extent_k(fieinfo, vbo, lbo, bytes,
						flags);
		if (err < 0)
			break;
		if (err == 1) {
@@ -2075,7 +2132,19 @@ int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,

	up_read(run_lock);

	/*
	 * Copy to user memory out of lock
	 */
	if (copy_to_user(fe_u, fe_k,
			 fieinfo->fi_extents_max *
				 sizeof(struct fiemap_extent))) {
		err = -EFAULT;
	}

out:
	/* Restore original pointer. */
	fieinfo->fi_extents_start = fe_u;
	kfree(fe_k);
	return err;
}