diff options
Diffstat (limited to 'drivers/media/video/v4l2-compat-ioctl32.c')
-rw-r--r-- | drivers/media/video/v4l2-compat-ioctl32.c | 67 |
1 files changed, 48 insertions, 19 deletions
diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 0056b115b42..997975d5e02 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -600,9 +600,37 @@ struct v4l2_ext_controls32 { compat_caddr_t controls; /* actually struct v4l2_ext_control32 * */ }; +struct v4l2_ext_control32 { + __u32 id; + __u32 size; + __u32 reserved2[1]; + union { + __s32 value; + __s64 value64; + compat_caddr_t string; /* actually char * */ + }; +} __attribute__ ((packed)); + +/* The following function really belong in v4l2-common, but that causes + a circular dependency between modules. We need to think about this, but + for now this will do. */ + +/* Return non-zero if this control is a pointer type. Currently only + type STRING is a pointer type. */ +static inline int ctrl_is_pointer(u32 id) +{ + switch (id) { + case V4L2_CID_RDS_TX_PS_NAME: + case V4L2_CID_RDS_TX_RADIO_TEXT: + return 1; + default: + return 0; + } +} + static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control32 __user *ucontrols; struct v4l2_ext_control __user *kcontrols; int n; compat_caddr_t p; @@ -626,15 +654,17 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control)); kp->controls = kcontrols; while (--n >= 0) { - if (copy_in_user(&kcontrols->id, &ucontrols->id, sizeof(__u32))) - return -EFAULT; - if (copy_in_user(&kcontrols->reserved2, &ucontrols->reserved2, sizeof(ucontrols->reserved2))) - return -EFAULT; - /* Note: if the void * part of the union ever becomes relevant - then we need to know the type of the control in order to do - the right thing here. Luckily, that is not yet an issue. */ - if (copy_in_user(&kcontrols->value, &ucontrols->value, sizeof(ucontrols->value))) + if (copy_in_user(kcontrols, ucontrols, sizeof(*kcontrols))) return -EFAULT; + if (ctrl_is_pointer(kcontrols->id)) { + void __user *s; + + if (get_user(p, &ucontrols->string)) + return -EFAULT; + s = compat_ptr(p); + if (put_user(s, &kcontrols->string)) + return -EFAULT; + } ucontrols++; kcontrols++; } @@ -643,7 +673,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up) { - struct v4l2_ext_control __user *ucontrols; + struct v4l2_ext_control32 __user *ucontrols; struct v4l2_ext_control __user *kcontrols = kp->controls; int n = kp->count; compat_caddr_t p; @@ -664,15 +694,14 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext return -EFAULT; while (--n >= 0) { - if (copy_in_user(&ucontrols->id, &kcontrols->id, sizeof(__u32))) - return -EFAULT; - if (copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2, - sizeof(ucontrols->reserved2))) - return -EFAULT; - /* Note: if the void * part of the union ever becomes relevant - then we need to know the type of the control in order to do - the right thing here. Luckily, that is not yet an issue. */ - if (copy_in_user(&ucontrols->value, &kcontrols->value, sizeof(ucontrols->value))) + unsigned size = sizeof(*ucontrols); + + /* Do not modify the pointer when copying a pointer control. + The contents of the pointer was changed, not the pointer + itself. */ + if (ctrl_is_pointer(kcontrols->id)) + size -= sizeof(ucontrols->value64); + if (copy_in_user(ucontrols, kcontrols, size)) return -EFAULT; ucontrols++; kcontrols++; |