media: v4l2: remaining compat handlers
authorArnd Bergmann <arnd@arndb.de>
Fri, 30 Oct 2020 16:55:28 +0000 (17:55 +0100)
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>
Mon, 16 Nov 2020 09:31:06 +0000 (10:31 +0100)
There are eight remaining ioctl commands handled by copying
incompatible data structures in v4l2_compat_ioctl32(),
all of them fairly simple.

Change them to instead go through the native ioctl
infrastructure and only special-case the data copy.

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-compat-ioctl32.c

index fe681aa..b51fa68 100644 (file)
@@ -338,27 +338,23 @@ struct v4l2_standard32 {
        __u32                reserved[4];
 };
 
-static int get_v4l2_standard32(struct v4l2_standard __user *p64,
+static int get_v4l2_standard32(struct v4l2_standard *p64,
                               struct v4l2_standard32 __user *p32)
 {
        /* other fields are not set by the user, nor used by the driver */
-       if (!access_ok(p32, sizeof(*p32)) ||
-           assign_in_user(&p64->index, &p32->index))
-               return -EFAULT;
-       return 0;
+       return get_user(p64->index, &p32->index);
 }
 
-static int put_v4l2_standard32(struct v4l2_standard __user *p64,
+static int put_v4l2_standard32(struct v4l2_standard *p64,
                               struct v4l2_standard32 __user *p32)
 {
-       if (!access_ok(p32, sizeof(*p32)) ||
-           assign_in_user(&p32->index, &p64->index) ||
-           assign_in_user(&p32->id, &p64->id) ||
-           copy_in_user(p32->name, p64->name, sizeof(p32->name)) ||
-           copy_in_user(&p32->frameperiod, &p64->frameperiod,
+       if (put_user(p64->index, &p32->index) ||
+           put_user(p64->id, &p32->id) ||
+           copy_to_user(p32->name, p64->name, sizeof(p32->name)) ||
+           copy_to_user(&p32->frameperiod, &p64->frameperiod,
                         sizeof(p32->frameperiod)) ||
-           assign_in_user(&p32->framelines, &p64->framelines) ||
-           copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
+           put_user(p64->framelines, &p32->framelines) ||
+           copy_to_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
                return -EFAULT;
        return 0;
 }
@@ -689,33 +685,30 @@ struct v4l2_framebuffer32 {
        } fmt;
 };
 
-static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64,
+static int get_v4l2_framebuffer32(struct v4l2_framebuffer *p64,
                                  struct v4l2_framebuffer32 __user *p32)
 {
        compat_caddr_t tmp;
 
-       if (!access_ok(p32, sizeof(*p32)) ||
-           get_user(tmp, &p32->base) ||
-           put_user_force(compat_ptr(tmp), &p64->base) ||
-           assign_in_user(&p64->capability, &p32->capability) ||
-           assign_in_user(&p64->flags, &p32->flags) ||
-           copy_in_user(&p64->fmt, &p32->fmt, sizeof(p64->fmt)))
+       if (get_user(tmp, &p32->base) ||
+           get_user(p64->capability, &p32->capability) ||
+           get_user(p64->flags, &p32->flags) ||
+           copy_from_user(&p64->fmt, &p32->fmt, sizeof(p64->fmt)))
                return -EFAULT;
+       p64->base = (void __force *)compat_ptr(tmp);
+
        return 0;
 }
 
-static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *p64,
+static int put_v4l2_framebuffer32(struct v4l2_framebuffer *p64,
                                  struct v4l2_framebuffer32 __user *p32)
 {
-       void *base;
-
-       if (!access_ok(p32, sizeof(*p32)) ||
-           get_user(base, &p64->base) ||
-           put_user(ptr_to_compat((void __user *)base), &p32->base) ||
-           assign_in_user(&p32->capability, &p64->capability) ||
-           assign_in_user(&p32->flags, &p64->flags) ||
-           copy_in_user(&p32->fmt, &p64->fmt, sizeof(p64->fmt)))
+       if (put_user((uintptr_t)p64->base, &p32->base) ||
+           put_user(p64->capability, &p32->capability) ||
+           put_user(p64->flags, &p32->flags) ||
+           copy_to_user(&p32->fmt, &p64->fmt, sizeof(p64->fmt)))
                return -EFAULT;
+
        return 0;
 }
 
@@ -735,18 +728,18 @@ struct v4l2_input32 {
  * The 64-bit v4l2_input struct has extra padding at the end of the struct.
  * Otherwise it is identical to the 32-bit version.
  */
-static inline int get_v4l2_input32(struct v4l2_input __user *p64,
+static inline int get_v4l2_input32(struct v4l2_input *p64,
                                   struct v4l2_input32 __user *p32)
 {
-       if (copy_in_user(p64, p32, sizeof(*p32)))
+       if (copy_from_user(p64, p32, sizeof(*p32)))
                return -EFAULT;
        return 0;
 }
 
-static inline int put_v4l2_input32(struct v4l2_input __user *p64,
+static inline int put_v4l2_input32(struct v4l2_input *p64,
                                   struct v4l2_input32 __user *p32)
 {
-       if (copy_in_user(p32, p64, sizeof(*p32)))
+       if (copy_to_user(p32, p64, sizeof(*p32)))
                return -EFAULT;
        return 0;
 }
@@ -880,38 +873,38 @@ struct v4l2_event32_time32 {
        __u32                           reserved[8];
 };
 
-static int put_v4l2_event32(struct v4l2_event __user *p64,
+static int put_v4l2_event32(struct v4l2_event *p64,
                            struct v4l2_event32 __user *p32)
 {
-       if (!access_ok(p32, sizeof(*p32)) ||
-           assign_in_user(&p32->type, &p64->type) ||
-           copy_in_user(&p32->u, &p64->u, sizeof(p64->u)) ||
-           assign_in_user(&p32->pending, &p64->pending) ||
-           assign_in_user(&p32->sequence, &p64->sequence) ||
-           assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) ||
-           assign_in_user(&p32->timestamp.tv_nsec, &p64->timestamp.tv_nsec) ||
-           assign_in_user(&p32->id, &p64->id) ||
-           copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
+       if (put_user(p64->type, &p32->type) ||
+           copy_to_user(&p32->u, &p64->u, sizeof(p64->u)) ||
+           put_user(p64->pending, &p32->pending) ||
+           put_user(p64->sequence, &p32->sequence) ||
+           put_user(p64->timestamp.tv_sec, &p32->timestamp.tv_sec) ||
+           put_user(p64->timestamp.tv_nsec, &p32->timestamp.tv_nsec) ||
+           put_user(p64->id, &p32->id) ||
+           copy_to_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
                return -EFAULT;
        return 0;
 }
 
-static int put_v4l2_event32_time32(struct v4l2_event_time32 __user *p64,
+#ifdef CONFIG_COMPAT_32BIT_TIME
+static int put_v4l2_event32_time32(struct v4l2_event *p64,
                                   struct v4l2_event32_time32 __user *p32)
 {
-       if (!access_ok(p32, sizeof(*p32)) ||
-           assign_in_user(&p32->type, &p64->type) ||
-           copy_in_user(&p32->u, &p64->u, sizeof(p64->u)) ||
-           assign_in_user(&p32->pending, &p64->pending) ||
-           assign_in_user(&p32->sequence, &p64->sequence) ||
-           assign_in_user(&p32->timestamp.tv_sec, &p64->timestamp.tv_sec) ||
-           assign_in_user(&p32->timestamp.tv_nsec, &p64->timestamp.tv_nsec) ||
-           assign_in_user(&p32->id, &p64->id) ||
-           copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
+       if (put_user(p64->type, &p32->type) ||
+           copy_to_user(&p32->u, &p64->u, sizeof(p64->u)) ||
+           put_user(p64->pending, &p32->pending) ||
+           put_user(p64->sequence, &p32->sequence) ||
+           put_user(p64->timestamp.tv_sec, &p32->timestamp.tv_sec) ||
+           put_user(p64->timestamp.tv_nsec, &p32->timestamp.tv_nsec) ||
+           put_user(p64->id, &p32->id) ||
+           copy_to_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
                return -EFAULT;
        return 0;
 }
 #endif
+#endif
 
 struct v4l2_edid32 {
        __u32 pad;
@@ -921,34 +914,23 @@ struct v4l2_edid32 {
        compat_caddr_t edid;
 };
 
-static int get_v4l2_edid32(struct v4l2_edid __user *p64,
+static int get_v4l2_edid32(struct v4l2_edid *p64,
                           struct v4l2_edid32 __user *p32)
 {
-       compat_uptr_t tmp;
-
-       if (!access_ok(p32, sizeof(*p32)) ||
-           assign_in_user(&p64->pad, &p32->pad) ||
-           assign_in_user(&p64->start_block, &p32->start_block) ||
-           assign_in_user_cast(&p64->blocks, &p32->blocks) ||
-           get_user(tmp, &p32->edid) ||
-           put_user_force(compat_ptr(tmp), &p64->edid) ||
-           copy_in_user(p64->reserved, p32->reserved, sizeof(p64->reserved)))
+       compat_uptr_t edid;
+
+       if (copy_from_user(p64, p32, offsetof(struct v4l2_edid32, edid)) ||
+           get_user(edid, &p32->edid))
                return -EFAULT;
+
+       p64->edid = (void __force *)compat_ptr(edid);
        return 0;
 }
 
-static int put_v4l2_edid32(struct v4l2_edid __user *p64,
+static int put_v4l2_edid32(struct v4l2_edid *p64,
                           struct v4l2_edid32 __user *p32)
 {
-       void *edid;
-
-       if (!access_ok(p32, sizeof(*p32)) ||
-           assign_in_user(&p32->pad, &p64->pad) ||
-           assign_in_user(&p32->start_block, &p64->start_block) ||
-           assign_in_user(&p32->blocks, &p64->blocks) ||
-           get_user(edid, &p64->edid) ||
-           put_user(ptr_to_compat((void __user *)edid), &p32->edid) ||
-           copy_in_user(p32->reserved, p64->reserved, sizeof(p32->reserved)))
+       if (copy_to_user(p32, p64, offsetof(struct v4l2_edid32, edid)))
                return -EFAULT;
        return 0;
 }
@@ -994,6 +976,10 @@ unsigned int v4l2_compat_translate_cmd(unsigned int cmd)
                return VIDIOC_S_FMT;
        case VIDIOC_TRY_FMT32:
                return VIDIOC_TRY_FMT;
+       case VIDIOC_G_FBUF32:
+               return VIDIOC_G_FBUF;
+       case VIDIOC_S_FBUF32:
+               return VIDIOC_S_FBUF;
 #ifdef CONFIG_COMPAT_32BIT_TIME
        case VIDIOC_QUERYBUF32_TIME32:
                return VIDIOC_QUERYBUF;
@@ -1020,6 +1006,22 @@ unsigned int v4l2_compat_translate_cmd(unsigned int cmd)
                return VIDIOC_TRY_EXT_CTRLS;
        case VIDIOC_PREPARE_BUF32:
                return VIDIOC_PREPARE_BUF;
+       case VIDIOC_ENUMSTD32:
+               return VIDIOC_ENUMSTD;
+       case VIDIOC_ENUMINPUT32:
+               return VIDIOC_ENUMINPUT;
+       case VIDIOC_G_EDID32:
+               return VIDIOC_G_EDID;
+       case VIDIOC_S_EDID32:
+               return VIDIOC_S_EDID;
+#ifdef CONFIG_X86_64
+       case VIDIOC_DQEVENT32:
+               return VIDIOC_DQEVENT;
+#ifdef CONFIG_COMPAT_32BIT_TIME
+       case VIDIOC_DQEVENT32_TIME32:
+               return VIDIOC_DQEVENT;
+#endif
+#endif
        }
        return cmd;
 }
@@ -1031,6 +1033,9 @@ int v4l2_compat_get_user(void __user *arg, void *parg, unsigned int cmd)
        case VIDIOC_S_FMT32:
        case VIDIOC_TRY_FMT32:
                return get_v4l2_format32(parg, arg);
+
+       case VIDIOC_S_FBUF32:
+               return get_v4l2_framebuffer32(parg, arg);
 #ifdef CONFIG_COMPAT_32BIT_TIME
        case VIDIOC_QUERYBUF32_TIME32:
        case VIDIOC_QBUF32_TIME32:
@@ -1051,6 +1056,16 @@ int v4l2_compat_get_user(void __user *arg, void *parg, unsigned int cmd)
 
        case VIDIOC_CREATE_BUFS32:
                return get_v4l2_create32(parg, arg);
+
+       case VIDIOC_ENUMSTD32:
+               return get_v4l2_standard32(parg, arg);
+
+       case VIDIOC_ENUMINPUT32:
+               return get_v4l2_input32(parg, arg);
+
+       case VIDIOC_G_EDID32:
+       case VIDIOC_S_EDID32:
+               return get_v4l2_edid32(parg, arg);
        }
        return 0;
 }
@@ -1062,6 +1077,9 @@ int v4l2_compat_put_user(void __user *arg, void *parg, unsigned int cmd)
        case VIDIOC_S_FMT32:
        case VIDIOC_TRY_FMT32:
                return put_v4l2_format32(parg, arg);
+
+       case VIDIOC_G_FBUF32:
+               return put_v4l2_framebuffer32(parg, arg);
 #ifdef CONFIG_COMPAT_32BIT_TIME
        case VIDIOC_QUERYBUF32_TIME32:
        case VIDIOC_QBUF32_TIME32:
@@ -1082,6 +1100,24 @@ int v4l2_compat_put_user(void __user *arg, void *parg, unsigned int cmd)
 
        case VIDIOC_CREATE_BUFS32:
                return put_v4l2_create32(parg, arg);
+
+       case VIDIOC_ENUMSTD32:
+               return put_v4l2_standard32(parg, arg);
+
+       case VIDIOC_ENUMINPUT32:
+               return put_v4l2_input32(parg, arg);
+
+       case VIDIOC_G_EDID32:
+       case VIDIOC_S_EDID32:
+               return put_v4l2_edid32(parg, arg);
+#ifdef CONFIG_X86_64
+       case VIDIOC_DQEVENT32:
+               return put_v4l2_event32(parg, arg);
+#ifdef CONFIG_COMPAT_32BIT_TIME
+       case VIDIOC_DQEVENT32_TIME32:
+               return put_v4l2_event32_time32(parg, arg);
+#endif
+#endif
        }
        return 0;
 }
@@ -1327,16 +1363,6 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
         * 1. When struct size is different, converts the command.
         */
        switch (cmd) {
-       case VIDIOC_G_FBUF32: ncmd = VIDIOC_G_FBUF; break;
-       case VIDIOC_S_FBUF32: ncmd = VIDIOC_S_FBUF; break;
-       case VIDIOC_ENUMSTD32: ncmd = VIDIOC_ENUMSTD; break;
-       case VIDIOC_ENUMINPUT32: ncmd = VIDIOC_ENUMINPUT; break;
-#ifdef CONFIG_X86_64
-       case VIDIOC_DQEVENT32: ncmd = VIDIOC_DQEVENT; break;
-       case VIDIOC_DQEVENT32_TIME32: ncmd = VIDIOC_DQEVENT_TIME32; break;
-#endif
-       case VIDIOC_G_EDID32: ncmd = VIDIOC_G_EDID; break;
-       case VIDIOC_S_EDID32: ncmd = VIDIOC_S_EDID; break;
        default: ncmd = cmd; break;
        }
 
@@ -1346,53 +1372,6 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
         * argument into it.
         */
        switch (cmd) {
-       case VIDIOC_G_EDID32:
-       case VIDIOC_S_EDID32:
-               err = alloc_userspace(sizeof(struct v4l2_edid), 0, &new_p64);
-               if (!err)
-                       err = get_v4l2_edid32(new_p64, p32);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_S_FBUF32:
-               err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0,
-                                     &new_p64);
-               if (!err)
-                       err = get_v4l2_framebuffer32(new_p64, p32);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_G_FBUF32:
-               err = alloc_userspace(sizeof(struct v4l2_framebuffer), 0,
-                                     &new_p64);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_ENUMSTD32:
-               err = alloc_userspace(sizeof(struct v4l2_standard), 0,
-                                     &new_p64);
-               if (!err)
-                       err = get_v4l2_standard32(new_p64, p32);
-               compatible_arg = 0;
-               break;
-
-       case VIDIOC_ENUMINPUT32:
-               err = alloc_userspace(sizeof(struct v4l2_input), 0, &new_p64);
-               if (!err)
-                       err = get_v4l2_input32(new_p64, p32);
-               compatible_arg = 0;
-               break;
-
-#ifdef CONFIG_X86_64
-       case VIDIOC_DQEVENT32:
-               err = alloc_userspace(sizeof(struct v4l2_event), 0, &new_p64);
-               compatible_arg = 0;
-               break;
-       case VIDIOC_DQEVENT32_TIME32:
-               err = alloc_userspace(sizeof(struct v4l2_event_time32), 0, &new_p64);
-               compatible_arg = 0;
-               break;
-#endif
        }
        if (err)
                return err;
@@ -1415,54 +1394,10 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
                return err;
 
        /*
-        * 4. Special case: even after an error we need to put the
-        * results back for some ioctls.
-        *
-        * In the case of EXT_CTRLS, the error_idx will contain information
-        * on which control failed.
-        *
-        * In the case of S_EDID, the driver can return E2BIG and set
-        * the blocks to maximum allowed value.
-        */
-       switch (cmd) {
-       case VIDIOC_S_EDID32:
-               if (put_v4l2_edid32(new_p64, p32))
-                       err = -EFAULT;
-               break;
-       }
-       if (err)
-               return err;
-
-       /*
         * 5. Copy the data returned at the 64 bits userspace pointer to
         * the original 32 bits structure.
         */
        switch (cmd) {
-       case VIDIOC_G_FBUF32:
-               err = put_v4l2_framebuffer32(new_p64, p32);
-               break;
-
-#ifdef CONFIG_X86_64
-       case VIDIOC_DQEVENT32:
-               err = put_v4l2_event32(new_p64, p32);
-               break;
-
-       case VIDIOC_DQEVENT32_TIME32:
-               err = put_v4l2_event32_time32(new_p64, p32);
-               break;
-#endif
-
-       case VIDIOC_G_EDID32:
-               err = put_v4l2_edid32(new_p64, p32);
-               break;
-
-       case VIDIOC_ENUMSTD32:
-               err = put_v4l2_standard32(new_p64, p32);
-               break;
-
-       case VIDIOC_ENUMINPUT32:
-               err = put_v4l2_input32(new_p64, p32);
-               break;
        }
        return err;
 }