Hi Arnd,

On 17/09/2020 17:28, Arnd Bergmann wrote:
> I have a series to remove all uses of compat_alloc_user_space()
> and copy_in_user() from the kernel, this is the part of it that
> involves the v4l2 compat code.
> 
> The resulting code is significantly shorter and arguably more
> readable, but I have not done any testing beyond compilation on
> it, so at the minimum this first needs to pass the test suite
> for both native and compat users space.
> 
> Given the complexity of the code, I am fairly sure that there
> are bugs I missed.
> 
> Please review and test if possible.

While testing I found a lot of bugs. Below is a patch that sits on top
of your series and fixes all the bugs:

- put_v4l2_standard32: typo: p64->index -> p64->id
- get_v4l2_plane32: typo: p64 -> plane32
                    typo: duplicate bytesused: the 2nd should be 'length'
- put_v4l2_plane32: typo: duplicate bytesused: the 2nd should be 'length'
- get_v4l2_buffer32/get_v4l2_buffer32_time32: missing compat_ptr for 
vb32.m.userptr
- get_v4l2_buffer32/get_v4l2_buffer32_time32: drop querybuf bool
- put_v4l2_buffer32/put_v4l2_buffer32_time32: add uintptr_t cast for 
vb->m.userptr
- get_v4l2_ext_controls32: typo: error_idx -> request_fd
- put_v4l2_ext_controls32: typo: error_idx -> request_fd
- v4l2_compat_translate_cmd: missing case VIDIOC_CREATE_BUFS32
- v4l2_compat_translate_cmd: #ifdef CONFIG_COMPAT_32BIT_TIME for
  case VIDIOC_DQEVENT32_TIME32 and return VIDIOC_DQEVENT
- v4l2_compat_put_user: #ifdef CONFIG_COMPAT_32BIT_TIME for case 
VIDIOC_DQEVENT32_TIME32
- v4l2_compat_get_array_args: typo: p64 -> b64
- v4l2_compat_put_array_args: typo: p64 -> b64
- video_get_user: make sure INFO_FL_CLEAR_MASK is honored, also when 
in_compat_syscall()
- video_usercopy: err = v4l2_compat_put_array_args overwrote the original ioctl 
err value.
  Handle this correctly.

I've tested this as well with the y2038 compat mode (i686 with 64-bit time_t)
and that too works fine.

Regards,

        Hans

