Re: [RFC v3 2/2] v4l: Implement compat functions for enum to __u32 change

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Em 02-05-2012 16:13, Sakari Ailus escreveu:
> Implement compat functions to provide conversion between structs containing
> enums and those not. The functions are intended to be removed when the
> support for such old binaries is no longer necessary.

This is not a full review of this patch, as checking the full logic
will consume some time.

This is just a few points to consider.
> 
> Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxx>
> ---
>  drivers/media/video/Kconfig      |   12 +
>  drivers/media/video/v4l2-ioctl.c |  684 +++++++++++++++++++++++++++++++++++++-
>  2 files changed, 687 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
> index f2479c5..949f804 100644
> --- a/drivers/media/video/Kconfig
> +++ b/drivers/media/video/Kconfig
> @@ -7,6 +7,18 @@ config VIDEO_V4L2
>  	depends on VIDEO_DEV && VIDEO_V4L2_COMMON
>  	default VIDEO_DEV && VIDEO_V4L2_COMMON
>  
> +config V4L2_COMPAT
> +	bool "Compatibility for old binaries"
> +	depends on VIDEO_V4L2
> +	default y
> +	---help---
> +	  Compatibility code to support binaries compiled with old V4L2
> +	  IOCTL definitions containing enums that have been replaced by
> +	  __u32. If you do not need to use such binaries you can disable
> +	  this option.
> +
> +	  When in doubt, say Y.
> +
>  config VIDEOBUF_GEN
>  	tristate
>  
> diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
> index 5b2ec1f..9b88360 100644
> --- a/drivers/media/video/v4l2-ioctl.c
> +++ b/drivers/media/video/v4l2-ioctl.c
> @@ -2303,6 +2303,653 @@ static long __video_do_ioctl(struct file *file,
>  	return ret;
>  }
>  
> +#ifdef CONFIG_V4L2_COMPAT

Hmm... maybe the better would be to put it on a separate file.

Also, as the size on userspace can be different than on Kernelspace, a similar
code is needed at v4l2-compat-ioctl32.c.

As I said before (not sure if it was via e-mail or on irc), I suspect that the
code there can be simplified a lot, as only the structs with enums and pointers
require compat code. For all others, the code could simply use the default
behavior.

