media: v4l2-core: split out data copy from video_usercopy
authorArnd Bergmann <arnd@arndb.de>
Mon, 16 Dec 2019 14:15:02 +0000 (15:15 +0100)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Fri, 3 Jan 2020 14:46:36 +0000 (15:46 +0100)
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: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
drivers/media/v4l2-core/v4l2-ioctl.c

index 282ffee..1f22b13 100644 (file)
@@ -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))
-                               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 = video_get_user((void __user *)arg, parg, orig_cmd, &always_copy);
+       if (err)
+               goto out;
+
        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))
-                       err = -EFAULT;
-               break;
-       }
-
+       if (video_put_user((void __user *)arg, parg, orig_cmd))
+               err = -EFAULT;
 out:
        kvfree(mbuf);
        return err;