Signed-off-by: Hans Verkuil <[email protected]>
---
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c 
b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index d20c36ab01b4..e8c823776de0 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -258,7 +258,7 @@ static int put_v4l2_standard32(struct v4l2_standard *p64,
                               struct v4l2_standard32 __user *p32)
 {
        if (put_user(p64->index, &p32->index) ||
-           put_user(p64->index, &p32->id) ||
+           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)) ||
@@ -358,10 +358,10 @@ static int get_v4l2_plane32(struct v4l2_plane *p64,

        memset(p64, 0, sizeof(*p64));
        *p64 = (struct v4l2_plane) {
-               .bytesused      = p64->bytesused,
-               .length         = p64->bytesused,
+               .bytesused      = plane32.bytesused,
+               .length         = plane32.length,
                .m              = m,
-               .data_offset    = p64->data_offset,
+               .data_offset    = plane32.data_offset,
        };

        return 0;
@@ -376,7 +376,7 @@ static int put_v4l2_plane32(struct v4l2_plane *p64,
        memset(&plane32, 0, sizeof(plane32));
        plane32 = (struct v4l2_plane32) {
                .bytesused      = p64->bytesused,
-               .length         = p64->bytesused,
+               .length         = p64->length,
                .data_offset    = p64->data_offset,
        };

@@ -400,8 +400,7 @@ static int put_v4l2_plane32(struct v4l2_plane *p64,
 }

 static int get_v4l2_buffer32(struct v4l2_buffer *vb,
-                            struct v4l2_buffer32 __user *arg,
-                            bool querybuf)
+                            struct v4l2_buffer32 __user *arg)
 {
        struct v4l2_buffer32 vb32;

@@ -431,7 +430,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *vb,
                vb->m.offset = vb32.m.offset;
                break;
        case V4L2_MEMORY_USERPTR:
-               vb->m.userptr = vb32.m.userptr;
+               vb->m.userptr = (unsigned long)compat_ptr(vb32.m.userptr);
                break;
        case V4L2_MEMORY_DMABUF:
                vb->m.fd = vb32.m.fd;
@@ -442,16 +441,12 @@ static int get_v4l2_buffer32(struct v4l2_buffer *vb,
                vb->m.planes = (void __force *)
                                compat_ptr(vb32.m.planes);

-       if (querybuf)
-               vb->request_fd = 0;
-
        return 0;
 }

 #ifdef CONFIG_COMPAT_32BIT_TIME
 static int get_v4l2_buffer32_time32(struct v4l2_buffer *vb,
-                                   struct v4l2_buffer32_time32 __user *arg,
-                                   bool querybuf)
+                                   struct v4l2_buffer32_time32 __user *arg)
 {
        struct v4l2_buffer32_time32 vb32;

@@ -479,7 +474,7 @@ static int get_v4l2_buffer32_time32(struct v4l2_buffer *vb,
                vb->m.offset = vb32.m.offset;
                break;
        case V4L2_MEMORY_USERPTR:
-               vb->m.userptr = vb32.m.userptr;
+               vb->m.userptr = (unsigned long)compat_ptr(vb32.m.userptr);
                break;
        case V4L2_MEMORY_DMABUF:
                vb->m.fd = vb32.m.fd;
@@ -489,8 +484,6 @@ static int get_v4l2_buffer32_time32(struct v4l2_buffer *vb,
        if (V4L2_TYPE_IS_MULTIPLANAR(vb->type))
                vb->m.planes = (void __force *)
                                compat_ptr(vb32.m.planes);
-       if (querybuf)
-               vb->request_fd = 0;

        return 0;
 }
@@ -524,7 +517,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *vb,
                vb32.m.offset = vb->m.offset;
                break;
        case V4L2_MEMORY_USERPTR:
-               vb32.m.userptr = vb->m.userptr;
+               vb32.m.userptr = (uintptr_t)(vb->m.userptr);
                break;
        case V4L2_MEMORY_DMABUF:
                vb32.m.fd = vb->m.fd;
@@ -568,7 +561,7 @@ static int put_v4l2_buffer32_time32(struct v4l2_buffer *vb,
                vb32.m.offset = vb->m.offset;
                break;
        case V4L2_MEMORY_USERPTR:
-               vb32.m.userptr = vb->m.userptr;
+               vb32.m.userptr = (uintptr_t)(vb->m.userptr);
                break;
        case V4L2_MEMORY_DMABUF:
                vb32.m.fd = vb->m.fd;
@@ -722,7 +715,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls 
*p64,
                .which          = ec32.which,
                .count          = ec32.count,
                .error_idx      = ec32.error_idx,
-               .request_fd     = ec32.error_idx,
+               .request_fd     = ec32.request_fd,
                .reserved[0]    = ec32.reserved[0],
                .controls       = (void __force *)compat_ptr(ec32.controls),
        };
@@ -740,7 +733,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls 
*p64,
                .which          = p64->which,
                .count          = p64->count,
                .error_idx      = p64->error_idx,
-               .request_fd     = p64->error_idx,
+               .request_fd     = p64->request_fd,
                .reserved[0]    = p64->reserved[0],
                .controls       = (uintptr_t)p64->controls,
        };
@@ -910,8 +903,10 @@ unsigned int v4l2_compat_translate_cmd(unsigned int cmd)
                return VIDIOC_QBUF;
        case VIDIOC_DQBUF32:
                return VIDIOC_DQBUF;
+       case VIDIOC_CREATE_BUFS32:
+               return VIDIOC_CREATE_BUFS;
        case VIDIOC_G_EXT_CTRLS32:
-               return  VIDIOC_G_EXT_CTRLS;
+               return VIDIOC_G_EXT_CTRLS;
        case VIDIOC_S_EXT_CTRLS32:
                return VIDIOC_S_EXT_CTRLS;
        case VIDIOC_TRY_EXT_CTRLS32:
@@ -929,8 +924,10 @@ unsigned int v4l2_compat_translate_cmd(unsigned int cmd)
 #ifdef CONFIG_X86_64
        case VIDIOC_DQEVENT32:
                return VIDIOC_DQEVENT;
+#ifdef CONFIG_COMPAT_32BIT_TIME
        case VIDIOC_DQEVENT32_TIME32:
-               return VIDIOC_DQEVENT_TIME32;
+               return VIDIOC_DQEVENT;
+#endif
 #endif
        }
        return cmd;
@@ -951,15 +948,13 @@ int v4l2_compat_get_user(void __user *arg, void *parg, 
unsigned int cmd)
        case VIDIOC_QBUF32_TIME32:
        case VIDIOC_DQBUF32_TIME32:
        case VIDIOC_PREPARE_BUF32_TIME32:
-               return get_v4l2_buffer32_time32(parg, arg,
-                                       cmd == VIDIOC_QUERYBUF32_TIME32);
+               return get_v4l2_buffer32_time32(parg, arg);
 #endif
        case VIDIOC_QUERYBUF32:
        case VIDIOC_QBUF32:
        case VIDIOC_DQBUF32:
        case VIDIOC_PREPARE_BUF32:
-               return get_v4l2_buffer32(parg, arg,
-                                       cmd == VIDIOC_QUERYBUF32);
+               return get_v4l2_buffer32(parg, arg);

        case VIDIOC_G_EXT_CTRLS32:
        case VIDIOC_S_EXT_CTRLS32:
@@ -1025,8 +1020,10 @@ int v4l2_compat_put_user(void __user *arg, void *parg, 
unsigned int cmd)
 #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;
@@ -1073,9 +1070,10 @@ int v4l2_compat_get_array_args(struct file *file, void 
*mbuf,
                struct v4l2_buffer *b64 = arg;
                struct v4l2_plane *p64 = mbuf;
                struct v4l2_plane32 __user *p32 = user_ptr;
-               u32 num_planes = p64->length;

                if (V4L2_TYPE_IS_MULTIPLANAR(b64->type)) {
+                       u32 num_planes = b64->length;
+
                        if (num_planes == 0)
                                return 0;

@@ -1161,9 +1159,10 @@ int v4l2_compat_put_array_args(struct file *file, void 
__user *user_ptr,
                struct v4l2_buffer *b64 = arg;
                struct v4l2_plane *p64 = mbuf;
                struct v4l2_plane32 __user *p32 = user_ptr;
-               u32 num_planes = p64->length;

                if (V4L2_TYPE_IS_MULTIPLANAR(b64->type)) {
+                       u32 num_planes = b64->length;
+
                        if (num_planes == 0)
                                return 0;

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c 
b/drivers/media/v4l2-core/v4l2-ioctl.c
index 114eaa79b6f5..aacb13b8f0a9 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -3133,7 +3133,8 @@ static int video_get_user(void __user *arg, void *parg,
                          unsigned int real_cmd, unsigned int cmd,
                          bool *always_copy)
 {
-       unsigned int n = _IOC_SIZE(cmd);
+       unsigned int n = _IOC_SIZE(real_cmd);
+       int err = 0;

        if (!(_IOC_DIR(cmd) & _IOC_WRITE)) {
                /* read-only ioctl */
@@ -3141,70 +3142,64 @@ static int video_get_user(void __user *arg, void *parg,
                return 0;
        }

-       if (cmd == real_cmd) {
-               /*
-                * 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;
+       /*
+        * 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(real_cmd)) {
+               u32 flags = v4l2_ioctls[_IOC_NR(real_cmd)].flags;

-               /* zero out anything we don't copy from userspace */
-               if (n < _IOC_SIZE(cmd))
-                       memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
-               return 0;
+               if (flags & INFO_FL_CLEAR_MASK)
+                       n = (flags & INFO_FL_CLEAR_MASK) >> 16;
+               *always_copy = flags & INFO_FL_ALWAYS_COPY;
        }

-       if (in_compat_syscall())
-               return v4l2_compat_get_user(arg, parg, cmd);
-
-       switch (cmd) {
+       if (cmd == real_cmd) {
+               if (copy_from_user(parg, (void __user *)arg, n))
+                       err = -EFAULT;
+       } else if (in_compat_syscall()) {
+               err = v4l2_compat_get_user(arg, parg, cmd);
+       } else {
+               switch (cmd) {
 #ifdef CONFIG_COMPAT_32BIT_TIME
-       case VIDIOC_QUERYBUF_TIME32:
-       case VIDIOC_QBUF_TIME32:
-       case VIDIOC_DQBUF_TIME32:
-       case VIDIOC_PREPARE_BUF_TIME32: {
-               struct v4l2_buffer_time32 vb32;
-               struct v4l2_buffer *vb = parg;
-
-               if (copy_from_user(&vb32, arg, sizeof(vb32)))
-                       return -EFAULT;
-
-               *vb = (struct v4l2_buffer) {
-                       .index          = vb32.index,
-                       .type           = vb32.type,
-                       .bytesused      = vb32.bytesused,
-                       .flags          = vb32.flags,
-                       .field          = vb32.field,
-                       .timestamp.tv_sec       = vb32.timestamp.tv_sec,
-                       .timestamp.tv_usec      = vb32.timestamp.tv_usec,
-                       .timecode       = vb32.timecode,
-                       .sequence       = vb32.sequence,
-                       .memory         = vb32.memory,
-                       .m.userptr      = vb32.m.userptr,
-                       .length         = vb32.length,
-                       .request_fd     = vb32.request_fd,
-               };
-
-               if (cmd == VIDIOC_QUERYBUF_TIME32)
-                       vb->request_fd = 0;
-
-               break;
-       }
+               case VIDIOC_QUERYBUF_TIME32:
+               case VIDIOC_QBUF_TIME32:
+               case VIDIOC_DQBUF_TIME32:
+               case VIDIOC_PREPARE_BUF_TIME32: {
+                       struct v4l2_buffer_time32 vb32;
+                       struct v4l2_buffer *vb = parg;
+
+                       if (copy_from_user(&vb32, arg, sizeof(vb32)))
+                               return -EFAULT;
+
+                       *vb = (struct v4l2_buffer) {
+                               .index          = vb32.index,
+                                       .type           = vb32.type,
+                                       .bytesused      = vb32.bytesused,
+                                       .flags          = vb32.flags,
+                                       .field          = vb32.field,
+                                       .timestamp.tv_sec       = 
vb32.timestamp.tv_sec,
+                                       .timestamp.tv_usec      = 
vb32.timestamp.tv_usec,
+                                       .timecode       = vb32.timecode,
+                                       .sequence       = vb32.sequence,
+                                       .memory         = vb32.memory,
+                                       .m.userptr      = vb32.m.userptr,
+                                       .length         = vb32.length,
+                                       .request_fd     = vb32.request_fd,
+                       };
+                       break;
+               }
 #endif
+               }
        }
-       return 0;
+
+       /* zero out anything we don't copy from userspace */
+       if (!err && n < _IOC_SIZE(real_cmd))
+               memset((u8 *)parg + n, 0, _IOC_SIZE(real_cmd) - n);
+       return err;
 }

 static int video_put_user(void __user *arg, void *parg,
@@ -3357,12 +3352,17 @@ video_usercopy(struct file *file, unsigned int 
orig_cmd, unsigned long arg,

        if (has_array_args) {
                *kernel_ptr = (void __force *)user_ptr;
-               if (in_compat_syscall())
-                       err = v4l2_compat_put_array_args(file, user_ptr, mbuf,
-                                                        array_size, orig_cmd,
-                                                        parg);
-               else if (copy_to_user(user_ptr, mbuf, array_size))
+               if (in_compat_syscall()) {
+                       int put_err;
+
+                       put_err = v4l2_compat_put_array_args(file, user_ptr, 
mbuf,
+                                                            array_size, 
orig_cmd,
+                                                            parg);
+                       if (put_err)
+                               err = put_err;
+               } else if (copy_to_user(user_ptr, mbuf, array_size)) {
                        err = -EFAULT;
+               }
                goto out_array_args;
        }
        /*

Reply via email to