Commit c8ef1a60 authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Mauro Carvalho Chehab
Browse files

media: v4l2-core: split out data copy from video_usercopy



The copy-in/out portions of video_usercopy() are about to
get more complex, so turn then into separate functions as
a cleanup first.

Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
Signed-off-by: default avatarHans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab+huawei@kernel.org>
parent 4a873f3f
Loading
Loading
Loading
Loading
+69 −39
Original line number Diff line number Diff line
@@ -3023,8 +3023,69 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
	return ret;
}

static unsigned int video_translate_cmd(unsigned int cmd)
{
	return cmd;
}

static int video_get_user(void __user *arg, void *parg, unsigned int cmd,
			  bool *always_copy)
{
	unsigned int n = _IOC_SIZE(cmd);

	if (!(_IOC_DIR(cmd) & _IOC_WRITE)) {
		/* read-only ioctl */
		memset(parg, 0, n);
		return 0;
	}

	switch (cmd) {
	default:
		/*
		 * In some cases, only a few fields are used as input,
		 * i.e. when the app sets "index" and then the driver
		 * fills in the rest of the structure for the thing
		 * with that index.  We only need to copy up the first
		 * non-input field.
		 */
		if (v4l2_is_known_ioctl(cmd)) {
			u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;

			if (flags & INFO_FL_CLEAR_MASK)
				n = (flags & INFO_FL_CLEAR_MASK) >> 16;
			*always_copy = flags & INFO_FL_ALWAYS_COPY;
		}

		if (copy_from_user(parg, (void __user *)arg, n))
			return -EFAULT;

		/* zero out anything we don't copy from userspace */
		if (n < _IOC_SIZE(cmd))
			memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
		break;
	}

	return 0;
}

static int video_put_user(void __user *arg, void *parg, unsigned int cmd)
{
	if (!(_IOC_DIR(cmd) & _IOC_READ))
		return 0;

	switch (cmd) {
	default:
		/*  Copy results into user buffer  */
		if (copy_to_user(arg, parg, _IOC_SIZE(cmd)))
			return -EFAULT;
		break;
	}

	return 0;
}

long
video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg,
	       v4l2_kioctl func)
{
	char	sbuf[128];
@@ -3036,6 +3097,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
	size_t  array_size = 0;
	void __user *user_ptr = NULL;
	void	**kernel_ptr = NULL;
	unsigned int cmd = video_translate_cmd(orig_cmd);
	const size_t ioc_size = _IOC_SIZE(cmd);

	/*  Copy arguments into temp kernel buffer  */
@@ -3050,37 +3112,12 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
			parg = mbuf;
		}

		err = -EFAULT;
		if (_IOC_DIR(cmd) & _IOC_WRITE) {
			unsigned int n = ioc_size;

			/*
			 * In some cases, only a few fields are used as input,
			 * i.e. when the app sets "index" and then the driver
			 * fills in the rest of the structure for the thing
			 * with that index.  We only need to copy up the first
			 * non-input field.
			 */
			if (v4l2_is_known_ioctl(cmd)) {
				u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags;

				if (flags & INFO_FL_CLEAR_MASK)
					n = (flags & INFO_FL_CLEAR_MASK) >> 16;
				always_copy = flags & INFO_FL_ALWAYS_COPY;
	}

			if (copy_from_user(parg, (void __user *)arg, n))
	err = video_get_user((void __user *)arg, parg, orig_cmd, &always_copy);
	if (err)
		goto out;

			/* zero out anything we don't copy from userspace */
			if (n < ioc_size)
				memset((u8 *)parg + n, 0, ioc_size - n);
		} else {
			/* read-only ioctl */
			memset(parg, 0, ioc_size);
		}
	}

	err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
	if (err < 0)
		goto out;
@@ -3131,15 +3168,8 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
		goto out;

out_array_args:
	/*  Copy results into user buffer  */
	switch (_IOC_DIR(cmd)) {
	case _IOC_READ:
	case (_IOC_WRITE | _IOC_READ):
		if (copy_to_user((void __user *)arg, parg, ioc_size))
	if (video_put_user((void __user *)arg, parg, orig_cmd))
		err = -EFAULT;
		break;
	}

out:
	kvfree(mbuf);
	return err;