aboutsummaryrefslogtreecommitdiff
path: root/drivers/media
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/v4l2-core/v4l2-compat-ioctl32.c597
1 files changed, 373 insertions, 224 deletions
diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index d3a60c31c..78b670e71 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -20,6 +20,13 @@
#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
+#define convert_in_user(srcptr, dstptr) \
+({ \
+ typeof(*srcptr) val; \
+ \
+ get_user(val, srcptr) || put_user(val, dstptr); \
+})
+
static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret = -ENOIOCTLCMD;
@@ -45,27 +52,45 @@ struct v4l2_window32 {
compat_caddr_t bitmap;
};
-static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+static int bufsize_v4l2_window32(struct v4l2_window32 __user *up)
+{
+ __u32 clipcount;
+
+ if (get_user(clipcount, &up->clipcount))
+ return -EFAULT;
+ if (clipcount > 2048)
+ return -EINVAL;
+ return clipcount * sizeof(struct v4l2_clip);
+}
+
+static int get_v4l2_window32(struct v4l2_window __user *kp, struct
+ v4l2_window32 __user *up, void __user *aux_buf, int aux_space)
{
+ __u32 clipcount;
+
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
- copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
- get_user(kp->field, &up->field) ||
- get_user(kp->chromakey, &up->chromakey) ||
- get_user(kp->clipcount, &up->clipcount))
+ copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
+ convert_in_user(&up->field, &kp->field) ||
+ convert_in_user(&up->chromakey, &kp->chromakey) ||
+ get_user(clipcount, &up->clipcount) ||
+ put_user(clipcount, &kp->clipcount))
return -EFAULT;
- if (kp->clipcount > 2048)
+ if (clipcount > 2048)
return -EINVAL;
- if (kp->clipcount) {
+ if (clipcount) {
struct v4l2_clip32 __user *uclips;
struct v4l2_clip __user *kclips;
- int n = kp->clipcount;
+ int n = clipcount;
compat_caddr_t p;
if (get_user(p, &up->clips))
return -EFAULT;
uclips = compat_ptr(p);
- kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
- kp->clips = kclips;
+ if (aux_space < n * sizeof(struct v4l2_clip))
+ return -EFAULT;
+ kclips = aux_buf;
+ if (put_user(kclips, &kp->clips))
+ return -EFAULT;
while (--n >= 0) {
if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
return -EFAULT;
@@ -74,75 +99,77 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
uclips += 1;
kclips += 1;
}
- } else
- kp->clips = NULL;
+ } else {
+ if (put_user(NULL, &kp->clips))
+ return -EFAULT;
+ }
return 0;
}
-static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+static int put_v4l2_window32(struct v4l2_window __user *kp, struct v4l2_window32 __user *up)
{
- if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) ||
- put_user(kp->field, &up->field) ||
- put_user(kp->chromakey, &up->chromakey) ||
- put_user(kp->clipcount, &up->clipcount))
- return -EFAULT;
+ if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
+ convert_in_user(&kp->field, &up->field) ||
+ convert_in_user(&kp->chromakey, &up->chromakey) ||
+ convert_in_user(&kp->clipcount, &up->clipcount))
+ return -EFAULT;
return 0;
}
-static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+static inline int get_v4l2_pix_format(struct v4l2_pix_format __user *kp, struct v4l2_pix_format __user *up)
{
- if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format)))
+ if (copy_in_user(kp, up, sizeof(struct v4l2_pix_format)))
return -EFAULT;
return 0;
}
-static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
+static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane __user *kp,
struct v4l2_pix_format_mplane __user *up)
{
- if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
+ if (copy_in_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
return -EFAULT;
return 0;
}
-static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
+static inline int put_v4l2_pix_format(struct v4l2_pix_format __user *kp, struct v4l2_pix_format __user *up)
{
- if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
+ if (copy_in_user(up, kp, sizeof(struct v4l2_pix_format)))
return -EFAULT;
return 0;
}
-static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
+static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane __user *kp,
struct v4l2_pix_format_mplane __user *up)
{
- if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
+ if (copy_in_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
return -EFAULT;
return 0;
}
-static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+static inline int get_v4l2_vbi_format(struct v4l2_vbi_format __user *kp, struct v4l2_vbi_format __user *up)
{
- if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
+ if (copy_in_user(kp, up, sizeof(struct v4l2_vbi_format)))
return -EFAULT;
return 0;
}
-static inline int put_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
+static inline int put_v4l2_vbi_format(struct v4l2_vbi_format __user *kp, struct v4l2_vbi_format __user *up)
{
- if (copy_to_user(up, kp, sizeof(struct v4l2_vbi_format)))
+ if (copy_in_user(up, kp, sizeof(struct v4l2_vbi_format)))
return -EFAULT;
return 0;
}
-static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+static inline int get_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format __user *kp, struct v4l2_sliced_vbi_format __user *up)
{
- if (copy_from_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
+ if (copy_in_user(kp, up, sizeof(struct v4l2_sliced_vbi_format)))
return -EFAULT;
return 0;
}
-static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, struct v4l2_sliced_vbi_format __user *up)
+static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format __user *kp, struct v4l2_sliced_vbi_format __user *up)
{
- if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
+ if (copy_in_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
return -EFAULT;
return 0;
}
@@ -176,12 +203,31 @@ struct v4l2_create_buffers32 {
__u32 reserved[8];
};
-static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int __bufsize_v4l2_format32(struct v4l2_format32 __user *up)
{
- if (get_user(kp->type, &up->type))
+ __u32 type;
+
+ if (get_user(type, &up->type))
return -EFAULT;
- switch (kp->type) {
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ return bufsize_v4l2_window32(&up->fmt.win);
+ default:
+ return 0;
+ }
+}
+
+static int __get_v4l2_format32(struct v4l2_format __user *kp, struct
+ v4l2_format32 __user *up, void __user *aux_buf, int aux_space)
+{
+ __u32 type;
+
+ if (get_user(type, &up->type) || put_user(type, &kp->type))
+ return -EFAULT;
+
+ switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
@@ -191,7 +237,7 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
&up->fmt.pix_mp);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
+ return get_v4l2_window32(&kp->fmt.win, &up->fmt.win, aux_buf, aux_space);
case V4L2_BUF_TYPE_VBI_CAPTURE:
case V4L2_BUF_TYPE_VBI_OUTPUT:
return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
@@ -205,24 +251,46 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
}
}
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int bufsize_v4l2_format32(struct v4l2_format32 __user *up)
{
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)))
return -EFAULT;
- return __get_v4l2_format32(kp, up);
+ return __bufsize_v4l2_format32(up);
}
-static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+static int get_v4l2_format32(struct v4l2_format __user *kp, struct
+ v4l2_format32 __user *up, void __user *aux_buf, int aux_space)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)))
+ return -EFAULT;
+ return __get_v4l2_format32(kp, up, aux_buf, aux_space);
+}
+
+static int bufsize_v4l2_create32(struct v4l2_create_buffers32 __user *up)
+{
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)))
+ return -EFAULT;
+ return __bufsize_v4l2_format32(&up->format);
+}
+
+static int get_v4l2_create32(struct v4l2_create_buffers __user *kp, struct
+ v4l2_create_buffers32 __user *up, void __user *aux_buf,
+ int aux_space)
{
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
- copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format)))
+ copy_in_user(kp, up, offsetof(struct v4l2_create_buffers32, format)))
return -EFAULT;
- return __get_v4l2_format32(&kp->format, &up->format);
+ return __get_v4l2_format32(&kp->format, &up->format, aux_buf, aux_space);
}
-static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int __put_v4l2_format32(struct v4l2_format __user *kp, struct v4l2_format32 __user *up)
{
- switch (kp->type) {
+ __u32 type;
+
+ if (get_user(type, &kp->type))
+ return -EFAULT;
+
+ switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
@@ -246,7 +314,7 @@ static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
}
}
-static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up)
+static int put_v4l2_format32(struct v4l2_format __user *kp, struct v4l2_format32 __user *up)
{
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) ||
put_user(kp->type, &up->type))
@@ -254,10 +322,10 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
return __put_v4l2_format32(kp, up);
}
-static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+static int put_v4l2_create32(struct v4l2_create_buffers __user *kp, struct v4l2_create_buffers32 __user *up)
{
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
- copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
+ copy_in_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt)))
return -EFAULT;
return __put_v4l2_format32(&kp->format, &up->format);
}
@@ -271,24 +339,24 @@ struct v4l2_standard32 {
__u32 reserved[4];
};
-static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+static int get_v4l2_standard32(struct v4l2_standard __user *kp, struct v4l2_standard32 __user *up)
{
/* other fields are not set by the user, nor used by the driver */
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_standard32)) ||
- get_user(kp->index, &up->index))
+ convert_in_user(&up->index, &kp->index))
return -EFAULT;
return 0;
}
-static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
+static int put_v4l2_standard32(struct v4l2_standard __user *kp, struct v4l2_standard32 __user *up)
{
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
- put_user(kp->index, &up->index) ||
- put_user(kp->id, &up->id) ||
- copy_to_user(up->name, kp->name, 24) ||
- copy_to_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
- put_user(kp->framelines, &up->framelines) ||
- copy_to_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
+ convert_in_user(&kp->index, &up->index) ||
+ copy_in_user(up->id, &kp->id, sizeof(__u64)) ||
+ copy_in_user(up->name, kp->name, 24) ||
+ copy_in_user(&up->frameperiod, &kp->frameperiod, sizeof(kp->frameperiod)) ||
+ convert_in_user(&kp->framelines, &up->framelines) ||
+ copy_in_user(up->reserved, kp->reserved, 4 * sizeof(__u32)))
return -EFAULT;
return 0;
}
@@ -331,7 +399,6 @@ struct v4l2_buffer32 {
static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
enum v4l2_memory memory)
{
- void __user *up_pln;
compat_long_t p;
if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
@@ -340,10 +407,9 @@ static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
return -EFAULT;
if (memory == V4L2_MEMORY_USERPTR) {
- if (get_user(p, &up32->m.userptr))
- return -EFAULT;
- up_pln = compat_ptr(p);
- if (put_user((unsigned long)up_pln, &up->m.userptr))
+ if (get_user(p, &up32->m.userptr) ||
+ put_user((unsigned long) compat_ptr(p),
+ &up->m.userptr))
return -EFAULT;
} else if (memory == V4L2_MEMORY_DMABUF) {
if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(int)))
@@ -380,8 +446,34 @@ static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
return 0;
}
-static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+static int bufsize_v4l2_buffer32(struct v4l2_buffer32 __user *up)
+{
+ __u32 type;
+ __u32 length;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
+ get_user(type, &up->type) ||
+ get_user(length, &up->length))
+ return -EFAULT;
+
+ if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+ if (length > VIDEO_MAX_PLANES)
+ return -EINVAL;
+
+ /* We don't really care if userspace decides to kill itself
+ * by passing a very big length value
+ */
+ return length * sizeof(struct v4l2_plane);
+ }
+ return 0;
+}
+
+static int get_v4l2_buffer32(struct v4l2_buffer __user *kp, struct
+ v4l2_buffer32 __user *up, void __user *aux_buf, int aux_space)
{
+ __u32 type;
+ __u32 length;
+ enum v4l2_memory memory;
struct v4l2_plane32 __user *uplane32;
struct v4l2_plane __user *uplane;
compat_caddr_t p;
@@ -389,38 +481,40 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
int ret;
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
- get_user(kp->index, &up->index) ||
- get_user(kp->type, &up->type) ||
- get_user(kp->flags, &up->flags) ||
- get_user(kp->memory, &up->memory) ||
- get_user(kp->length, &up->length))
+ convert_in_user(&up->index, &kp->index) ||
+ get_user(type, &up->type) ||
+ put_user(type, &kp->type) ||
+ convert_in_user(&up->flags, &kp->flags) ||
+ get_user(memory, &up->memory) ||
+ put_user(memory, &kp->memory) ||
+ convert_in_user(&up->length, &kp->length) ||
+ get_user(length, &up->length) ||
+ put_user(length, &kp->length))
return -EFAULT;
- if (V4L2_TYPE_IS_OUTPUT(kp->type))
- if (get_user(kp->bytesused, &up->bytesused) ||
- get_user(kp->field, &up->field) ||
- get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
- get_user(kp->timestamp.tv_usec,
- &up->timestamp.tv_usec))
+ if (V4L2_TYPE_IS_OUTPUT(type))
+ if (convert_in_user(&up->bytesused, &kp->bytesused) ||
+ convert_in_user(&up->field, &kp->field) ||
+ convert_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
+ convert_in_user(&up->timestamp.tv_usec,
+ &kp->timestamp.tv_usec))
return -EFAULT;
- if (V4L2_TYPE_IS_PRIVATE(kp->type)) {
+ if (V4L2_TYPE_IS_PRIVATE(type)) {
compat_long_t tmp;
- if (get_user(kp->length, &up->length) ||
- get_user(tmp, &up->m.userptr))
+ if (get_user(tmp, &up->m.userptr) ||
+ put_user((unsigned long) compat_ptr(tmp),
+ &kp->m.userptr))
return -EFAULT;
-
- kp->m.userptr = (unsigned long)compat_ptr(tmp);
}
- if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
- num_planes = kp->length;
+ if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+ num_planes = length;
if (num_planes == 0) {
- kp->m.planes = NULL;
/* num_planes == 0 is legal, e.g. when userspace doesn't
* need planes array on DQBUF*/
- return 0;
+ return put_user(NULL, &kp->m.planes);
}
if (get_user(p, &up->m.planes))
@@ -433,39 +527,44 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
/* We don't really care if userspace decides to kill itself
* by passing a very big num_planes value */
- uplane = compat_alloc_user_space(num_planes *
- sizeof(struct v4l2_plane));
- kp->m.planes = uplane;
+ if (aux_space < num_planes * sizeof(struct v4l2_plane))
+ return -EFAULT;
+
+ uplane = aux_buf;
+ if (put_user((__force struct v4l2_plane *)uplane,
+ &kp->m.planes))
+ return -EFAULT;
while (--num_planes >= 0) {
- ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
+ ret = get_v4l2_plane32(uplane, uplane32, memory);
if (ret)
return ret;
++uplane;
++uplane32;
}
} else {
- switch (kp->memory) {
+ switch (memory) {
case V4L2_MEMORY_MMAP:
- if (get_user(kp->m.offset, &up->m.offset))
+ if (convert_in_user(&up->m.offset, &kp->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
{
- compat_long_t tmp;
+ compat_long_t tmp;
- if (get_user(tmp, &up->m.userptr))
- return -EFAULT;
-
- kp->m.userptr = (unsigned long)compat_ptr(tmp);
+ if (get_user(tmp, &up->m.userptr) ||
+ put_user((unsigned long)
+ compat_ptr(tmp),
+ &kp->m.userptr))
+ return -EFAULT;
}
break;
case V4L2_MEMORY_OVERLAY:
- if (get_user(kp->m.offset, &up->m.offset))
+ if (convert_in_user(&up->m.offset, &kp->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_DMABUF:
- if (get_user(kp->m.fd, &up->m.fd))
+ if (convert_in_user(&up->m.fd, &kp->m.fd))
return -EFAULT;
break;
}
@@ -474,8 +573,11 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
return 0;
}
-static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
+static int put_v4l2_buffer32(struct v4l2_buffer __user *kp, struct v4l2_buffer32 __user *up)
{
+ __u32 type;
+ __u32 length;
+ enum v4l2_memory memory;
struct v4l2_plane32 __user *uplane32;
struct v4l2_plane __user *uplane;
compat_caddr_t p;
@@ -483,62 +585,65 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
int ret;
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
- put_user(kp->index, &up->index) ||
- put_user(kp->type, &up->type) ||
- put_user(kp->flags, &up->flags) ||
- put_user(kp->memory, &up->memory))
+ convert_in_user(&kp->index, &up->index) ||
+ get_user(type, &kp->type) ||
+ put_user(type, &up->type) ||
+ convert_in_user(&kp->flags, &up->flags) ||
+ get_user(memory, &kp->memory) ||
+ put_user(memory, &up->memory))
return -EFAULT;
- if (put_user(kp->bytesused, &up->bytesused) ||
- put_user(kp->field, &up->field) ||
- put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
- put_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
- copy_to_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
- put_user(kp->sequence, &up->sequence) ||
- put_user(kp->reserved2, &up->reserved2) ||
- put_user(kp->reserved, &up->reserved) ||
- put_user(kp->length, &up->length))
+ if (convert_in_user(&kp->bytesused, &up->bytesused) ||
+ convert_in_user(&kp->field, &up->field) ||
+ convert_in_user(&kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+ convert_in_user(&kp->timestamp.tv_usec, &up->timestamp.tv_usec) ||
+ copy_in_user(&up->timecode, &kp->timecode, sizeof(struct v4l2_timecode)) ||
+ convert_in_user(&kp->sequence, &up->sequence) ||
+ convert_in_user(&kp->reserved2, &up->reserved2) ||
+ convert_in_user(&kp->reserved, &up->reserved) ||
+ get_user(length, &kp->length) ||
+ put_user(length, &up->length))
return -EFAULT;
- if (V4L2_TYPE_IS_PRIVATE(kp->type)) {
- if (put_user(kp->length, &up->length) ||
- put_user(kp->m.userptr, &up->m.userptr))
+ if (V4L2_TYPE_IS_PRIVATE(type)) {
+ if (convert_in_user(&kp->m.userptr, &up->m.userptr))
return -EFAULT;
}
- if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
- num_planes = kp->length;
+ if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+ num_planes = length;
if (num_planes == 0)
return 0;
- uplane = kp->m.planes;
+ if (get_user(uplane, ((__force struct v4l2_plane __user **)&kp->m.planes)))
+ return -EFAULT;
if (get_user(p, &up->m.planes))
return -EFAULT;
uplane32 = compat_ptr(p);
while (--num_planes >= 0) {
- ret = put_v4l2_plane32(uplane, uplane32, kp->memory);
+ ret = put_v4l2_plane32(uplane, uplane32, memory);
if (ret)
return ret;
++uplane;
++uplane32;
}
} else {
- switch (kp->memory) {
+ switch (memory) {
case V4L2_MEMORY_MMAP:
- if (put_user(kp->m.offset, &up->m.offset))
+ if (convert_in_user(&kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
- if (put_user(kp->m.userptr, &up->m.userptr))
+ if (convert_in_user(&kp->m.userptr, &up->m.userptr))
return -EFAULT;
break;
case V4L2_MEMORY_OVERLAY:
- if (put_user(kp->m.offset, &up->m.offset))
+ if (convert_in_user(&kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_DMABUF:
- if (put_user(kp->m.fd, &up->m.fd))
+ if (convert_in_user(&kp->m.fd, &up->m.fd))
return -EFAULT;
break;
}
@@ -554,30 +659,31 @@ struct v4l2_framebuffer32 {
struct v4l2_pix_format fmt;
};
-static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+static int get_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp, struct v4l2_framebuffer32 __user *up)
{
- u32 tmp;
+ compat_caddr_t tmp;
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
get_user(tmp, &up->base) ||
- get_user(kp->capability, &up->capability) ||
- get_user(kp->flags, &up->flags))
+ put_user((__force void *)compat_ptr(tmp), &kp->base) ||
+ convert_in_user(&up->capability, &kp->capability) ||
+ convert_in_user(&up->flags, &kp->flags) ||
+ get_v4l2_pix_format(&kp->fmt, &up->fmt))
return -EFAULT;
- kp->base = compat_ptr(tmp);
- get_v4l2_pix_format(&kp->fmt, &up->fmt);
return 0;
}
-static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up)
+static int put_v4l2_framebuffer32(struct v4l2_framebuffer __user *kp, struct v4l2_framebuffer32 __user *up)
{
- u32 tmp = (u32)((unsigned long)kp->base);
+ void *base;
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
- put_user(tmp, &up->base) ||
- put_user(kp->capability, &up->capability) ||
- put_user(kp->flags, &up->flags))
+ get_user(base, &kp->base) ||
+ put_user(ptr_to_compat(base), &up->base) ||
+ convert_in_user(&kp->capability, &up->capability) ||
+ convert_in_user(&kp->flags, &up->flags) ||
+ put_v4l2_pix_format(&kp->fmt, &up->fmt))
return -EFAULT;
- put_v4l2_pix_format(&kp->fmt, &up->fmt);
return 0;
}
@@ -594,16 +700,16 @@ 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 *kp, struct v4l2_input32 __user *up)
+static inline int get_v4l2_input32(struct v4l2_input __user *kp, struct v4l2_input32 __user *up)
{
- if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
+ if (copy_in_user(kp, up, sizeof(struct v4l2_input32)))
return -EFAULT;
return 0;
}
-static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
+static inline int put_v4l2_input32(struct v4l2_input __user *kp, struct v4l2_input32 __user *up)
{
- if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
+ if (copy_in_user(up, kp, sizeof(struct v4l2_input32)))
return -EFAULT;
return 0;
}
@@ -644,33 +750,50 @@ static inline int ctrl_is_pointer(u32 id)
}
}
-static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+static int bufsize_v4l2_ext_controls32(struct v4l2_ext_controls32 __user *up)
+{
+ __u32 count;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
+ get_user(count, &up->count))
+ return -EFAULT;
+ if (count > V4L2_CID_MAX_CTRLS)
+ return -EINVAL;
+ return count * sizeof(struct v4l2_ext_control);
+}
+
+static int get_v4l2_ext_controls32(struct v4l2_ext_controls __user *kp, struct
+ v4l2_ext_controls32 __user *up, void __user *aux_buf,
+ int aux_space)
{
struct v4l2_ext_control32 __user *ucontrols;
struct v4l2_ext_control __user *kcontrols;
- int n;
+ __u32 count;
+ unsigned int n;
compat_caddr_t p;
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
- get_user(kp->ctrl_class, &up->ctrl_class) ||
- get_user(kp->count, &up->count) ||
- get_user(kp->error_idx, &up->error_idx) ||
- copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ convert_in_user(&up->ctrl_class, &kp->ctrl_class) ||
+ get_user(count, &up->count) ||
+ put_user(count, &kp->count) ||
+ convert_in_user(&up->error_idx, &kp->error_idx) ||
+ copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
return -EFAULT;
- n = kp->count;
- if (n == 0) {
- kp->controls = NULL;
- return 0;
- }
+ if (count == 0)
+ return put_user(NULL, &kp->controls);
if (get_user(p, &up->controls))
return -EFAULT;
ucontrols = compat_ptr(p);
if (!access_ok(VERIFY_READ, ucontrols,
- n * sizeof(struct v4l2_ext_control32)))
+ count * sizeof(struct v4l2_ext_control32)))
+ return -EFAULT;
+ if (aux_space < count * sizeof(struct v4l2_ext_control))
return -EFAULT;
- kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
- kp->controls = kcontrols;
- while (--n >= 0) {
+ kcontrols = aux_buf;
+ if (put_user((__force struct v4l2_ext_control *)kcontrols,
+ &kp->controls))
+ return -EFAULT;
+ for (n = 0; n < count; n++) {
if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
return -EFAULT;
if (ctrl_is_pointer(kcontrols->id)) {
@@ -688,30 +811,33 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
return 0;
}
-static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+static int put_v4l2_ext_controls32(struct v4l2_ext_controls __user *kp, struct v4l2_ext_controls32 __user *up)
{
struct v4l2_ext_control32 __user *ucontrols;
- struct v4l2_ext_control __user *kcontrols = kp->controls;
- int n = kp->count;
+ struct v4l2_ext_control __user *kcontrols;
+ __u32 count;
+ unsigned int n;
compat_caddr_t p;
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
- put_user(kp->ctrl_class, &up->ctrl_class) ||
- put_user(kp->count, &up->count) ||
- put_user(kp->error_idx, &up->error_idx) ||
- copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+ get_user(kcontrols, &kp->controls) ||
+ convert_in_user(&kp->ctrl_class, &up->ctrl_class) ||
+ get_user(count, &kp->count) ||
+ put_user(count, &up->count) ||
+ convert_in_user(&kp->error_idx, &up->error_idx) ||
+ copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
return -EFAULT;
- if (!kp->count)
+ if (!count)
return 0;
if (get_user(p, &up->controls))
return -EFAULT;
ucontrols = compat_ptr(p);
if (!access_ok(VERIFY_WRITE, ucontrols,
- n * sizeof(struct v4l2_ext_control32)))
+ count * sizeof(struct v4l2_ext_control32)))
return -EFAULT;
- while (--n >= 0) {
+ for (n = 0; n < count; n++) {
unsigned size = sizeof(*ucontrols);
/* Do not modify the pointer when copying a pointer control.
@@ -740,16 +866,17 @@ struct v4l2_event32 {
__u32 reserved[8];
};
-static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
+static int put_v4l2_event32(struct v4l2_event __user *kp, struct v4l2_event32 __user *up)
{
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
- put_user(kp->type, &up->type) ||
- copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
- put_user(kp->pending, &up->pending) ||
- put_user(kp->sequence, &up->sequence) ||
- put_compat_timespec(&kp->timestamp, &up->timestamp) ||
- put_user(kp->id, &up->id) ||
- copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
+ convert_in_user(&kp->type, &up->type) ||
+ copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
+ convert_in_user(&kp->pending, &up->pending) ||
+ convert_in_user(&kp->sequence, &up->sequence) ||
+ convert_in_user(&kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+ convert_in_user(&kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
+ convert_in_user(&kp->id, &up->id) ||
+ copy_in_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
return -EFAULT;
return 0;
}
@@ -762,31 +889,32 @@ struct v4l2_subdev_edid32 {
compat_caddr_t edid;
};
-static int get_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subdev_edid32 __user *up)
+static int get_v4l2_subdev_edid32(struct v4l2_subdev_edid __user *kp, struct v4l2_subdev_edid32 __user *up)
{
- u32 tmp;
+ compat_uptr_t tmp;
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_subdev_edid32)) ||
- get_user(kp->pad, &up->pad) ||
- get_user(kp->start_block, &up->start_block) ||
- get_user(kp->blocks, &up->blocks) ||
+ convert_in_user(&up->pad, &kp->pad) ||
+ convert_in_user(&up->start_block, &kp->start_block) ||
+ convert_in_user(&up->blocks, &kp->blocks) ||
get_user(tmp, &up->edid) ||
- copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ put_user(compat_ptr(tmp), &kp->edid) ||
+ copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
return -EFAULT;
- kp->edid = compat_ptr(tmp);
return 0;
}
-static int put_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subdev_edid32 __user *up)
+static int put_v4l2_subdev_edid32(struct v4l2_subdev_edid __user *kp, struct v4l2_subdev_edid32 __user *up)
{
- u32 tmp = (u32)((unsigned long)kp->edid);
+ void *edid;
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_subdev_edid32)) ||
- put_user(kp->pad, &up->pad) ||
- put_user(kp->start_block, &up->start_block) ||
- put_user(kp->blocks, &up->blocks) ||
- put_user(tmp, &up->edid) ||
- copy_to_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+ convert_in_user(&kp->pad, &up->pad) ||
+ convert_in_user(&kp->start_block, &up->start_block) ||
+ convert_in_user(&kp->blocks, &up->blocks) ||
+ get_user(edid, &kp->edid) ||
+ put_user(ptr_to_compat(edid), &up->edid) ||
+ copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
return -EFAULT;
return 0;
}
@@ -819,22 +947,37 @@ static int put_v4l2_subdev_edid32(struct v4l2_subdev_edid *kp, struct v4l2_subde
#define VIDIOC_G_OUTPUT32 _IOR ('V', 46, s32)
#define VIDIOC_S_OUTPUT32 _IOWR('V', 47, s32)
+/*
+ * Note that these macros contain return statements to avoid the need for the
+ * "caller" to check return values.
+ */
+#define ALLOC_USER_SPACE(size) \
+({ \
+ void __user *up_native; \
+ up_native = compat_alloc_user_space(size); \
+ if (!up_native) \
+ return -ENOMEM; \
+ if (clear_user(up_native, size)) \
+ return -EFAULT; \
+ up_native; \
+})
+
+#define ALLOC_AND_GET(bufsizefunc, getfunc, structname) \
+ do { \
+ aux_space = bufsizefunc(up); \
+ if (aux_space < 0) \
+ return aux_space; \
+ up_native = ALLOC_USER_SPACE(sizeof(struct structname) + aux_space); \
+ aux_buf = up_native + sizeof(struct structname); \
+ err = getfunc(up_native, up, aux_buf, aux_space); \
+ } while (0)
+
static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- union {
- struct v4l2_format v2f;
- struct v4l2_buffer v2b;
- struct v4l2_framebuffer v2fb;
- struct v4l2_input v2i;
- struct v4l2_standard v2s;
- struct v4l2_ext_controls v2ecs;
- struct v4l2_event v2ev;
- struct v4l2_create_buffers v2crt;
- struct v4l2_subdev_edid v2edid;
- unsigned long vx;
- int vi;
- } karg;
void __user *up = compat_ptr(arg);
+ void __user *up_native = NULL;
+ void __user *aux_buf;
+ int aux_space;
int compatible_arg = 1;
long err = 0;
@@ -873,30 +1016,35 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_STREAMOFF:
case VIDIOC_S_INPUT:
case VIDIOC_S_OUTPUT:
- err = get_user(karg.vi, (s32 __user *)up);
+ up_native = ALLOC_USER_SPACE(sizeof(unsigned __user));
+ if (convert_in_user((compat_uint_t __user *)up,
+ (unsigned __user *) up_native))
+ return -EFAULT;
compatible_arg = 0;
break;
case VIDIOC_G_INPUT:
case VIDIOC_G_OUTPUT:
+ up_native = ALLOC_USER_SPACE(sizeof(unsigned __user));
compatible_arg = 0;
break;
case VIDIOC_SUBDEV_G_EDID:
case VIDIOC_SUBDEV_S_EDID:
- err = get_v4l2_subdev_edid32(&karg.v2edid, up);
+ up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_subdev_edid));
+ err = get_v4l2_subdev_edid32(up_native, up);
compatible_arg = 0;
break;
case VIDIOC_G_FMT:
case VIDIOC_S_FMT:
case VIDIOC_TRY_FMT:
- err = get_v4l2_format32(&karg.v2f, up);
+ ALLOC_AND_GET(bufsize_v4l2_format32, get_v4l2_format32, v4l2_format);
compatible_arg = 0;
break;
case VIDIOC_CREATE_BUFS:
- err = get_v4l2_create32(&karg.v2crt, up);
+ ALLOC_AND_GET(bufsize_v4l2_create32, get_v4l2_create32, v4l2_create_buffers);
compatible_arg = 0;
break;
@@ -904,36 +1052,41 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_QUERYBUF:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
- err = get_v4l2_buffer32(&karg.v2b, up);
+ ALLOC_AND_GET(bufsize_v4l2_buffer32, get_v4l2_buffer32, v4l2_buffer);
compatible_arg = 0;
break;
case VIDIOC_S_FBUF:
- err = get_v4l2_framebuffer32(&karg.v2fb, up);
+ up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_framebuffer));
+ err = get_v4l2_framebuffer32(up_native, up);
compatible_arg = 0;
break;
case VIDIOC_G_FBUF:
+ up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_framebuffer));
compatible_arg = 0;
break;
case VIDIOC_ENUMSTD:
- err = get_v4l2_standard32(&karg.v2s, up);
+ up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_standard));
+ err = get_v4l2_standard32(up_native, up);
compatible_arg = 0;
break;
case VIDIOC_ENUMINPUT:
- err = get_v4l2_input32(&karg.v2i, up);
+ up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_input));
+ err = get_v4l2_input32(up_native, up);
compatible_arg = 0;
break;
case VIDIOC_G_EXT_CTRLS:
case VIDIOC_S_EXT_CTRLS:
case VIDIOC_TRY_EXT_CTRLS:
- err = get_v4l2_ext_controls32(&karg.v2ecs, up);
+ ALLOC_AND_GET(bufsize_v4l2_ext_controls32, get_v4l2_ext_controls32, v4l2_ext_controls);
compatible_arg = 0;
break;
case VIDIOC_DQEVENT:
+ up_native = ALLOC_USER_SPACE(sizeof(struct v4l2_event));
compatible_arg = 0;
break;
}
@@ -942,13 +1095,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
if (compatible_arg)
err = native_ioctl(file, cmd, (unsigned long)up);
- else {
- mm_segment_t old_fs = get_fs();
-
- set_fs(KERNEL_DS);
- err = native_ioctl(file, cmd, (unsigned long)&karg);
- set_fs(old_fs);
- }
+ else
+ err = native_ioctl(file, cmd, (unsigned long)up_native);
/* Special case: even after an error we need to put the
results back for these ioctls since the error_idx will
@@ -957,7 +1105,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_G_EXT_CTRLS:
case VIDIOC_S_EXT_CTRLS:
case VIDIOC_TRY_EXT_CTRLS:
- if (put_v4l2_ext_controls32(&karg.v2ecs, up))
+ if (put_v4l2_ext_controls32(up_native, up))
err = -EFAULT;
break;
}
@@ -969,44 +1117,45 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
case VIDIOC_S_OUTPUT:
case VIDIOC_G_INPUT:
case VIDIOC_G_OUTPUT:
- err = put_user(((s32)karg.vi), (s32 __user *)up);
+ err = convert_in_user(((unsigned __user *)up_native),
+ (compat_uint_t __user *)up);
break;
case VIDIOC_G_FBUF:
- err = put_v4l2_framebuffer32(&karg.v2fb, up);
+ err = put_v4l2_framebuffer32(up_native, up);
break;
case VIDIOC_DQEVENT:
- err = put_v4l2_event32(&karg.v2ev, up);
+ err = put_v4l2_event32(up_native, up);
break;
case VIDIOC_SUBDEV_G_EDID:
case VIDIOC_SUBDEV_S_EDID:
- err = put_v4l2_subdev_edid32(&karg.v2edid, up);
+ err = put_v4l2_subdev_edid32(up_native, up);
break;
case VIDIOC_G_FMT:
case VIDIOC_S_FMT:
case VIDIOC_TRY_FMT:
- err = put_v4l2_format32(&karg.v2f, up);
+ err = put_v4l2_format32(up_native, up);
break;
case VIDIOC_CREATE_BUFS:
- err = put_v4l2_create32(&karg.v2crt, up);
+ err = put_v4l2_create32(up_native, up);
break;
case VIDIOC_QUERYBUF:
case VIDIOC_QBUF:
case VIDIOC_DQBUF:
- err = put_v4l2_buffer32(&karg.v2b, up);
+ err = put_v4l2_buffer32(up_native, up);
break;
case VIDIOC_ENUMSTD:
- err = put_v4l2_standard32(&karg.v2s, up);
+ err = put_v4l2_standard32(up_native, up);
break;
case VIDIOC_ENUMINPUT:
- err = put_v4l2_input32(&karg.v2i, up);
+ err = put_v4l2_input32(up_native, up);
break;
}
return err;