> +
> +static inline unsigned int get_non_compat_cmd(unsigned int cmd)
> +{
> +	switch (cmd) {
> +	case VIDIOC_ENUM_FMT_ENUM:
> +		return VIDIOC_ENUM_FMT;
> +	case VIDIOC_G_FMT_ENUM:
> +		return VIDIOC_G_FMT;
> +	case VIDIOC_S_FMT_ENUM:
> +		return VIDIOC_S_FMT;
> +	case VIDIOC_REQBUFS_ENUM:
> +		return VIDIOC_REQBUFS;
> +	case VIDIOC_QUERYBUF_ENUM:
> +		return VIDIOC_QUERYBUF;
> +	case VIDIOC_G_FBUF_ENUM:
> +		return VIDIOC_G_FBUF;
> +	case VIDIOC_S_FBUF_ENUM:
> +		return VIDIOC_S_FBUF;
> +	case VIDIOC_QBUF_ENUM:
> +		return VIDIOC_QBUF;
> +	case VIDIOC_DQBUF_ENUM:
> +		return VIDIOC_DQBUF;
> +	case VIDIOC_G_PARM_ENUM:
> +		return VIDIOC_G_PARM;
> +	case VIDIOC_S_PARM_ENUM:
> +		return VIDIOC_S_PARM;
> +	case VIDIOC_G_TUNER_ENUM:
> +		return VIDIOC_G_TUNER;
> +	case VIDIOC_S_TUNER_ENUM:
> +		return VIDIOC_S_TUNER;
> +	case VIDIOC_QUERYCTRL_ENUM:
> +		return VIDIOC_QUERYCTRL;
> +	case VIDIOC_G_FREQUENCY_ENUM:
> +		return VIDIOC_G_FREQUENCY;
> +	case VIDIOC_S_FREQUENCY_ENUM:
> +		return VIDIOC_S_FREQUENCY;
> +	case VIDIOC_CROPCAP_ENUM:
> +		return VIDIOC_CROPCAP;
> +	case VIDIOC_G_CROP_ENUM:
> +		return VIDIOC_G_CROP;
> +	case VIDIOC_S_CROP_ENUM:
> +		return VIDIOC_S_CROP;
> +	case VIDIOC_TRY_FMT_ENUM:
> +		return VIDIOC_TRY_FMT;
> +	case VIDIOC_G_SLICED_VBI_CAP_ENUM:
> +		return VIDIOC_G_SLICED_VBI_CAP;
> +	case VIDIOC_S_HW_FREQ_SEEK_ENUM:
> +		return VIDIOC_S_HW_FREQ_SEEK;
> +	case VIDIOC_CREATE_BUFS_ENUM:
> +		return VIDIOC_CREATE_BUFS;
> +	case VIDIOC_PREPARE_BUF_ENUM:
> +		return VIDIOC_PREPARE_BUF;
> +	default:
> +		return cmd;
> +	}
> +}
> +
> +#define get_user_conv(x, ptr)			\
> +	({					\
> +		typeof (*ptr) tmp;		\
> +		int rval;			\
> +						\
> +		rval = get_user(tmp, ptr);	\
> +		if (!rval)			\
> +			x = (typeof (x))tmp;	\
> +						\
> +		rval;				\
> +	})
> +
> +static int get_user_pix_format(struct v4l2_pix_format *k,
> +			       struct v4l2_pix_format_enum *u)
> +{
> +	return get_user(k->width, &u->width)
> +		|| get_user(k->height, &u->height)
> +		|| get_user(k->pixelformat, &u->pixelformat)
> +		|| get_user_conv(k->field, &u->field)
> +		|| get_user(k->bytesperline, &u->bytesperline)
> +		|| get_user(k->sizeimage, &u->sizeimage)
> +		|| get_user_conv(k->colorspace, &u->colorspace)
> +		|| get_user(k->priv, &u->priv);
> +}
> +
> +static int get_user_pix_format_mplane(struct v4l2_pix_format_mplane *k,
> +				      struct v4l2_pix_format_mplane_enum *u)
> +{
> +	return get_user(k->width, &u->width)
> +		|| get_user(k->height, &u->height)
> +		|| get_user(k->pixelformat, &u->pixelformat)
> +		|| get_user_conv(k->field, &u->field)
> +		|| get_user_conv(k->colorspace, &u->colorspace)
> +		|| copy_from_user(k->plane_fmt, u->plane_fmt,
> +				  sizeof(k->plane_fmt))
> +		|| get_user(k->num_planes, &u->num_planes)
> +		|| copy_from_user(k->reserved, u->reserved,
> +				  sizeof(k->reserved));
> +}
> +
> +static int get_user_window(struct v4l2_window *k,
> +			   struct v4l2_window_enum *u)
> +{
> +	return copy_from_user(&k->w, &u->w, sizeof(k->w))
> +		|| get_user_conv(k->field, &u->field)
> +		|| get_user(k->chromakey, &u->chromakey)
> +		|| get_user(k->clips, &u->clips)
> +		|| get_user(k->clipcount, &u->clipcount)
> +		|| get_user(k->bitmap, &u->bitmap)
> +		|| get_user(k->global_alpha, &u->global_alpha);
> +}
> +
> +static int get_user_format(struct v4l2_format *k,
> +			   struct v4l2_format_enum *u)
> +{
> +	if (get_user(k->type, &u->type))
> +		return -EFAULT;
> +
> +	switch (k->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		if (get_user_pix_format(&k->fmt.pix, &u->fmt.pix))
> +			return -EFAULT;
> +		return 0;
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		if (get_user_pix_format_mplane(&k->fmt.pix_mp, &u->fmt.pix_mp))
> +			return -EFAULT;
> +		return 0;
> +	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> +		if (get_user_window(&k->fmt.win, &u->fmt.win))
> +			return -EFAULT;
> +		return 0;
> +	default:
> +		if (copy_from_user(k->fmt.raw_data, u->fmt.raw_data,
> +				   sizeof(k->fmt.raw_data)))
> +			return -EFAULT;
> +		return 0;
> +	}
> +}
> +
> +static long copy_compat_from_user(unsigned int cmd, void *parg,
> +				  void __user *arg)
> +{
> +	union {
> +		struct v4l2_fmtdesc_enum fmtdesc;
> +		struct v4l2_format_enum fmt;
> +		struct v4l2_requestbuffers_enum reqbufs;
> +		struct v4l2_framebuffer_enum fb;
> +		struct v4l2_buffer_enum buf;
> +		struct v4l2_streamparm_enum sparm;
> +		struct v4l2_tuner_enum tuner;
> +		struct v4l2_queryctrl_enum qc;
> +		struct v4l2_frequency_enum freq;
> +		struct v4l2_cropcap_enum cropcap;
> +		struct v4l2_crop_enum crop;
> +		struct v4l2_sliced_vbi_cap_enum vbi_cap;
> +		struct v4l2_hw_freq_seek_enum freq_seek;
> +		struct v4l2_create_buffers_enum create_bufs;
> +	} __user *u = arg;
> +	union {
> +		struct v4l2_fmtdesc fmtdesc;
> +		struct v4l2_format fmt;
> +		struct v4l2_requestbuffers reqbufs;
> +		struct v4l2_framebuffer fb;
> +		struct v4l2_buffer buf;
> +		struct v4l2_streamparm sparm;
> +		struct v4l2_tuner tuner;
> +		struct v4l2_queryctrl qc;
> +		struct v4l2_frequency freq;
> +		struct v4l2_cropcap cropcap;
> +		struct v4l2_crop crop;
> +		struct v4l2_sliced_vbi_cap vbi_cap;
> +		struct v4l2_hw_freq_seek freq_seek;
> +		struct v4l2_create_buffers create_bufs;
> +	} *k = parg;
> +
> +	if (!access_ok(VERIFY_READ, u, _IOC_SIZE(cmd)))
> +		return -EFAULT;
> +
> +	switch (cmd) {
> +	case VIDIOC_ENUM_FMT_ENUM:
> +		if (get_user(k->fmtdesc.index, &u->fmtdesc.index)
> +		    || get_user_conv(k->fmtdesc.type, &u->fmtdesc.type)
> +		    || get_user(k->fmtdesc.flags, &u->fmtdesc.flags)
> +		    || copy_from_user(k->fmtdesc.description,
> +				      u->fmtdesc.description,
> +				      sizeof(k->fmtdesc.description))
> +		    || get_user(k->fmtdesc.pixelformat,
> +				&u->fmtdesc.pixelformat)
> +		    || copy_from_user(k->fmtdesc.reserved,
> +				      u->fmtdesc.reserved,
> +				      sizeof(k->fmtdesc.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_FMT_ENUM:
> +	case VIDIOC_S_FMT_ENUM:
> +	case VIDIOC_TRY_FMT_ENUM:
> +		if (get_user_format(&k->fmt, &u->fmt))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_REQBUFS_ENUM:
> +		if (get_user(k->reqbufs.count, &u->reqbufs.count)
> +		    || get_user_conv(k->reqbufs.type, &u->reqbufs.type)
> +		    || get_user_conv(k->reqbufs.memory, &u->reqbufs.memory)
> +		    || copy_from_user(k->reqbufs.reserved,
> +				      u->reqbufs.reserved,
> +				      sizeof(k->reqbufs.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_QUERYBUF_ENUM:
> +	case VIDIOC_QBUF_ENUM:
> +	case VIDIOC_DQBUF_ENUM:
> +	case VIDIOC_PREPARE_BUF_ENUM:
> +		if (get_user(k->buf.index, &u->buf.index)
> +		    || get_user_conv(k->buf.type, &u->buf.type)
> +		    || get_user(k->buf.bytesused, &u->buf.bytesused)
> +		    || get_user(k->buf.flags, &u->buf.flags)
> +		    || get_user_conv(k->buf.field, &u->buf.field)
> +		    || copy_from_user(&k->buf.timestamp, &u->buf.timestamp,
> +				      sizeof(k->buf.timestamp))
> +		    || copy_from_user(&k->buf.timecode, &u->buf.timecode,
> +				      sizeof(k->buf.timecode))
> +		    || get_user(k->buf.sequence, &u->buf.sequence)
> +		    || get_user_conv(k->buf.memory, &u->buf.memory)
> +		    || copy_from_user(&k->buf.m, &u->buf.m, sizeof(k->buf.m))
> +		    || get_user(k->buf.length, &u->buf.length)
> +		    || get_user(k->buf.reserved2, &u->buf.reserved2)
> +		    || get_user(k->buf.reserved, &u->buf.reserved))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_FBUF_ENUM:
> +	case VIDIOC_S_FBUF_ENUM:
> +		if (get_user(k->fb.capability, &u->fb.capability)
> +		    || get_user(k->fb.flags, &u->fb.flags)
> +		    || get_user(k->fb.base, &u->fb.base)
> +		    || get_user_pix_format(&k->fb.fmt, &u->fb.fmt))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_PARM_ENUM:
> +	case VIDIOC_S_PARM_ENUM:
> +		if (get_user_conv(k->sparm.type, &u->sparm.type)
> +		    || copy_from_user(&k->sparm.parm, &u->sparm.parm,
> +				      sizeof(k->sparm.parm)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_TUNER_ENUM:
> +	case VIDIOC_S_TUNER_ENUM:
> +		if (get_user(k->tuner.index, &u->tuner.index)
> +		    || copy_from_user(k->tuner.name, u->tuner.name,
> +				      sizeof(k->tuner.name))
> +		    || get_user_conv(k->tuner.type, &u->tuner.type)
> +		    || get_user(k->tuner.capability, &u->tuner.capability)
> +		    || get_user(k->tuner.rangelow, &u->tuner.rangelow)
> +		    || get_user(k->tuner.rangehigh, &u->tuner.rangehigh)
> +		    || get_user(k->tuner.rxsubchans, &u->tuner.rxsubchans)
> +		    || get_user(k->tuner.audmode, &u->tuner.audmode)
> +		    || get_user(k->tuner.signal, &u->tuner.signal)
> +		    || get_user(k->tuner.afc, &u->tuner.afc)
> +		    || copy_from_user(k->tuner.reserved, u->tuner.reserved,
> +				      sizeof(k->tuner.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_QUERYCTRL_ENUM:
> +		if (get_user(k->qc.id, &u->qc.id)
> +		    || get_user_conv(k->qc.type, &u->qc.type)
> +		    || copy_from_user(k->qc.name, u->qc.name,
> +				      sizeof(k->qc.name))
> +		    || get_user(k->qc.minimum, &u->qc.minimum)
> +		    || get_user(k->qc.maximum, &u->qc.maximum)
> +		    || get_user(k->qc.step, &u->qc.step)
> +		    || get_user(k->qc.default_value, &u->qc.default_value)
> +		    || get_user(k->qc.flags, &u->qc.flags)
> +		    || copy_from_user(k->qc.reserved, u->qc.reserved,
> +				      sizeof(k->qc.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_FREQUENCY_ENUM:
> +	case VIDIOC_S_FREQUENCY_ENUM:
> +		if (get_user(k->freq.tuner, &u->freq.tuner)
> +		    || get_user_conv(k->freq.type, &u->freq.type)
> +		    || get_user(k->freq.frequency, &u->freq.frequency)
> +		    || copy_from_user(k->freq.reserved, u->freq.reserved,
> +				      sizeof(k->freq.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_CROPCAP_ENUM:
> +		if (get_user_conv(k->cropcap.type, &u->cropcap.type)
> +		    || copy_from_user(&k->cropcap.bounds, &u->cropcap.bounds,
> +				      sizeof(k->cropcap.bounds))
> +		    || copy_from_user(&k->cropcap.defrect, &u->cropcap.defrect,
> +				      sizeof(k->cropcap.bounds))
> +		    || copy_from_user(&k->cropcap.pixelaspect,
> +				      &u->cropcap.pixelaspect,
> +				      sizeof(k->cropcap.pixelaspect)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_CROP_ENUM:
> +	case VIDIOC_S_CROP_ENUM:
> +		if (get_user_conv(k->crop.type, &u->crop.type)
> +		    || copy_from_user(&k->crop.c, &u->crop.c,
> +				      sizeof(k->crop.c)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_SLICED_VBI_CAP_ENUM:
> +		if (get_user(k->vbi_cap.service_set, &u->vbi_cap.service_set)
> +		    || copy_from_user(k->vbi_cap.service_lines,
> +				      u->vbi_cap.service_lines,
> +				      sizeof(k->vbi_cap.service_lines))
> +		    || get_user_conv(k->vbi_cap.type, &u->vbi_cap.type)
> +		    || copy_from_user(k->vbi_cap.reserved, u->vbi_cap.reserved,
> +				      sizeof(k->vbi_cap.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_S_HW_FREQ_SEEK_ENUM:
> +		if (get_user(k->freq_seek.tuner, &u->freq_seek.tuner)
> +		    || get_user_conv(k->freq_seek.type, &u->freq_seek.type)
> +		    || get_user(k->freq_seek.seek_upward,
> +				&u->freq_seek.seek_upward)
> +		    || get_user(k->freq_seek.wrap_around,
> +				&u->freq_seek.wrap_around)
> +		    || get_user(k->freq_seek.spacing, &u->freq_seek.spacing)
> +		    || copy_from_user(k->freq_seek.reserved,
> +				      u->freq_seek.reserved,
> +				      sizeof(k->freq_seek.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_CREATE_BUFS_ENUM:
> +		if (get_user(k->create_bufs.index, &u->create_bufs.index)
> +		    || get_user(k->create_bufs.count, &u->create_bufs.count)
> +		    || get_user_conv(k->create_bufs.memory,
> +				     &u->create_bufs.memory)
> +		    || get_user_format(&k->create_bufs.format,
> +				       &u->create_bufs.format)
> +		    || copy_from_user(k->create_bufs.reserved,
> +				      u->create_bufs.reserved,
> +				      sizeof(k->create_bufs.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	default:
> +		WARN(1, "%s: bad compat cmd %8.8x\n", __func__, cmd);
> +		return -EINVAL;
> +	}
> +}
> +
> +#define put_user_conv(x, ptr)			\
> +	({					\
> +		typeof (*ptr) tmp;		\
> +						\
> +		tmp = (typeof (*ptr))x;		\
> +		put_user(tmp, ptr);		\
> +	})
> +
> +static int put_user_pix_format(struct v4l2_pix_format *k,
> +			       struct v4l2_pix_format_enum *u)
> +{
> +	return put_user(k->width, &u->width)
> +		|| put_user(k->height, &u->height)
> +		|| put_user(k->pixelformat, &u->pixelformat)
> +		|| put_user_conv(k->field, &u->field)
> +		|| put_user(k->bytesperline, &u->bytesperline)
> +		|| put_user(k->sizeimage, &u->sizeimage)
> +		|| put_user_conv(k->colorspace, &u->colorspace)
> +		|| put_user(k->priv, &u->priv);
> +}
> +
> +static int put_user_pix_format_mplane(struct v4l2_pix_format_mplane *k,
> +				      struct v4l2_pix_format_mplane_enum *u)
> +{
> +	return put_user(k->width, &u->width)
> +		|| put_user(k->height, &u->height)
> +		|| put_user(k->pixelformat, &u->pixelformat)
> +		|| put_user_conv(k->field, &u->field)
> +		|| put_user_conv(k->colorspace, &u->colorspace)
> +		|| copy_to_user(u->plane_fmt, k->plane_fmt,
> +				sizeof(k->plane_fmt))
> +		|| put_user(k->num_planes, &u->num_planes)
> +		|| copy_to_user(u->reserved, k->reserved,
> +				sizeof(k->reserved));
> +}
> +
> +static int put_user_window(struct v4l2_window *k,
> +			   struct v4l2_window_enum *u)
> +{
> +	return copy_to_user(&u->w, &k->w, sizeof(k->w))
> +		|| put_user_conv(k->field, &u->field)
> +		|| put_user(k->chromakey, &u->chromakey)
> +		|| put_user(k->clips, &u->clips)
> +		|| put_user(k->clipcount, &u->clipcount)
> +		|| put_user(k->bitmap, &u->bitmap)
> +		|| put_user(k->global_alpha, &u->global_alpha);
> +}
> +
> +static int put_user_format(struct v4l2_format *k,
> +			   struct v4l2_format_enum *u)
> +{
> +	if (put_user(k->type, &u->type))
> +		return -EFAULT;
> +
> +	switch (k->type) {
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> +		if (put_user_pix_format(&k->fmt.pix, &u->fmt.pix))
> +			return -EFAULT;
> +		return 0;
> +	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> +		if (put_user_pix_format_mplane(&k->fmt.pix_mp, &u->fmt.pix_mp))
> +			return -EFAULT;
> +		return 0;
> +	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> +	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> +		if (put_user_window(&k->fmt.win, &u->fmt.win))
> +			return -EFAULT;
> +		return 0;
> +	default:
> +		if (copy_to_user(u->fmt.raw_data, k->fmt.raw_data,
> +				 sizeof(k->fmt.raw_data)))
> +			return -EFAULT;
> +		return 0;
> +	}
> +}
> +
> +static long copy_compat_to_user(unsigned int cmd, void __user *arg,
> +				void *parg)
> +{
> +	union {
> +		struct v4l2_fmtdesc_enum fmtdesc;
> +		struct v4l2_format_enum fmt;
> +		struct v4l2_requestbuffers_enum reqbufs;
> +		struct v4l2_framebuffer_enum fb;
> +		struct v4l2_buffer_enum buf;
> +		struct v4l2_streamparm_enum sparm;
> +		struct v4l2_tuner_enum tuner;
> +		struct v4l2_queryctrl_enum qc;
> +		struct v4l2_frequency_enum freq;
> +		struct v4l2_cropcap_enum cropcap;
> +		struct v4l2_crop_enum crop;
> +		struct v4l2_sliced_vbi_cap_enum vbi_cap;
> +		struct v4l2_hw_freq_seek_enum freq_seek;
> +		struct v4l2_create_buffers_enum create_bufs;
> +	} __user *u = arg;
> +	union {
> +		struct v4l2_fmtdesc fmtdesc;
> +		struct v4l2_format fmt;
> +		struct v4l2_requestbuffers reqbufs;
> +		struct v4l2_framebuffer fb;
> +		struct v4l2_buffer buf;
> +		struct v4l2_streamparm sparm;
> +		struct v4l2_tuner tuner;
> +		struct v4l2_queryctrl qc;
> +		struct v4l2_frequency freq;
> +		struct v4l2_cropcap cropcap;
> +		struct v4l2_crop crop;
> +		struct v4l2_sliced_vbi_cap vbi_cap;
> +		struct v4l2_hw_freq_seek freq_seek;
> +		struct v4l2_create_buffers create_bufs;
> +	} *k = parg;
> +
> +	if (!access_ok(VERIFY_WRITE, u, _IOC_SIZE(cmd)))
> +		return -EFAULT;
> +
> +	switch (cmd) {
> +	case VIDIOC_ENUM_FMT_ENUM:
> +		if (put_user(k->fmtdesc.index, &u->fmtdesc.index)
> +		    || put_user_conv(k->fmtdesc.type, &u->fmtdesc.type)
> +		    || put_user(k->fmtdesc.flags, &u->fmtdesc.flags)
> +		    || copy_to_user(u->fmtdesc.description,
> +				    k->fmtdesc.description,
> +				    sizeof(k->fmtdesc.description))
> +		    || put_user(k->fmtdesc.pixelformat,
> +				&u->fmtdesc.pixelformat)
> +		    || copy_to_user(u->fmtdesc.reserved,
> +				    k->fmtdesc.reserved,
> +				    sizeof(k->fmtdesc.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_FMT_ENUM:
> +	case VIDIOC_S_FMT_ENUM:
> +	case VIDIOC_TRY_FMT_ENUM:
> +		if (put_user_format(&k->fmt, &u->fmt))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_REQBUFS_ENUM:
> +		if (put_user(k->reqbufs.count, &u->reqbufs.count)
> +		    || put_user_conv(k->reqbufs.type, &u->reqbufs.type)
> +		    || put_user_conv(k->reqbufs.memory, &u->reqbufs.memory)
> +		    || copy_to_user(u->reqbufs.reserved,
> +				    k->reqbufs.reserved,
> +				    sizeof(k->reqbufs.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_QUERYBUF_ENUM:
> +	case VIDIOC_QBUF_ENUM:
> +	case VIDIOC_DQBUF_ENUM:
> +	case VIDIOC_PREPARE_BUF_ENUM:
> +		if (put_user(k->buf.index, &u->buf.index)
> +		    || put_user_conv(k->buf.type, &u->buf.type)
> +		    || put_user(k->buf.bytesused, &u->buf.bytesused)
> +		    || put_user(k->buf.flags, &u->buf.flags)
> +		    || put_user_conv(k->buf.field, &u->buf.field)
> +		    || copy_to_user(&u->buf.timestamp, &k->buf.timestamp,
> +				    sizeof(k->buf.timestamp))
> +		    || copy_to_user(&u->buf.timecode, &k->buf.timecode,
> +				    sizeof(k->buf.timecode))
> +		    || put_user(k->buf.sequence, &u->buf.sequence)
> +		    || put_user_conv(k->buf.memory, &u->buf.memory)
> +		    || copy_to_user(&u->buf.m, &k->buf.m, sizeof(k->buf.m))
> +		    || put_user(k->buf.length, &u->buf.length)
> +		    || put_user(k->buf.reserved2, &u->buf.reserved2)
> +		    || put_user(k->buf.reserved, &u->buf.reserved))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_FBUF_ENUM:
> +	case VIDIOC_S_FBUF_ENUM:
> +		if (put_user(k->fb.capability, &u->fb.capability)
> +		    || put_user(k->fb.flags, &u->fb.flags)
> +		    || put_user(k->fb.base, &u->fb.base)
> +		    || put_user_pix_format(&k->fb.fmt, &u->fb.fmt))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_PARM_ENUM:
> +	case VIDIOC_S_PARM_ENUM:
> +		if (put_user_conv(k->sparm.type, &u->sparm.type)
> +		    || copy_to_user(&u->sparm.parm, &k->sparm.parm,
> +				    sizeof(k->sparm.parm)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_TUNER_ENUM:
> +	case VIDIOC_S_TUNER_ENUM:
> +		if (put_user(k->tuner.index, &u->tuner.index)
> +		    || copy_to_user(u->tuner.name, k->tuner.name,
> +				    sizeof(k->tuner.name))
> +		    || put_user_conv(k->tuner.type, &u->tuner.type)
> +		    || put_user(k->tuner.capability, &u->tuner.capability)
> +		    || put_user(k->tuner.rangelow, &u->tuner.rangelow)
> +		    || put_user(k->tuner.rangehigh, &u->tuner.rangehigh)
> +		    || put_user(k->tuner.rxsubchans, &u->tuner.rxsubchans)
> +		    || put_user(k->tuner.audmode, &u->tuner.audmode)
> +		    || put_user(k->tuner.signal, &u->tuner.signal)
> +		    || put_user(k->tuner.afc, &u->tuner.afc)
> +		    || copy_to_user(u->tuner.reserved, k->tuner.reserved,
> +				    sizeof(k->tuner.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_QUERYCTRL_ENUM:
> +		if (put_user(k->qc.id, &u->qc.id)
> +		    || put_user_conv(k->qc.type, &u->qc.type)
> +		    || copy_to_user(u->qc.name, k->qc.name,
> +				    sizeof(k->qc.name))
> +		    || put_user(k->qc.minimum, &u->qc.minimum)
> +		    || put_user(k->qc.maximum, &u->qc.maximum)
> +		    || put_user(k->qc.step, &u->qc.step)
> +		    || put_user(k->qc.default_value, &u->qc.default_value)
> +		    || put_user(k->qc.flags, &u->qc.flags)
> +		    || copy_to_user(u->qc.reserved, k->qc.reserved,
> +				    sizeof(k->qc.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_FREQUENCY_ENUM:
> +	case VIDIOC_S_FREQUENCY_ENUM:
> +		if (put_user(k->freq.tuner, &u->freq.tuner)
> +		    || put_user_conv(k->freq.type, &u->freq.type)
> +		    || put_user(k->freq.frequency, &u->freq.frequency)
> +		    || copy_to_user(u->freq.reserved, k->freq.reserved,
> +				    sizeof(k->freq.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_CROPCAP_ENUM:
> +		if (put_user_conv(k->cropcap.type, &u->cropcap.type)
> +		    || copy_to_user(&u->cropcap.bounds, &k->cropcap.bounds,
> +				    sizeof(k->cropcap.bounds))
> +		    || copy_to_user(&u->cropcap.defrect, &k->cropcap.defrect,
> +				    sizeof(k->cropcap.bounds))
> +		    || copy_to_user(&u->cropcap.pixelaspect,
> +				    &k->cropcap.pixelaspect,
> +				    sizeof(k->cropcap.pixelaspect)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_CROP_ENUM:
> +	case VIDIOC_S_CROP_ENUM:
> +		if (put_user_conv(k->crop.type, &u->crop.type)
> +		    || copy_to_user(&u->crop.c, &k->crop.c,
> +				    sizeof(k->crop.c)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_G_SLICED_VBI_CAP_ENUM:
> +		if (put_user(k->vbi_cap.service_set, &u->vbi_cap.service_set)
> +		    || copy_to_user(u->vbi_cap.service_lines,
> +				    k->vbi_cap.service_lines,
> +				    sizeof(k->vbi_cap.service_lines))
> +		    || put_user_conv(k->vbi_cap.type, &u->vbi_cap.type)
> +		    || copy_to_user(u->vbi_cap.reserved, k->vbi_cap.reserved,
> +				    sizeof(k->vbi_cap.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_S_HW_FREQ_SEEK_ENUM:
> +		if (put_user(k->freq_seek.tuner, &u->freq_seek.tuner)
> +		    || put_user_conv(k->freq_seek.type, &u->freq_seek.type)
> +		    || put_user(k->freq_seek.seek_upward,
> +				&u->freq_seek.seek_upward)
> +		    || put_user(k->freq_seek.wrap_around,
> +				&u->freq_seek.wrap_around)
> +		    || put_user(k->freq_seek.spacing, &u->freq_seek.spacing)
> +		    || copy_to_user(u->freq_seek.reserved,
> +				    k->freq_seek.reserved,
> +				    sizeof(k->freq_seek.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	case VIDIOC_CREATE_BUFS_ENUM:
> +		if (put_user(k->create_bufs.index, &u->create_bufs.index)
> +		    || put_user(k->create_bufs.count, &u->create_bufs.count)
> +		    || put_user_conv(k->create_bufs.memory,
> +				     &u->create_bufs.memory)
> +		    || put_user_format(&k->create_bufs.format,
> +				       &u->create_bufs.format)
> +		    || copy_to_user(u->create_bufs.reserved,
> +				    k->create_bufs.reserved,
> +				    sizeof(k->create_bufs.reserved)))
> +			return -EFAULT;
> +		return 0;
> +	default:
> +		WARN(1, "%s: bad compat cmd %8.8x\n", __func__, cmd);
> +		return -EINVAL;
> +	}
> +}
> +
> +#else /* CONFIG_V4L2_COMPAT */
> +
> +static inline unsigned int get_non_compat_cmd(unsigned int cmd)
> +{
> +	return cmd;
> +}
> +
> +static inline long copy_compat_from_user(unsigned int cmd, void __user *arg,
> +					 void *parg)
> +{
> +	return 0;
> +}
> +
> +static inline long copy_compat_to_user(unsigned int cmd, void __user *arg,
> +				       void *parg)
> +{
> +	return 0;
> +}
> +
> +#endif /* CONFIG_V4L2_COMPAT */
> +
>  /* 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.  */
> @@ -2390,7 +3037,7 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
>  }
>  
>  long
> -video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
> +video_usercopy(struct file *file, unsigned int compat_cmd, unsigned long arg,
>  	       v4l2_kioctl func)
>  {
>  	char	sbuf[128];
> @@ -2401,6 +3048,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 = get_non_compat_cmd(compat_cmd);

This will put a penalty on archs where sizeof(u32) == sizeof(enum), with covers
most of the cases.

My suggestion is to, instead, call it at the end of  __video_do_ioctl, at the
"default" clause, with a recursive call to __video_do_ioctl().

It should be noticed that UVC driver and pvrusb2 are the only two drivers that
don't use __video_do_ioctl(). So, a logic similar to it should be implemented
there.

>  
>  	/*  Copy arguments into temp kernel buffer  */
>  	if (_IOC_DIR(cmd) != _IOC_NONE) {
> @@ -2418,12 +3066,23 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
>  		if (_IOC_DIR(cmd) & _IOC_WRITE) {
>  			unsigned long n = cmd_input_size(cmd);
>  
> -			if (copy_from_user(parg, (void __user *)arg, n))
> -				goto out;
> -
> -			/* zero out anything we don't copy from userspace */
> -			if (n < _IOC_SIZE(cmd))
> -				memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
> +			if (cmd == compat_cmd) {
> +				if (copy_from_user(
> +					    parg, (void __user *)arg, n))
> +					goto out;
> +
> +				/*
> +				 * zero out anything we don't copy
> +				 * from userspace
> +				 */
> +				if (n < _IOC_SIZE(cmd))
> +					memset((u8 *)parg + n, 0,
> +					       _IOC_SIZE(cmd) - n);
> +			} else {
> +				if (copy_compat_from_user(compat_cmd, parg,
> +							  (void __user *)arg))
> +					goto out;
> +			}
>  		} else {
>  			/* read-only ioctl */
>  			memset(parg, 0, _IOC_SIZE(cmd));
> @@ -2471,8 +3130,15 @@ out_array_args:
>  	switch (_IOC_DIR(cmd)) {
>  	case _IOC_READ:
>  	case (_IOC_WRITE | _IOC_READ):
> -		if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd)))
> -			err = -EFAULT;
> +		if (cmd == compat_cmd) {
> +			if (copy_to_user((void __user *)arg, parg,
> +					 _IOC_SIZE(cmd)))
> +				err = -EFAULT;
> +		} else {
> +			if (copy_compat_to_user(compat_cmd, (void __user *)arg,
> +						parg))
> +				err = -EFAULT;
> +		}
>  		break;
>  	}
>  

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux