All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS)
@ 2018-01-26 12:43 Hans Verkuil
  2018-01-26 12:43 ` [PATCH 01/12] vivid: fix module load error when enabling fb and no_error_inj=1 Hans Verkuil
                   ` (11 more replies)
  0 siblings, 12 replies; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz

From: Hans Verkuil <hans.verkuil@cisco.com>

This patch series fixes a number of bugs and culminates in the removal
of the set_fs(KERNEL_DS) call in v4l2-compat-ioctl32.c.

See http://people.canonical.com/~ubuntu-security/cve/2017/CVE-2017-13166.html
for why this set_fs call is a bad idea.

In order to test this I used vivid and a 32-bit v4l2-compliance. The
advantage of vivid is that it implements almost all ioctls, and those
are all tested by v4l2-compliance. This ensures good test coverage.

Since I had to track down all failures that v4l2-compliance reported
in order to verify whether those were introduced by the final patch
or if those were pre-existing bugs, this series starts off with fixes
for bugs that v4l2-compliance found, mostly in v4l2-compat-ioctl32.c.
It is clear that v4l2-compat-ioctl32.c doesn't receive a lot of
testing.

There are also three patches that just clean up v4l2-compat-ioctl32.c
in order to simplify the final patch:

  v4l2-compat-ioctl32.c: fix the indentation
  v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32
  v4l2-compat-ioctl32.c: avoid sizeof(type)

No functional changes are introduced in these three patches.

Note the "fix ctrl_is_pointer" patch: we've discussed this in the past,
but now I really had to fix this.

It would be really nice if the next time someone finds a security risk
in V4L2 core code they would contact the V4L2 maintainers. We only heard
about this last week, while all the information about this CVE has been
out there for 6 months or so.

Backporting this will be a bit of a nightmare since v4l2-compat-ioctl32.c
changes frequently, so assuming we'll only backport this to lts kernels
then for each lts the patch series needs to be adapted. But let's get
this upstream first before looking at that.

Please review!

Regards,

	Hans

Daniel Mentz (1):
  v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32

Hans Verkuil (11):
  vivid: fix module load error when enabling fb and no_error_inj=1
  v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt
  v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF
  v4l2-compat-ioctl32.c: fix the indentation
  v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32
  v4l2-compat-ioctl32.c: avoid sizeof(type)
  v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32
  v4l2-compat-ioctl32.c: fix ctrl_is_pointer
  v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32
  v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type
  v4l2-compat-ioctl32.c: don't copy back the result for certain errors

 drivers/media/platform/vivid/vivid-core.h     |   1 +
 drivers/media/platform/vivid/vivid-ctrls.c    |  35 +-
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 962 +++++++++++++++-----------
 drivers/media/v4l2-core/v4l2-ioctl.c          | 140 ++--
 4 files changed, 646 insertions(+), 492 deletions(-)

-- 
2.15.1

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 01/12] vivid: fix module load error when enabling fb and no_error_inj=1
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-26 14:43   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt Hans Verkuil
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

If the framebuffer is enabled and error injection is disabled, then
creating the controls for the video output device would fail with an
error.

This is because the Clear Framebuffer control uses the 'vivid control
class' and that control class isn't added if error injection is disabled.

In addition, this control was added to e.g. vbi devices as well, which
makes no sense.

Move this control to its own control handler and handle it correctly.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/platform/vivid/vivid-core.h  |  1 +
 drivers/media/platform/vivid/vivid-ctrls.c | 35 +++++++++++++++++++++++++-----
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/vivid/vivid-core.h b/drivers/media/platform/vivid/vivid-core.h
index 50802e650750..c90e4a0ab94e 100644
--- a/drivers/media/platform/vivid/vivid-core.h
+++ b/drivers/media/platform/vivid/vivid-core.h
@@ -154,6 +154,7 @@ struct vivid_dev {
 	struct v4l2_ctrl_handler	ctrl_hdl_streaming;
 	struct v4l2_ctrl_handler	ctrl_hdl_sdtv_cap;
 	struct v4l2_ctrl_handler	ctrl_hdl_loop_cap;
+	struct v4l2_ctrl_handler	ctrl_hdl_fb;
 	struct video_device		vid_cap_dev;
 	struct v4l2_ctrl_handler	ctrl_hdl_vid_cap;
 	struct video_device		vid_out_dev;
diff --git a/drivers/media/platform/vivid/vivid-ctrls.c b/drivers/media/platform/vivid/vivid-ctrls.c
index 34731f71cc00..3f9d354827af 100644
--- a/drivers/media/platform/vivid/vivid-ctrls.c
+++ b/drivers/media/platform/vivid/vivid-ctrls.c
@@ -120,9 +120,6 @@ static int vivid_user_gen_s_ctrl(struct v4l2_ctrl *ctrl)
 		clear_bit(V4L2_FL_REGISTERED, &dev->radio_rx_dev.flags);
 		clear_bit(V4L2_FL_REGISTERED, &dev->radio_tx_dev.flags);
 		break;
-	case VIVID_CID_CLEAR_FB:
-		vivid_clear_fb(dev);
-		break;
 	case VIVID_CID_BUTTON:
 		dev->button_pressed = 30;
 		break;
@@ -274,8 +271,28 @@ static const struct v4l2_ctrl_config vivid_ctrl_disconnect = {
 	.type = V4L2_CTRL_TYPE_BUTTON,
 };
 
+
+/* Framebuffer Controls */
+
+static int vivid_fb_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct vivid_dev *dev = container_of(ctrl->handler,
+					     struct vivid_dev, ctrl_hdl_fb);
+
+	switch (ctrl->id) {
+	case VIVID_CID_CLEAR_FB:
+		vivid_clear_fb(dev);
+		break;
+	}
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops vivid_fb_ctrl_ops = {
+	.s_ctrl = vivid_fb_s_ctrl,
+};
+
 static const struct v4l2_ctrl_config vivid_ctrl_clear_fb = {
-	.ops = &vivid_user_gen_ctrl_ops,
+	.ops = &vivid_fb_ctrl_ops,
 	.id = VIVID_CID_CLEAR_FB,
 	.name = "Clear Framebuffer",
 	.type = V4L2_CTRL_TYPE_BUTTON,
@@ -1357,6 +1374,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
 	struct v4l2_ctrl_handler *hdl_streaming = &dev->ctrl_hdl_streaming;
 	struct v4l2_ctrl_handler *hdl_sdtv_cap = &dev->ctrl_hdl_sdtv_cap;
 	struct v4l2_ctrl_handler *hdl_loop_cap = &dev->ctrl_hdl_loop_cap;
+	struct v4l2_ctrl_handler *hdl_fb = &dev->ctrl_hdl_fb;
 	struct v4l2_ctrl_handler *hdl_vid_cap = &dev->ctrl_hdl_vid_cap;
 	struct v4l2_ctrl_handler *hdl_vid_out = &dev->ctrl_hdl_vid_out;
 	struct v4l2_ctrl_handler *hdl_vbi_cap = &dev->ctrl_hdl_vbi_cap;
@@ -1384,10 +1402,12 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
 	v4l2_ctrl_new_custom(hdl_sdtv_cap, &vivid_ctrl_class, NULL);
 	v4l2_ctrl_handler_init(hdl_loop_cap, 1);
 	v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_class, NULL);
+	v4l2_ctrl_handler_init(hdl_fb, 1);
+	v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_class, NULL);
 	v4l2_ctrl_handler_init(hdl_vid_cap, 55);
 	v4l2_ctrl_new_custom(hdl_vid_cap, &vivid_ctrl_class, NULL);
 	v4l2_ctrl_handler_init(hdl_vid_out, 26);
-	if (!no_error_inj)
+	if (!no_error_inj || dev->has_fb)
 		v4l2_ctrl_new_custom(hdl_vid_out, &vivid_ctrl_class, NULL);
 	v4l2_ctrl_handler_init(hdl_vbi_cap, 21);
 	v4l2_ctrl_new_custom(hdl_vbi_cap, &vivid_ctrl_class, NULL);
@@ -1561,7 +1581,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
 		v4l2_ctrl_new_custom(hdl_loop_cap, &vivid_ctrl_loop_video, NULL);
 
 	if (dev->has_fb)
-		v4l2_ctrl_new_custom(hdl_user_gen, &vivid_ctrl_clear_fb, NULL);
+		v4l2_ctrl_new_custom(hdl_fb, &vivid_ctrl_clear_fb, NULL);
 
 	if (dev->has_radio_rx) {
 		v4l2_ctrl_new_custom(hdl_radio_rx, &vivid_ctrl_radio_hw_seek_mode, NULL);
@@ -1658,6 +1678,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
 		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_streaming, NULL);
 		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_sdtv_cap, NULL);
 		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_loop_cap, NULL);
+		v4l2_ctrl_add_handler(hdl_vid_cap, hdl_fb, NULL);
 		if (hdl_vid_cap->error)
 			return hdl_vid_cap->error;
 		dev->vid_cap_dev.ctrl_handler = hdl_vid_cap;
@@ -1666,6 +1687,7 @@ int vivid_create_controls(struct vivid_dev *dev, bool show_ccs_cap,
 		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_gen, NULL);
 		v4l2_ctrl_add_handler(hdl_vid_out, hdl_user_aud, NULL);
 		v4l2_ctrl_add_handler(hdl_vid_out, hdl_streaming, NULL);
+		v4l2_ctrl_add_handler(hdl_vid_out, hdl_fb, NULL);
 		if (hdl_vid_out->error)
 			return hdl_vid_out->error;
 		dev->vid_out_dev.ctrl_handler = hdl_vid_out;
@@ -1725,4 +1747,5 @@ void vivid_free_controls(struct vivid_dev *dev)
 	v4l2_ctrl_handler_free(&dev->ctrl_hdl_streaming);
 	v4l2_ctrl_handler_free(&dev->ctrl_hdl_sdtv_cap);
 	v4l2_ctrl_handler_free(&dev->ctrl_hdl_loop_cap);
+	v4l2_ctrl_handler_free(&dev->ctrl_hdl_fb);
 }
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
  2018-01-26 12:43 ` [PATCH 01/12] vivid: fix module load error when enabling fb and no_error_inj=1 Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-26 14:41   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 03/12] v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF Hans Verkuil
                   ` (9 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Don't duplicate the buffer type checks in enum/g/s/try_fmt.
The check_fmt function does that already.

It is hard to keep the checks in sync for all these functions and
in fact the check for VBI was wrong in the _fmt functions as it
allowed SDR types as well. This caused a v4l2-compliance failure
for /dev/swradio0 using vivid.

This simplifies the code and keeps the check in one place and
fixes the SDR/VBI bug.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-ioctl.c | 140 ++++++++++++++---------------------
 1 file changed, 54 insertions(+), 86 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 59d2100eeff6..c7f6b65d3ad7 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -1316,52 +1316,50 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
 	struct v4l2_fmtdesc *p = arg;
-	struct video_device *vfd = video_devdata(file);
-	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
-	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
-	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
-	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
-	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
-	int ret = -EINVAL;
+	int ret = check_fmt(file, p->type);
+
+	if (ret)
+		return ret;
+	ret = -EINVAL;
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_enum_fmt_vid_cap))
+		if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
 			break;
 		ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
 		break;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap_mplane))
+		if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
 			break;
 		ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
 		break;
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_overlay))
+		if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
 			break;
 		ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
 		break;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out))
+		if (unlikely(!ops->vidioc_enum_fmt_vid_out))
 			break;
 		ret = ops->vidioc_enum_fmt_vid_out(file, fh, arg);
 		break;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out_mplane))
+		if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
 			break;
 		ret = ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
 		break;
 	case V4L2_BUF_TYPE_SDR_CAPTURE:
-		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_enum_fmt_sdr_cap))
+		if (unlikely(!ops->vidioc_enum_fmt_sdr_cap))
 			break;
 		ret = ops->vidioc_enum_fmt_sdr_cap(file, fh, arg);
 		break;
 	case V4L2_BUF_TYPE_SDR_OUTPUT:
-		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_enum_fmt_sdr_out))
+		if (unlikely(!ops->vidioc_enum_fmt_sdr_out))
 			break;
 		ret = ops->vidioc_enum_fmt_sdr_out(file, fh, arg);
 		break;
 	case V4L2_BUF_TYPE_META_CAPTURE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_meta_cap))
+		if (unlikely(!ops->vidioc_enum_fmt_meta_cap))
 			break;
 		ret = ops->vidioc_enum_fmt_meta_cap(file, fh, arg);
 		break;
@@ -1375,13 +1373,10 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
 	struct v4l2_format *p = arg;
-	struct video_device *vfd = video_devdata(file);
-	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
-	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
-	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
-	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
-	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
-	int ret;
+	int ret = check_fmt(file, p->type);
+
+	if (ret)
+		return ret;
 
 	/*
 	 * fmt can't be cleared for these overlay types due to the 'clips'
@@ -1409,7 +1404,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_g_fmt_vid_cap))
+		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
 			break;
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
@@ -1417,23 +1412,15 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane))
-			break;
 		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_overlay))
-			break;
 		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
 	case V4L2_BUF_TYPE_VBI_CAPTURE:
-		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_vbi_cap))
-			break;
 		return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_cap))
-			break;
 		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out))
+		if (unlikely(!ops->vidioc_g_fmt_vid_out))
 			break;
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
@@ -1441,32 +1428,18 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane))
-			break;
 		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_overlay))
-			break;
 		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
 	case V4L2_BUF_TYPE_VBI_OUTPUT:
-		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_vbi_out))
-			break;
 		return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
 	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_out))
-			break;
 		return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
 	case V4L2_BUF_TYPE_SDR_CAPTURE:
-		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_g_fmt_sdr_cap))
-			break;
 		return ops->vidioc_g_fmt_sdr_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_SDR_OUTPUT:
-		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_g_fmt_sdr_out))
-			break;
 		return ops->vidioc_g_fmt_sdr_out(file, fh, arg);
 	case V4L2_BUF_TYPE_META_CAPTURE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_meta_cap))
-			break;
 		return ops->vidioc_g_fmt_meta_cap(file, fh, arg);
 	}
 	return -EINVAL;
@@ -1492,12 +1465,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 {
 	struct v4l2_format *p = arg;
 	struct video_device *vfd = video_devdata(file);
-	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
-	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
-	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
-	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
-	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
-	int ret;
+	int ret = check_fmt(file, p->type);
+
+	if (ret)
+		return ret;
 
 	ret = v4l_enable_media_source(vfd);
 	if (ret)
@@ -1506,37 +1477,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_s_fmt_vid_cap))
+		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
 		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
 		/* just in case the driver zeroed it again */
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
-		if (is_tch)
+		if (vfd->vfl_type == VFL_TYPE_TOUCH)
 			v4l_pix_format_touch(&p->fmt.pix);
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane))
+		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
 		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_overlay))
+		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.win);
 		return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
 	case V4L2_BUF_TYPE_VBI_CAPTURE:
-		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_vbi_cap))
+		if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.vbi);
 		return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_cap))
+		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.sliced);
 		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out))
+		if (unlikely(!ops->vidioc_s_fmt_vid_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
 		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
@@ -1544,37 +1515,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane))
+		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
 		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_overlay))
+		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.win);
 		return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
 	case V4L2_BUF_TYPE_VBI_OUTPUT:
-		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_vbi_out))
+		if (unlikely(!ops->vidioc_s_fmt_vbi_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.vbi);
 		return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
 	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_out))
+		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.sliced);
 		return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
 	case V4L2_BUF_TYPE_SDR_CAPTURE:
-		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_s_fmt_sdr_cap))
+		if (unlikely(!ops->vidioc_s_fmt_sdr_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.sdr);
 		return ops->vidioc_s_fmt_sdr_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_SDR_OUTPUT:
-		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_s_fmt_sdr_out))
+		if (unlikely(!ops->vidioc_s_fmt_sdr_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.sdr);
 		return ops->vidioc_s_fmt_sdr_out(file, fh, arg);
 	case V4L2_BUF_TYPE_META_CAPTURE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_meta_cap))
+		if (unlikely(!ops->vidioc_s_fmt_meta_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.meta);
 		return ops->vidioc_s_fmt_meta_cap(file, fh, arg);
@@ -1586,19 +1557,16 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
 	struct v4l2_format *p = arg;
-	struct video_device *vfd = video_devdata(file);
-	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
-	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
-	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
-	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
-	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
-	int ret;
+	int ret = check_fmt(file, p->type);
+
+	if (ret)
+		return ret;
 
 	v4l_sanitize_format(p);
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_try_fmt_vid_cap))
+		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
 		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
@@ -1606,27 +1574,27 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane))
+		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
 		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_overlay))
+		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.win);
 		return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
 	case V4L2_BUF_TYPE_VBI_CAPTURE:
-		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_vbi_cap))
+		if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.vbi);
 		return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
-		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_cap))
+		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.sliced);
 		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out))
+		if (unlikely(!ops->vidioc_try_fmt_vid_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
 		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
@@ -1634,37 +1602,37 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane))
+		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
 		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
-		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_overlay))
+		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.win);
 		return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
 	case V4L2_BUF_TYPE_VBI_OUTPUT:
-		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_vbi_out))
+		if (unlikely(!ops->vidioc_try_fmt_vbi_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.vbi);
 		return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
 	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_out))
+		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.sliced);
 		return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
 	case V4L2_BUF_TYPE_SDR_CAPTURE:
-		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_try_fmt_sdr_cap))
+		if (unlikely(!ops->vidioc_try_fmt_sdr_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.sdr);
 		return ops->vidioc_try_fmt_sdr_cap(file, fh, arg);
 	case V4L2_BUF_TYPE_SDR_OUTPUT:
-		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_try_fmt_sdr_out))
+		if (unlikely(!ops->vidioc_try_fmt_sdr_out))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.sdr);
 		return ops->vidioc_try_fmt_sdr_out(file, fh, arg);
 	case V4L2_BUF_TYPE_META_CAPTURE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_meta_cap))
+		if (unlikely(!ops->vidioc_try_fmt_meta_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.meta);
 		return ops->vidioc_try_fmt_meta_cap(file, fh, arg);
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 03/12] v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
  2018-01-26 12:43 ` [PATCH 01/12] vivid: fix module load error when enabling fb and no_error_inj=1 Hans Verkuil
  2018-01-26 12:43 ` [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-26 14:43   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 04/12] v4l2-compat-ioctl32.c: fix the indentation Hans Verkuil
                   ` (8 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The result of the VIDIOC_PREPARE_BUF ioctl was never copied back
to userspace since it was missing in the switch.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index e48d59046086..76ed43e774dd 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -1052,6 +1052,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 		err = put_v4l2_create32(&karg.v2crt, up);
 		break;
 
+	case VIDIOC_PREPARE_BUF:
 	case VIDIOC_QUERYBUF:
 	case VIDIOC_QBUF:
 	case VIDIOC_DQBUF:
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 04/12] v4l2-compat-ioctl32.c: fix the indentation
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (2 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 03/12] v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-26 14:57   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 05/12] v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32 Hans Verkuil
                   ` (7 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The indentation of this source is all over the place. Fix this.
This patch only changes whitespace.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 212 +++++++++++++-------------
 1 file changed, 107 insertions(+), 105 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 76ed43e774dd..83066b21b0b2 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -49,12 +49,12 @@ struct v4l2_window32 {
 static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
 {
 	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) ||
-		get_user(kp->global_alpha, &up->global_alpha))
-			return -EFAULT;
+	    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) ||
+	    get_user(kp->global_alpha, &up->global_alpha))
+		return -EFAULT;
 	if (kp->clipcount > 2048)
 		return -EINVAL;
 	if (kp->clipcount) {
@@ -84,11 +84,11 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
 static int put_v4l2_window32(struct v4l2_window *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) ||
-		put_user(kp->global_alpha, &up->global_alpha))
-			return -EFAULT;
+	    put_user(kp->field, &up->field) ||
+	    put_user(kp->chromakey, &up->chromakey) ||
+	    put_user(kp->clipcount, &up->clipcount) ||
+	    put_user(kp->global_alpha, &up->global_alpha))
+		return -EFAULT;
 	return 0;
 }
 
@@ -100,7 +100,7 @@ static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi
 }
 
 static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
-				struct v4l2_pix_format_mplane __user *up)
+					     struct v4l2_pix_format_mplane __user *up)
 {
 	if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
 		return -EFAULT;
@@ -115,7 +115,7 @@ static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi
 }
 
 static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
-				struct v4l2_pix_format_mplane __user *up)
+					     struct v4l2_pix_format_mplane __user *up)
 {
 	if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
 		return -EFAULT;
@@ -238,7 +238,7 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
 		return get_v4l2_meta_format(&kp->fmt.meta, &up->fmt.meta);
 	default:
 		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
-								kp->type);
+			kp->type);
 		return -EINVAL;
 	}
 }
@@ -287,7 +287,7 @@ static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
 		return put_v4l2_meta_format(&kp->fmt.meta, &up->fmt.meta);
 	default:
 		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
-								kp->type);
+			kp->type);
 		return -EINVAL;
 	}
 }
@@ -321,7 +321,7 @@ static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
 {
 	/* 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))
+	    get_user(kp->index, &up->index))
 		return -EFAULT;
 	return 0;
 }
@@ -329,13 +329,14 @@ static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
 static int put_v4l2_standard32(struct v4l2_standard *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)))
-			return -EFAULT;
+	    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)))
+		return -EFAULT;
 	return 0;
 }
 
@@ -375,14 +376,14 @@ struct v4l2_buffer32 {
 };
 
 static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
-				enum v4l2_memory memory)
+			    enum v4l2_memory memory)
 {
 	void __user *up_pln;
 	compat_long_t p;
 
 	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
-		copy_in_user(&up->data_offset, &up32->data_offset,
-				sizeof(__u32)))
+	    copy_in_user(&up->data_offset, &up32->data_offset,
+			 sizeof(__u32)))
 		return -EFAULT;
 
 	if (memory == V4L2_MEMORY_USERPTR) {
@@ -396,7 +397,7 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 			return -EFAULT;
 	} else {
 		if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
-					sizeof(__u32)))
+				 sizeof(__u32)))
 			return -EFAULT;
 	}
 
@@ -404,23 +405,23 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 }
 
 static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
-				enum v4l2_memory memory)
+			    enum v4l2_memory memory)
 {
 	if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
-		copy_in_user(&up32->data_offset, &up->data_offset,
-				sizeof(__u32)))
+	    copy_in_user(&up32->data_offset, &up->data_offset,
+			 sizeof(__u32)))
 		return -EFAULT;
 
 	/* For MMAP, driver might've set up the offset, so copy it back.
 	 * USERPTR stays the same (was userspace-provided), so no copying. */
 	if (memory == V4L2_MEMORY_MMAP)
 		if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
-					sizeof(__u32)))
+				 sizeof(__u32)))
 			return -EFAULT;
 	/* For DMABUF, driver might've set up the fd, so copy it back. */
 	if (memory == V4L2_MEMORY_DMABUF)
 		if (copy_in_user(&up32->m.fd, &up->m.fd,
-					sizeof(int)))
+				 sizeof(int)))
 			return -EFAULT;
 
 	return 0;
@@ -434,19 +435,19 @@ 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))
-			return -EFAULT;
+	    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))
+		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))
+		    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))
 			return -EFAULT;
 
 	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
@@ -466,7 +467,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 
 		uplane32 = compat_ptr(p);
 		if (!access_ok(VERIFY_READ, uplane32,
-				kp->length * sizeof(struct v4l2_plane32)))
+			       kp->length * sizeof(struct v4l2_plane32)))
 			return -EFAULT;
 
 		/* We don't really care if userspace decides to kill itself
@@ -490,12 +491,12 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 			break;
 		case V4L2_MEMORY_USERPTR:
 			{
-			compat_long_t tmp;
+				compat_long_t tmp;
 
-			if (get_user(tmp, &up->m.userptr))
-				return -EFAULT;
+				if (get_user(tmp, &up->m.userptr))
+					return -EFAULT;
 
-			kp->m.userptr = (unsigned long)compat_ptr(tmp);
+				kp->m.userptr = (unsigned long)compat_ptr(tmp);
 			}
 			break;
 		case V4L2_MEMORY_OVERLAY:
@@ -521,22 +522,23 @@ 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))
-			return -EFAULT;
+	    put_user(kp->index, &up->index) ||
+	    put_user(kp->type, &up->type) ||
+	    put_user(kp->flags, &up->flags) ||
+	    put_user(kp->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))
-			return -EFAULT;
+	    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))
+		return -EFAULT;
 
 	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
 		num_planes = kp->length;
@@ -600,11 +602,11 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
 	u32 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) ||
-		copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
-			return -EFAULT;
+	    get_user(tmp, &up->base) ||
+	    get_user(kp->capability, &up->capability) ||
+	    get_user(kp->flags, &up->flags) ||
+	    copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
+		return -EFAULT;
 	kp->base = (__force void *)compat_ptr(tmp);
 	return 0;
 }
@@ -614,11 +616,11 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
 	u32 tmp = (u32)((unsigned long)kp->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) ||
-		copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
-			return -EFAULT;
+	    put_user(tmp, &up->base) ||
+	    put_user(kp->capability, &up->capability) ||
+	    put_user(kp->flags, &up->flags) ||
+	    copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
+		return -EFAULT;
 	return 0;
 }
 
@@ -694,12 +696,12 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 	compat_caddr_t p;
 
 	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
-		get_user(kp->which, &up->which) ||
-		get_user(kp->count, &up->count) ||
-		get_user(kp->error_idx, &up->error_idx) ||
-		copy_from_user(kp->reserved, up->reserved,
-			       sizeof(kp->reserved)))
-			return -EFAULT;
+	    get_user(kp->which, &up->which) ||
+	    get_user(kp->count, &up->count) ||
+	    get_user(kp->error_idx, &up->error_idx) ||
+	    copy_from_user(kp->reserved, up->reserved,
+			   sizeof(kp->reserved)))
+		return -EFAULT;
 	if (kp->count == 0) {
 		kp->controls = NULL;
 		return 0;
@@ -710,7 +712,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 		return -EFAULT;
 	ucontrols = compat_ptr(p);
 	if (!access_ok(VERIFY_READ, ucontrols,
-			kp->count * sizeof(struct v4l2_ext_control32)))
+		       kp->count * sizeof(struct v4l2_ext_control32)))
 		return -EFAULT;
 	kcontrols = compat_alloc_user_space(kp->count *
 					    sizeof(struct v4l2_ext_control));
@@ -746,11 +748,11 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 	compat_caddr_t p;
 
 	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
-		put_user(kp->which, &up->which) ||
-		put_user(kp->count, &up->count) ||
-		put_user(kp->error_idx, &up->error_idx) ||
-		copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
-			return -EFAULT;
+	    put_user(kp->which, &up->which) ||
+	    put_user(kp->count, &up->count) ||
+	    put_user(kp->error_idx, &up->error_idx) ||
+	    copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
+		return -EFAULT;
 	if (!kp->count)
 		return 0;
 
@@ -758,7 +760,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 		return -EFAULT;
 	ucontrols = compat_ptr(p);
 	if (!access_ok(VERIFY_WRITE, ucontrols,
-			n * sizeof(struct v4l2_ext_control32)))
+		       n * sizeof(struct v4l2_ext_control32)))
 		return -EFAULT;
 
 	while (--n >= 0) {
@@ -796,15 +798,15 @@ struct v4l2_event32 {
 static int put_v4l2_event32(struct v4l2_event *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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
-		put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
-		put_user(kp->id, &up->id) ||
-		copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
-			return -EFAULT;
+	    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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
+	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
+	    put_user(kp->id, &up->id) ||
+	    copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
+		return -EFAULT;
 	return 0;
 }
 
@@ -821,12 +823,12 @@ static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
 	u32 tmp;
 
 	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_edid32)) ||
-		get_user(kp->pad, &up->pad) ||
-		get_user(kp->start_block, &up->start_block) ||
-		get_user(kp->blocks, &up->blocks) ||
-		get_user(tmp, &up->edid) ||
-		copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
-			return -EFAULT;
+	    get_user(kp->pad, &up->pad) ||
+	    get_user(kp->start_block, &up->start_block) ||
+	    get_user(kp->blocks, &up->blocks) ||
+	    get_user(tmp, &up->edid) ||
+	    copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
+		return -EFAULT;
 	kp->edid = (__force u8 *)compat_ptr(tmp);
 	return 0;
 }
@@ -836,12 +838,12 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
 	u32 tmp = (u32)((unsigned long)kp->edid);
 
 	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_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(up->reserved, kp->reserved, sizeof(up->reserved)))
-			return -EFAULT;
+	    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(up->reserved, kp->reserved, sizeof(up->reserved)))
+		return -EFAULT;
 	return 0;
 }
 
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 05/12] v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (3 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 04/12] v4l2-compat-ioctl32.c: fix the indentation Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-26 15:20   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 06/12] v4l2-compat-ioctl32.c: avoid sizeof(type) Hans Verkuil
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

These helper functions do not really help. Move the code to the
__get/put_v4l2_format32 functions.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 124 +++++---------------------
 1 file changed, 24 insertions(+), 100 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 83066b21b0b2..2dd9b42d5859 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -92,92 +92,6 @@ static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
 	return 0;
 }
 
-static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
-{
-	if (copy_from_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,
-					     struct v4l2_pix_format_mplane __user *up)
-{
-	if (copy_from_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)
-{
-	if (copy_to_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,
-					     struct v4l2_pix_format_mplane __user *up)
-{
-	if (copy_to_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)
-{
-	if (copy_from_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)
-{
-	if (copy_to_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)
-{
-	if (copy_from_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)
-{
-	if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
-		return -EFAULT;
-	return 0;
-}
-
-static inline int get_v4l2_sdr_format(struct v4l2_sdr_format *kp, struct v4l2_sdr_format __user *up)
-{
-	if (copy_from_user(kp, up, sizeof(struct v4l2_sdr_format)))
-		return -EFAULT;
-	return 0;
-}
-
-static inline int put_v4l2_sdr_format(struct v4l2_sdr_format *kp, struct v4l2_sdr_format __user *up)
-{
-	if (copy_to_user(up, kp, sizeof(struct v4l2_sdr_format)))
-		return -EFAULT;
-	return 0;
-}
-
-static inline int get_v4l2_meta_format(struct v4l2_meta_format *kp, struct v4l2_meta_format __user *up)
-{
-	if (copy_from_user(kp, up, sizeof(struct v4l2_meta_format)))
-		return -EFAULT;
-	return 0;
-}
-
-static inline int put_v4l2_meta_format(struct v4l2_meta_format *kp, struct v4l2_meta_format __user *up)
-{
-	if (copy_to_user(up, kp, sizeof(struct v4l2_meta_format)))
-		return -EFAULT;
-	return 0;
-}
-
 struct v4l2_format32 {
 	__u32	type;	/* enum v4l2_buf_type */
 	union {
@@ -217,25 +131,30 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
 	switch (kp->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+		return copy_from_user(&kp->fmt.pix, &up->fmt.pix,
+				      sizeof(kp->fmt.pix)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
-						  &up->fmt.pix_mp);
+		return copy_from_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
+				      sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 		return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
 	case V4L2_BUF_TYPE_VBI_CAPTURE:
 	case V4L2_BUF_TYPE_VBI_OUTPUT:
-		return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+		return copy_from_user(&kp->fmt.vbi, &up->fmt.vbi,
+				      sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-		return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+		return copy_from_user(&kp->fmt.sliced, &up->fmt.sliced,
+				      sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_SDR_CAPTURE:
 	case V4L2_BUF_TYPE_SDR_OUTPUT:
-		return get_v4l2_sdr_format(&kp->fmt.sdr, &up->fmt.sdr);
+		return copy_from_user(&kp->fmt.sdr, &up->fmt.sdr,
+				      sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_META_CAPTURE:
-		return get_v4l2_meta_format(&kp->fmt.meta, &up->fmt.meta);
+		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
+				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
 	default:
 		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
 			kp->type);
@@ -266,25 +185,30 @@ static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
 	switch (kp->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
+		return copy_to_user(&up->fmt.pix, &kp->fmt.pix,
+				    sizeof(kp->fmt.pix)) ?  -EFAULT : 0;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
-						  &up->fmt.pix_mp);
+		return copy_to_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
+				    sizeof(kp->fmt.pix_mp)) ?  -EFAULT : 0;
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 		return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
 	case V4L2_BUF_TYPE_VBI_CAPTURE:
 	case V4L2_BUF_TYPE_VBI_OUTPUT:
-		return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
+		return copy_to_user(&up->fmt.vbi, &kp->fmt.vbi,
+				    sizeof(kp->fmt.vbi)) ?  -EFAULT : 0;
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-		return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
+		return copy_to_user(&up->fmt.sliced, &kp->fmt.sliced,
+				    sizeof(kp->fmt.sliced)) ?  -EFAULT : 0;
 	case V4L2_BUF_TYPE_SDR_CAPTURE:
 	case V4L2_BUF_TYPE_SDR_OUTPUT:
-		return put_v4l2_sdr_format(&kp->fmt.sdr, &up->fmt.sdr);
+		return copy_to_user(&up->fmt.sdr, &kp->fmt.sdr,
+				    sizeof(kp->fmt.sdr)) ?  -EFAULT : 0;
 	case V4L2_BUF_TYPE_META_CAPTURE:
-		return put_v4l2_meta_format(&kp->fmt.meta, &up->fmt.meta);
+		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
+				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
 	default:
 		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
 			kp->type);
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 06/12] v4l2-compat-ioctl32.c: avoid sizeof(type)
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (4 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 05/12] v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32 Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-26 15:35   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 07/12] v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32 Hans Verkuil
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Instead of doing sizeof(struct foo) use sizeof(*up). There even were
cases where 4 * sizeof(__u32) was used instead of sizeof(kp->reserved),
which is very dangerous when the size of the reserved array changes.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 77 ++++++++++++---------------
 1 file changed, 35 insertions(+), 42 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 2dd9b42d5859..809448d1b7db 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -48,7 +48,7 @@ struct v4l2_window32 {
 
 static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
 {
-	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
 	    copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
 	    get_user(kp->field, &up->field) ||
 	    get_user(kp->chromakey, &up->chromakey) ||
@@ -66,7 +66,7 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
 		if (get_user(p, &up->clips))
 			return -EFAULT;
 		uclips = compat_ptr(p);
-		kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
+		kclips = compat_alloc_user_space(n * sizeof(*kclips));
 		kp->clips = kclips;
 		while (--n >= 0) {
 			if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
@@ -164,14 +164,14 @@ 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)
 {
-	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)))
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)))
 		return -EFAULT;
 	return __get_v4l2_format32(kp, up);
 }
 
 static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
 {
-	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
 	    copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format)))
 		return -EFAULT;
 	return __get_v4l2_format32(&kp->format, &up->format);
@@ -218,14 +218,14 @@ 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)
 {
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)))
+	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
 		return -EFAULT;
 	return __put_v4l2_format32(kp, up);
 }
 
 static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
 {
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
+	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
 	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
 	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
 		return -EFAULT;
@@ -244,7 +244,7 @@ struct v4l2_standard32 {
 static int get_v4l2_standard32(struct v4l2_standard *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)) ||
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
 	    get_user(kp->index, &up->index))
 		return -EFAULT;
 	return 0;
@@ -252,14 +252,14 @@ static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
 
 static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
 {
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
+	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
 	    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)))
+	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
 		return -EFAULT;
 	return 0;
 }
@@ -307,7 +307,7 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 
 	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
 	    copy_in_user(&up->data_offset, &up32->data_offset,
-			 sizeof(__u32)))
+			 sizeof(up->data_offset)))
 		return -EFAULT;
 
 	if (memory == V4L2_MEMORY_USERPTR) {
@@ -317,11 +317,11 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 		if (put_user((unsigned long)up_pln, &up->m.userptr))
 			return -EFAULT;
 	} else if (memory == V4L2_MEMORY_DMABUF) {
-		if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(int)))
+		if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(up32->m.fd)))
 			return -EFAULT;
 	} else {
 		if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
-				 sizeof(__u32)))
+				 sizeof(up32->m.mem_offset)))
 			return -EFAULT;
 	}
 
@@ -333,19 +333,19 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 {
 	if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
 	    copy_in_user(&up32->data_offset, &up->data_offset,
-			 sizeof(__u32)))
+			 sizeof(up->data_offset)))
 		return -EFAULT;
 
 	/* For MMAP, driver might've set up the offset, so copy it back.
 	 * USERPTR stays the same (was userspace-provided), so no copying. */
 	if (memory == V4L2_MEMORY_MMAP)
 		if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
-				 sizeof(__u32)))
+				 sizeof(up->m.mem_offset)))
 			return -EFAULT;
 	/* For DMABUF, driver might've set up the fd, so copy it back. */
 	if (memory == V4L2_MEMORY_DMABUF)
 		if (copy_in_user(&up32->m.fd, &up->m.fd,
-				 sizeof(int)))
+				 sizeof(up->m.fd)))
 			return -EFAULT;
 
 	return 0;
@@ -358,7 +358,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 	compat_caddr_t p;
 	int ret;
 
-	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
 	    get_user(kp->index, &up->index) ||
 	    get_user(kp->type, &up->type) ||
 	    get_user(kp->flags, &up->flags) ||
@@ -370,8 +370,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 		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))
+		    get_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec))
 			return -EFAULT;
 
 	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
@@ -391,13 +390,12 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 
 		uplane32 = compat_ptr(p);
 		if (!access_ok(VERIFY_READ, uplane32,
-			       kp->length * sizeof(struct v4l2_plane32)))
+			       kp->length * sizeof(*uplane32)))
 			return -EFAULT;
 
 		/* We don't really care if userspace decides to kill itself
 		 * by passing a very big num_planes value */
-		uplane = compat_alloc_user_space(kp->length *
-						 sizeof(struct v4l2_plane));
+		uplane = compat_alloc_user_space(kp->length * sizeof(*uplane));
 		kp->m.planes = (__force struct v4l2_plane *)uplane;
 
 		for (num_planes = 0; num_planes < kp->length; num_planes++) {
@@ -445,7 +443,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 	int num_planes;
 	int ret;
 
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
+	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
 	    put_user(kp->index, &up->index) ||
 	    put_user(kp->type, &up->type) ||
 	    put_user(kp->flags, &up->flags) ||
@@ -456,8 +454,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 	    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)) ||
+	    copy_to_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
 	    put_user(kp->sequence, &up->sequence) ||
 	    put_user(kp->reserved2, &up->reserved2) ||
 	    put_user(kp->reserved, &up->reserved) ||
@@ -525,7 +522,7 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
 {
 	u32 tmp;
 
-	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
 	    get_user(tmp, &up->base) ||
 	    get_user(kp->capability, &up->capability) ||
 	    get_user(kp->flags, &up->flags) ||
@@ -539,7 +536,7 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
 {
 	u32 tmp = (u32)((unsigned long)kp->base);
 
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
+	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
 	    put_user(tmp, &up->base) ||
 	    put_user(kp->capability, &up->capability) ||
 	    put_user(kp->flags, &up->flags) ||
@@ -564,14 +561,14 @@ struct v4l2_input32 {
    Otherwise it is identical to the 32-bit version. */
 static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
 {
-	if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
+	if (copy_from_user(kp, up, sizeof(*up)))
 		return -EFAULT;
 	return 0;
 }
 
 static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
 {
-	if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
+	if (copy_to_user(up, kp, sizeof(*up)))
 		return -EFAULT;
 	return 0;
 }
@@ -619,12 +616,11 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 	unsigned int n;
 	compat_caddr_t p;
 
-	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
 	    get_user(kp->which, &up->which) ||
 	    get_user(kp->count, &up->count) ||
 	    get_user(kp->error_idx, &up->error_idx) ||
-	    copy_from_user(kp->reserved, up->reserved,
-			   sizeof(kp->reserved)))
+	    copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
 		return -EFAULT;
 	if (kp->count == 0) {
 		kp->controls = NULL;
@@ -635,11 +631,9 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 	if (get_user(p, &up->controls))
 		return -EFAULT;
 	ucontrols = compat_ptr(p);
-	if (!access_ok(VERIFY_READ, ucontrols,
-		       kp->count * sizeof(struct v4l2_ext_control32)))
+	if (!access_ok(VERIFY_READ, ucontrols, kp->count * sizeof(*ucontrols)))
 		return -EFAULT;
-	kcontrols = compat_alloc_user_space(kp->count *
-					    sizeof(struct v4l2_ext_control));
+	kcontrols = compat_alloc_user_space(kp->count * sizeof(*kcontrols));
 	kp->controls = (__force struct v4l2_ext_control *)kcontrols;
 	for (n = 0; n < kp->count; n++) {
 		u32 id;
@@ -671,7 +665,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 	int n = kp->count;
 	compat_caddr_t p;
 
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
+	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
 	    put_user(kp->which, &up->which) ||
 	    put_user(kp->count, &up->count) ||
 	    put_user(kp->error_idx, &up->error_idx) ||
@@ -683,8 +677,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 	if (get_user(p, &up->controls))
 		return -EFAULT;
 	ucontrols = compat_ptr(p);
-	if (!access_ok(VERIFY_WRITE, ucontrols,
-		       n * sizeof(struct v4l2_ext_control32)))
+	if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(*ucontrols)))
 		return -EFAULT;
 
 	while (--n >= 0) {
@@ -721,7 +714,7 @@ struct v4l2_event32 {
 
 static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
 {
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
+	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
 	    put_user(kp->type, &up->type) ||
 	    copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
 	    put_user(kp->pending, &up->pending) ||
@@ -729,7 +722,7 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u
 	    put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
 	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
 	    put_user(kp->id, &up->id) ||
-	    copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
+	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
 		return -EFAULT;
 	return 0;
 }
@@ -746,7 +739,7 @@ static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
 {
 	u32 tmp;
 
-	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_edid32)) ||
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
 	    get_user(kp->pad, &up->pad) ||
 	    get_user(kp->start_block, &up->start_block) ||
 	    get_user(kp->blocks, &up->blocks) ||
@@ -761,7 +754,7 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
 {
 	u32 tmp = (u32)((unsigned long)kp->edid);
 
-	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_edid32)) ||
+	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
 	    put_user(kp->pad, &up->pad) ||
 	    put_user(kp->start_block, &up->start_block) ||
 	    put_user(kp->blocks, &up->blocks) ||
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 07/12] v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (5 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 06/12] v4l2-compat-ioctl32.c: avoid sizeof(type) Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-26 16:15   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 08/12] v4l2-compat-ioctl32.c: fix ctrl_is_pointer Hans Verkuil
                   ` (4 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

The struct v4l2_plane32 should set m.userptr as well. The same
happens in v4l2_buffer32 and v4l2-compliance tests for this.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 47 ++++++++++++++++-----------
 1 file changed, 28 insertions(+), 19 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 809448d1b7db..da8a56818a18 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -310,19 +310,24 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 			 sizeof(up->data_offset)))
 		return -EFAULT;
 
-	if (memory == V4L2_MEMORY_USERPTR) {
+	switch (memory) {
+	case V4L2_MEMORY_MMAP:
+	case V4L2_MEMORY_OVERLAY:
+		if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
+				 sizeof(up32->m.mem_offset)))
+			return -EFAULT;
+		break;
+	case 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))
 			return -EFAULT;
-	} else if (memory == V4L2_MEMORY_DMABUF) {
+		break;
+	case V4L2_MEMORY_DMABUF:
 		if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(up32->m.fd)))
 			return -EFAULT;
-	} else {
-		if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
-				 sizeof(up32->m.mem_offset)))
-			return -EFAULT;
+		break;
 	}
 
 	return 0;
@@ -331,22 +336,32 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
 			    enum v4l2_memory memory)
 {
+	unsigned long p;
+
 	if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
 	    copy_in_user(&up32->data_offset, &up->data_offset,
 			 sizeof(up->data_offset)))
 		return -EFAULT;
 
-	/* For MMAP, driver might've set up the offset, so copy it back.
-	 * USERPTR stays the same (was userspace-provided), so no copying. */
-	if (memory == V4L2_MEMORY_MMAP)
+	switch (memory) {
+	case V4L2_MEMORY_MMAP:
+	case V4L2_MEMORY_OVERLAY:
 		if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
 				 sizeof(up->m.mem_offset)))
 			return -EFAULT;
-	/* For DMABUF, driver might've set up the fd, so copy it back. */
-	if (memory == V4L2_MEMORY_DMABUF)
+		break;
+	case V4L2_MEMORY_USERPTR:
+		if (get_user(p, &up->m.userptr) ||
+		    put_user((compat_long_t)ptr_to_compat((__force void *)p),
+			     &up32->m.userptr))
+			return -EFAULT;
+		break;
+	case V4L2_MEMORY_DMABUF:
 		if (copy_in_user(&up32->m.fd, &up->m.fd,
 				 sizeof(up->m.fd)))
 			return -EFAULT;
+		break;
+	}
 
 	return 0;
 }
@@ -408,6 +423,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 	} else {
 		switch (kp->memory) {
 		case V4L2_MEMORY_MMAP:
+		case V4L2_MEMORY_OVERLAY:
 			if (get_user(kp->m.offset, &up->m.offset))
 				return -EFAULT;
 			break;
@@ -421,10 +437,6 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 				kp->m.userptr = (unsigned long)compat_ptr(tmp);
 			}
 			break;
-		case V4L2_MEMORY_OVERLAY:
-			if (get_user(kp->m.offset, &up->m.offset))
-				return -EFAULT;
-			break;
 		case V4L2_MEMORY_DMABUF:
 			if (get_user(kp->m.fd, &up->m.fd))
 				return -EFAULT;
@@ -481,6 +493,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 	} else {
 		switch (kp->memory) {
 		case V4L2_MEMORY_MMAP:
+		case V4L2_MEMORY_OVERLAY:
 			if (put_user(kp->m.offset, &up->m.offset))
 				return -EFAULT;
 			break;
@@ -488,10 +501,6 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 			if (put_user(kp->m.userptr, &up->m.userptr))
 				return -EFAULT;
 			break;
-		case V4L2_MEMORY_OVERLAY:
-			if (put_user(kp->m.offset, &up->m.offset))
-				return -EFAULT;
-			break;
 		case V4L2_MEMORY_DMABUF:
 			if (put_user(kp->m.fd, &up->m.fd))
 				return -EFAULT;
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 08/12] v4l2-compat-ioctl32.c: fix ctrl_is_pointer
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (6 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 07/12] v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32 Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-27 23:18   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 09/12] v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32 Hans Verkuil
                   ` (3 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

ctrl_is_pointer just hardcoded two known string controls, but that
caused problems when using e.g. custom controls that use a pointer
for the payload.

Reimplement this function: it now finds the v4l2_ctrl (if the driver
uses the control framework) or it calls vidioc_query_ext_ctrl (if the
driver implements that directly).

In both cases it can now check if the control is a pointer control
or not.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 56 ++++++++++++++++++---------
 1 file changed, 37 insertions(+), 19 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index da8a56818a18..cf3d4bfcd132 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -18,6 +18,8 @@
 #include <linux/videodev2.h>
 #include <linux/v4l2-subdev.h>
 #include <media/v4l2-dev.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
 #include <media/v4l2-ioctl.h>
 
 static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
@@ -601,24 +603,38 @@ struct v4l2_ext_control32 {
 	};
 } __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)
+/* Return true if this control is a pointer type. */
+static inline bool ctrl_is_pointer(struct file *file, u32 id)
 {
-	switch (id) {
-	case V4L2_CID_RDS_TX_PS_NAME:
-	case V4L2_CID_RDS_TX_RADIO_TEXT:
-		return 1;
-	default:
-		return 0;
+	struct video_device *vdev = video_devdata(file);
+	struct v4l2_fh *fh = NULL;
+	struct v4l2_ctrl_handler *hdl = NULL;
+	struct v4l2_query_ext_ctrl qec = { id };
+	const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
+	int ret = -ENOTTY;
+
+	if (test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags))
+		fh = file->private_data;
+
+	if (fh && fh->ctrl_handler)
+		hdl = fh->ctrl_handler;
+	else if (vdev->ctrl_handler)
+		hdl = vdev->ctrl_handler;
+
+	if (hdl) {
+		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, id);
+
+		return ctrl && ctrl->is_ptr;
 	}
+
+	if (ops->vidioc_query_ext_ctrl)
+		ret = ops->vidioc_query_ext_ctrl(file, fh, &qec);
+	return !ret && (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
 }
 
-static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
+static int get_v4l2_ext_controls32(struct file *file,
+				   struct v4l2_ext_controls *kp,
+				   struct v4l2_ext_controls32 __user *up)
 {
 	struct v4l2_ext_control32 __user *ucontrols;
 	struct v4l2_ext_control __user *kcontrols;
@@ -651,7 +667,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 			return -EFAULT;
 		if (get_user(id, &kcontrols->id))
 			return -EFAULT;
-		if (ctrl_is_pointer(id)) {
+		if (ctrl_is_pointer(file, id)) {
 			void __user *s;
 
 			if (get_user(p, &ucontrols->string))
@@ -666,7 +682,9 @@ 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 file *file,
+				   struct v4l2_ext_controls *kp,
+				   struct v4l2_ext_controls32 __user *up)
 {
 	struct v4l2_ext_control32 __user *ucontrols;
 	struct v4l2_ext_control __user *kcontrols =
@@ -698,7 +716,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
 		/* 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(id))
+		if (ctrl_is_pointer(file, id))
 			size -= sizeof(ucontrols->value64);
 		if (copy_in_user(ucontrols, kcontrols, size))
 			return -EFAULT;
@@ -912,7 +930,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:
-		err = get_v4l2_ext_controls32(&karg.v2ecs, up);
+		err = get_v4l2_ext_controls32(file, &karg.v2ecs, up);
 		compatible_arg = 0;
 		break;
 	case VIDIOC_DQEVENT:
@@ -939,7 +957,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(file, &karg.v2ecs, up))
 			err = -EFAULT;
 		break;
 	case VIDIOC_S_EDID:
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 09/12] v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (7 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 08/12] v4l2-compat-ioctl32.c: fix ctrl_is_pointer Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-29  9:47   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 10/12] v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type Hans Verkuil
                   ` (2 subsequent siblings)
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

put_v4l2_window32() didn't copy back the clip list to userspace.
Drivers can update the clip rectangles, so this should be done.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 58 ++++++++++++++++++---------
 1 file changed, 39 insertions(+), 19 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index cf3d4bfcd132..6e3fbbde2c9c 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -50,6 +50,11 @@ struct v4l2_window32 {
 
 static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
 {
+	struct v4l2_clip32 __user *uclips;
+	struct v4l2_clip __user *kclips;
+	compat_caddr_t p;
+	u32 n;
+
 	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
 	    copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
 	    get_user(kp->field, &up->field) ||
@@ -59,38 +64,53 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
 		return -EFAULT;
 	if (kp->clipcount > 2048)
 		return -EINVAL;
-	if (kp->clipcount) {
-		struct v4l2_clip32 __user *uclips;
-		struct v4l2_clip __user *kclips;
-		int n = kp->clipcount;
-		compat_caddr_t p;
+	if (!kp->clipcount) {
+		kp->clips = NULL;
+		return 0;
+	}
 
-		if (get_user(p, &up->clips))
+	n = kp->clipcount;
+	if (get_user(p, &up->clips))
+		return -EFAULT;
+	uclips = compat_ptr(p);
+	kclips = compat_alloc_user_space(n * sizeof(*kclips));
+	kp->clips = kclips;
+	while (n--) {
+		if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
 			return -EFAULT;
-		uclips = compat_ptr(p);
-		kclips = compat_alloc_user_space(n * sizeof(*kclips));
-		kp->clips = kclips;
-		while (--n >= 0) {
-			if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
-				return -EFAULT;
-			if (put_user(n ? kclips + 1 : NULL, &kclips->next))
-				return -EFAULT;
-			uclips += 1;
-			kclips += 1;
-		}
-	} else
-		kp->clips = NULL;
+		if (put_user(n ? kclips + 1 : NULL, &kclips->next))
+			return -EFAULT;
+		uclips++;
+		kclips++;
+	}
 	return 0;
 }
 
 static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
 {
+	struct v4l2_clip __user *kclips = kp->clips;
+	struct v4l2_clip32 __user *uclips;
+	int n = kp->clipcount;
+	compat_caddr_t p;
+
 	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) ||
 	    put_user(kp->global_alpha, &up->global_alpha))
 		return -EFAULT;
+	if (!kp->clipcount)
+		return 0;
+
+	if (get_user(p, &up->clips))
+		return -EFAULT;
+	uclips = compat_ptr(p);
+	while (n--) {
+		if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
+			return -EFAULT;
+		uclips++;
+		kclips++;
+	}
 	return 0;
 }
 
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 10/12] v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (8 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 09/12] v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32 Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-29  9:48   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 11/12] v4l2-compat-ioctl32.c: don't copy back the result for certain errors Hans Verkuil
  2018-01-26 12:43 ` [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32 Hans Verkuil
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

There is nothing wrong with using an unknown buffer type. So
stop spamming the kernel log whenever this happens. The kernel
will just return -EINVAL to signal this.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 6e3fbbde2c9c..790473b45a21 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -178,8 +178,6 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
 		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
 				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
 	default:
-		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
-			kp->type);
 		return -EINVAL;
 	}
 }
@@ -232,8 +230,6 @@ static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
 		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
 				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
 	default:
-		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
-			kp->type);
 		return -EINVAL;
 	}
 }
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 11/12] v4l2-compat-ioctl32.c: don't copy back the result for certain errors
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (9 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 10/12] v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-29  9:56   ` Sakari Ailus
  2018-01-26 12:43 ` [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32 Hans Verkuil
  11 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

Some ioctls need to copy back the result even if the ioctl returned
an error. However, don't do this for the error codes -ENOTTY, -EFAULT
and -ENOIOCTLCMD. It makes no sense in those cases.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 790473b45a21..2aa9b43daf60 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -966,6 +966,9 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
 		set_fs(old_fs);
 	}
 
+	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
+		return err;
+
 	/* Special case: even after an error we need to put the
 	   results back for these ioctls since the error_idx will
 	   contain information on which control failed. */
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32
  2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
                   ` (10 preceding siblings ...)
  2018-01-26 12:43 ` [PATCH 11/12] v4l2-compat-ioctl32.c: don't copy back the result for certain errors Hans Verkuil
@ 2018-01-26 12:43 ` Hans Verkuil
  2018-01-29 15:14   ` Mauro Carvalho Chehab
  2018-01-29 17:06   ` Sakari Ailus
  11 siblings, 2 replies; 38+ messages in thread
From: Hans Verkuil @ 2018-01-26 12:43 UTC (permalink / raw)
  To: linux-media; +Cc: Daniel Mentz, Hans Verkuil

From: Daniel Mentz <danielmentz@google.com>

The 32-bit compat v4l2 ioctl is implemented based on its 64-bit
equivalent. It converts 32-bit data structures into its 64-bit
equivalents and needs to provide the data to the 64-bit ioctl in user
space memory which is commonly allocated using
compat_alloc_user_space(). However, due to how that function is
implemented, it can only be called a single time for every syscall
invocation.  Supposedly to avoid this limitation, the existing code uses
a mix of memory from the kernel stack and memory allocated through
compat_alloc_user_space(). Under normal circumstances, this would not
work, because the 64-bit ioctl expects all pointers to point to user
space memory. As a workaround, set_fs(KERNEL_DS) is called to
temporarily disable this extra safety check and allow kernel pointers.
However, this might introduce a security vulnerability: The
result of the 32-bit to 64-bit conversion is writeable by user space
because the output buffer has been allocated via
compat_alloc_user_space(). A malicious user space process could then
manipulate pointers inside this output buffer, and due to the previous
set_fs(KERNEL_DS) call, functions like get_user() or put_user() no longer
prevent kernel memory access.

The new approach is to pre-calculate the total amount of user space
memory that is needed, allocate it using compat_alloc_user_space() and
then divide up the allocated memory to accommodate all data structures
that need to be converted.

An alternative approach would have been to retain the union type karg
that they allocated on the kernel stack in do_video_ioctl(), copy all
data from user space into karg and then back to user space. However,
we decided against this approach because it does not align with other
compat syscall implementations. Instead, we tried to replicate the
get_user/put_user pairs as found in other places in the kernel:

if (get_user(clipcount, &up->clipcount) ||
    put_user(clipcount, &kp->clipcount)) return -EFAULT;

Notes from hans.verkuil@cisco.com:

This patch was taken from
https://github.com/LineageOS/android_kernel_samsung_apq8084/commit/97b733953c06e4f0398ade18850f0817778255f7

Clearly nobody could be bothered to upstream this patch or at minimum
tell us :-( We only heard about this a week ago.

This patch was rebased and cleaned up. Compared to the original I
also swapped the order of the convert_in_user arguments so that they
matched copy_in_user. It was hard to review otherwise. I also replaced
the ALLOC_USER_SPACE/ALLOC_AND_GET by a normal function.

Signed-off-by: Daniel Mentz <danielmentz@google.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 700 ++++++++++++++++----------
 1 file changed, 448 insertions(+), 252 deletions(-)

diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
index 2aa9b43daf60..27a5a0961cbd 100644
--- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
+++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
@@ -22,6 +22,14 @@
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-ioctl.h>
 
+/* Use the same argument order as copy_in_user */
+#define convert_in_user(to, from)			\
+({							\
+	typeof(*from) val;				\
+							\
+	get_user(val, from) || put_user(val, to);	\
+})
+
 static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
 	long ret = -ENOIOCTLCMD;
@@ -48,37 +56,41 @@ struct v4l2_window32 {
 	__u8                    global_alpha;
 };
 
-static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
+static int get_v4l2_window32(struct v4l2_window __user *kp,
+			     struct v4l2_window32 __user *up,
+			     void __user *aux_buf, int aux_space)
 {
 	struct v4l2_clip32 __user *uclips;
 	struct v4l2_clip __user *kclips;
 	compat_caddr_t p;
-	u32 n;
+	u32 clipcount;
 
 	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
-	    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) ||
-	    get_user(kp->global_alpha, &up->global_alpha))
+	    copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
+	    convert_in_user(&kp->field, &up->field) ||
+	    convert_in_user(&kp->chromakey, &up->chromakey) ||
+	    convert_in_user(&kp->global_alpha, &up->global_alpha) ||
+	    get_user(clipcount, &up->clipcount) ||
+	    put_user(clipcount, &kp->clipcount))
 		return -EFAULT;
-	if (kp->clipcount > 2048)
+	if (clipcount > 2048)
 		return -EINVAL;
-	if (!kp->clipcount) {
-		kp->clips = NULL;
-		return 0;
-	}
+	if (!clipcount)
+		return put_user(NULL, &kp->clips) ? -EFAULT : 0;
 
-	n = kp->clipcount;
 	if (get_user(p, &up->clips))
 		return -EFAULT;
 	uclips = compat_ptr(p);
-	kclips = compat_alloc_user_space(n * sizeof(*kclips));
-	kp->clips = kclips;
-	while (n--) {
+	if (aux_space < clipcount * sizeof(*kclips))
+		return -EFAULT;
+	kclips = aux_buf;
+	if (put_user(kclips, &kp->clips))
+		return -EFAULT;
+
+	while (clipcount--) {
 		if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
 			return -EFAULT;
-		if (put_user(n ? kclips + 1 : NULL, &kclips->next))
+		if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next))
 			return -EFAULT;
 		uclips++;
 		kclips++;
@@ -86,26 +98,28 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
 	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)
 {
 	struct v4l2_clip __user *kclips = kp->clips;
 	struct v4l2_clip32 __user *uclips;
-	int n = kp->clipcount;
 	compat_caddr_t p;
-
-	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) ||
-	    put_user(kp->global_alpha, &up->global_alpha))
+	u32 clipcount;
+
+	if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
+	    convert_in_user(&up->field, &kp->field) ||
+	    convert_in_user(&up->chromakey, &kp->chromakey) ||
+	    convert_in_user(&up->global_alpha, &kp->global_alpha) ||
+	    get_user(clipcount, &kp->clipcount) ||
+	    put_user(clipcount, &up->clipcount))
 		return -EFAULT;
-	if (!kp->clipcount)
+	if (!clipcount)
 		return 0;
 
 	if (get_user(p, &up->clips))
 		return -EFAULT;
 	uclips = compat_ptr(p);
-	while (n--) {
+	while (clipcount--) {
 		if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
 			return -EFAULT;
 		uclips++;
@@ -145,107 +159,161 @@ 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: {
+		u32 clipcount;
+
+		if (get_user(clipcount, &up->fmt.win.clipcount))
+			return -EFAULT;
+		if (clipcount > 2048)
+			return -EINVAL;
+		return clipcount * sizeof(struct v4l2_clip);
+	}
+	default:
+		return 0;
+	}
+}
+
+static int bufsize_v4l2_format32(struct v4l2_format32 __user *up)
+{
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)))
+		return -EFAULT;
+	return __bufsize_v4l2_format32(up);
+}
+
+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 copy_from_user(&kp->fmt.pix, &up->fmt.pix,
-				      sizeof(kp->fmt.pix)) ? -EFAULT : 0;
+		return copy_in_user(&kp->fmt.pix, &up->fmt.pix,
+				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		return copy_from_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
-				      sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
+		return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
+				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
 	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 copy_from_user(&kp->fmt.vbi, &up->fmt.vbi,
-				      sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
+		return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi,
+				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-		return copy_from_user(&kp->fmt.sliced, &up->fmt.sliced,
-				      sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
+		return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced,
+				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_SDR_CAPTURE:
 	case V4L2_BUF_TYPE_SDR_OUTPUT:
-		return copy_from_user(&kp->fmt.sdr, &up->fmt.sdr,
-				      sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
+		return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr,
+				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_META_CAPTURE:
-		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
-				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
+		return copy_in_user(&kp->fmt.meta, &up->fmt.meta,
+				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __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(*up)))
+		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(*up)))
 		return -EFAULT;
-	return __get_v4l2_format32(kp, up);
+	return __bufsize_v4l2_format32(&up->format);
 }
 
-static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
+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(*up)) ||
-	    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)
 {
-	if (put_user(kp->type, &up->type))
+	u32 type;
+
+	if (get_user(type, &kp->type))
 		return -EFAULT;
 
-	switch (kp->type) {
+	switch (type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
-		return copy_to_user(&up->fmt.pix, &kp->fmt.pix,
-				    sizeof(kp->fmt.pix)) ?  -EFAULT : 0;
+		return copy_in_user(&up->fmt.pix, &kp->fmt.pix,
+				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
-		return copy_to_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
-				    sizeof(kp->fmt.pix_mp)) ?  -EFAULT : 0;
+		return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
+				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 		return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
 	case V4L2_BUF_TYPE_VBI_CAPTURE:
 	case V4L2_BUF_TYPE_VBI_OUTPUT:
-		return copy_to_user(&up->fmt.vbi, &kp->fmt.vbi,
-				    sizeof(kp->fmt.vbi)) ?  -EFAULT : 0;
+		return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi,
+				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
-		return copy_to_user(&up->fmt.sliced, &kp->fmt.sliced,
-				    sizeof(kp->fmt.sliced)) ?  -EFAULT : 0;
+		return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced,
+				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_SDR_CAPTURE:
 	case V4L2_BUF_TYPE_SDR_OUTPUT:
-		return copy_to_user(&up->fmt.sdr, &kp->fmt.sdr,
-				    sizeof(kp->fmt.sdr)) ?  -EFAULT : 0;
+		return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr,
+				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
 	case V4L2_BUF_TYPE_META_CAPTURE:
-		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
-				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
+		return copy_in_user(&up->fmt.meta, &kp->fmt.meta,
+				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
 	default:
 		return -EINVAL;
 	}
 }
 
-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(*up)))
 		return -EFAULT;
 	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(*up)) ||
-	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
-	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
+	    copy_in_user(up, kp,
+			 offsetof(struct v4l2_create_buffers32, format)) ||
+	    copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
 		return -EFAULT;
 	return __put_v4l2_format32(&kp->format, &up->format);
 }
@@ -259,25 +327,27 @@ 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(*up)) ||
-	    get_user(kp->index, &up->index))
+	    convert_in_user(&kp->index, &up->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(*up)) ||
-	    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, sizeof(kp->reserved)))
+	    convert_in_user(&up->index, &kp->index) ||
+	    copy_in_user(&up->id, &kp->id, sizeof(up->id)) ||
+	    copy_in_user(up->name, kp->name, 24) ||
+	    copy_in_user(&up->frameperiod, &kp->frameperiod,
+			 sizeof(up->frameperiod)) ||
+	    convert_in_user(&up->framelines, &kp->framelines) ||
+	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
 		return -EFAULT;
 	return 0;
 }
@@ -317,10 +387,10 @@ struct v4l2_buffer32 {
 	__u32			reserved;
 };
 
-static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
+static int get_v4l2_plane32(struct v4l2_plane __user *up,
+			    struct v4l2_plane32 __user *up32,
 			    enum v4l2_memory memory)
 {
-	void __user *up_pln;
 	compat_long_t p;
 
 	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
@@ -336,10 +406,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 			return -EFAULT;
 		break;
 	case 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;
 		break;
 	case V4L2_MEMORY_DMABUF:
@@ -351,7 +419,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 	return 0;
 }
 
-static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
+static int put_v4l2_plane32(struct v4l2_plane __user *up,
+			    struct v4l2_plane32 __user *up32,
 			    enum v4l2_memory memory)
 {
 	unsigned long p;
@@ -375,8 +444,7 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 			return -EFAULT;
 		break;
 	case V4L2_MEMORY_DMABUF:
-		if (copy_in_user(&up32->m.fd, &up->m.fd,
-				 sizeof(up->m.fd)))
+		if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd)))
 			return -EFAULT;
 		break;
 	}
@@ -384,37 +452,68 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
 	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(*up)) ||
+	    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;
+	int num_planes;
 	int ret;
 
 	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
-	    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(&kp->index, &up->index) ||
+	    get_user(type, &up->type) ||
+	    put_user(type, &kp->type) ||
+	    convert_in_user(&kp->flags, &up->flags) ||
+	    get_user(memory, &up->memory) ||
+	    put_user(memory, &kp->memory) ||
+	    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(&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))
 			return -EFAULT;
 
-	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
-		unsigned int num_planes;
-
-		if (kp->length == 0) {
-			kp->m.planes = NULL;
+	if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
+		num_planes = length;
+		if (num_planes == 0) {
 			/* num_planes == 0 is legal, e.g. when userspace doesn't
 			 * need planes array on DQBUF*/
-			return 0;
-		} else if (kp->length > VIDEO_MAX_PLANES) {
+			return put_user(NULL, &kp->m.planes);
+		} else if (num_planes > VIDEO_MAX_PLANES) {
 			return -EINVAL;
 		}
 
@@ -423,40 +522,47 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 
 		uplane32 = compat_ptr(p);
 		if (!access_ok(VERIFY_READ, uplane32,
-			       kp->length * sizeof(*uplane32)))
+				num_planes * sizeof(*uplane32)))
 			return -EFAULT;
 
 		/* We don't really care if userspace decides to kill itself
 		 * by passing a very big num_planes value */
-		uplane = compat_alloc_user_space(kp->length * sizeof(*uplane));
-		kp->m.planes = (__force struct v4l2_plane *)uplane;
+		if (aux_space < num_planes * sizeof(*uplane))
+			return -EFAULT;
+
+		uplane = aux_buf;
+		if (put_user((__force struct v4l2_plane *)uplane,
+					&kp->m.planes))
+			return -EFAULT;
 
-		for (num_planes = 0; num_planes < kp->length; num_planes++) {
-			ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
+		while (--num_planes >= 0) {
+			ret = get_v4l2_plane32(uplane, uplane32, memory);
 			if (ret)
 				return ret;
-			++uplane;
-			++uplane32;
+			uplane++;
+			uplane32++;
 		}
 	} else {
-		switch (kp->memory) {
+		switch (memory) {
 		case V4L2_MEMORY_MMAP:
-		case V4L2_MEMORY_OVERLAY:
-			if (get_user(kp->m.offset, &up->m.offset))
+			if (convert_in_user(&kp->m.offset, &up->m.offset))
 				return -EFAULT;
 			break;
-		case V4L2_MEMORY_USERPTR:
-			{
-				compat_long_t tmp;
-
-				if (get_user(tmp, &up->m.userptr))
-					return -EFAULT;
+		case V4L2_MEMORY_USERPTR: {
+			compat_long_t tmp;
 
-				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 (convert_in_user(&kp->m.offset, &up->m.offset))
+				return -EFAULT;
 			break;
 		case V4L2_MEMORY_DMABUF:
-			if (get_user(kp->m.fd, &up->m.fd))
+			if (convert_in_user(&kp->m.fd, &up->m.fd))
 				return -EFAULT;
 			break;
 		}
@@ -465,8 +571,12 @@ 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;
@@ -474,53 +584,60 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
 	int ret;
 
 	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
-	    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(&up->index, &kp->index) ||
+	    get_user(type, &kp->type) ||
+	    put_user(type, &up->type) ||
+	    convert_in_user(&up->flags, &kp->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(kp->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(&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) ||
+	    copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
+	    convert_in_user(&up->sequence, &kp->sequence) ||
+	    convert_in_user(&up->reserved2, &kp->reserved2) ||
+	    convert_in_user(&up->reserved, &kp->reserved) ||
+	    get_user(length, &kp->length) ||
+	    put_user(length, &up->length))
 		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 = (__force struct v4l2_plane __user *)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:
-		case V4L2_MEMORY_OVERLAY:
-			if (put_user(kp->m.offset, &up->m.offset))
+			if (convert_in_user(&up->m.offset, &kp->m.offset))
 				return -EFAULT;
 			break;
 		case V4L2_MEMORY_USERPTR:
-			if (put_user(kp->m.userptr, &up->m.userptr))
+			if (convert_in_user(&up->m.userptr, &kp->m.userptr))
+				return -EFAULT;
+			break;
+		case V4L2_MEMORY_OVERLAY:
+			if (convert_in_user(&up->m.offset, &kp->m.offset))
 				return -EFAULT;
 			break;
 		case V4L2_MEMORY_DMABUF:
-			if (put_user(kp->m.fd, &up->m.fd))
+			if (convert_in_user(&up->m.fd, &kp->m.fd))
 				return -EFAULT;
 			break;
 		}
@@ -545,29 +662,32 @@ struct v4l2_framebuffer32 {
 	} 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(*up)) ||
 	    get_user(tmp, &up->base) ||
-	    get_user(kp->capability, &up->capability) ||
-	    get_user(kp->flags, &up->flags) ||
-	    copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
+	    put_user((__force void *)compat_ptr(tmp), &kp->base) ||
+	    convert_in_user(&kp->capability, &up->capability) ||
+	    convert_in_user(&kp->flags, &up->flags) ||
+	    copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt)))
 		return -EFAULT;
-	kp->base = (__force void *)compat_ptr(tmp);
 	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(*up)) ||
-	    put_user(tmp, &up->base) ||
-	    put_user(kp->capability, &up->capability) ||
-	    put_user(kp->flags, &up->flags) ||
-	    copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
+	    get_user(base, &kp->base) ||
+	    put_user(ptr_to_compat(base), &up->base) ||
+	    convert_in_user(&up->capability, &kp->capability) ||
+	    convert_in_user(&up->flags, &kp->flags) ||
+	    copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt)))
 		return -EFAULT;
 	return 0;
 }
@@ -586,16 +706,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 *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(*up)))
+	if (copy_in_user(kp, up, sizeof(*up)))
 		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(*up)))
+	if (copy_in_user(up, kp, sizeof(*up)))
 		return -EFAULT;
 	return 0;
 }
@@ -648,39 +770,58 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id)
 	return !ret && (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
 }
 
+static int bufsize_v4l2_ext_controls32(struct v4l2_ext_controls32 __user *up)
+{
+	u32 count;
+
+	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+	    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 file *file,
-				   struct v4l2_ext_controls *kp,
-				   struct v4l2_ext_controls32 __user *up)
+				   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;
+	u32 count;
 	unsigned int n;
 	compat_caddr_t p;
 
 	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
-	    get_user(kp->which, &up->which) ||
-	    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(&kp->which, &up->which) ||
+	    get_user(count, &up->count) ||
+	    put_user(count, &kp->count) ||
+	    convert_in_user(&kp->error_idx, &up->error_idx) ||
+	    copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
 		return -EFAULT;
-	if (kp->count == 0) {
-		kp->controls = NULL;
-		return 0;
-	} else if (kp->count > V4L2_CID_MAX_CTRLS) {
+	if (count == 0)
+		return put_user(NULL, &kp->controls);
+	else if (kp->count > V4L2_CID_MAX_CTRLS)
 		return -EINVAL;
-	}
 	if (get_user(p, &up->controls))
 		return -EFAULT;
 	ucontrols = compat_ptr(p);
-	if (!access_ok(VERIFY_READ, ucontrols, kp->count * sizeof(*ucontrols)))
+	if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols)))
+		return -EFAULT;
+	if (aux_space < count * sizeof(*kcontrols))
 		return -EFAULT;
-	kcontrols = compat_alloc_user_space(kp->count * sizeof(*kcontrols));
-	kp->controls = (__force struct v4l2_ext_control *)kcontrols;
-	for (n = 0; n < kp->count; n++) {
+	kcontrols = aux_buf;
+	if (put_user((__force struct v4l2_ext_control *)kcontrols,
+		     &kp->controls))
+		return -EFAULT;
+
+	for (n = 0; n < count; n++) {
 		u32 id;
 
 		if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
 			return -EFAULT;
+
 		if (get_user(id, &kcontrols->id))
 			return -EFAULT;
 		if (ctrl_is_pointer(file, id)) {
@@ -699,36 +840,47 @@ static int get_v4l2_ext_controls32(struct file *file,
 }
 
 static int put_v4l2_ext_controls32(struct file *file,
-				   struct v4l2_ext_controls *kp,
+				   struct v4l2_ext_controls __user *kp,
 				   struct v4l2_ext_controls32 __user *up)
 {
 	struct v4l2_ext_control32 __user *ucontrols;
-	struct v4l2_ext_control __user *kcontrols =
-		(__force struct v4l2_ext_control __user *)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(*up)) ||
-	    put_user(kp->which, &up->which) ||
-	    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(&up->which, &kp->which) ||
+	    get_user(count, &kp->count) ||
+	    put_user(count, &up->count) ||
+	    convert_in_user(&up->error_idx, &kp->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(*ucontrols)))
+	if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols)))
 		return -EFAULT;
 
-	while (--n >= 0) {
-		unsigned size = sizeof(*ucontrols);
+	for (n = 0; n < count; n++) {
+		unsigned int size = sizeof(*ucontrols);
 		u32 id;
 
+		if (copy_in_user(&ucontrols->id, &kcontrols->id,
+				 sizeof(ucontrols->id)) ||
+		    copy_in_user(&ucontrols->size, &kcontrols->size,
+				 sizeof(ucontrols->size)) ||
+		    copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
+				 sizeof(ucontrols->reserved2)))
+			return -EFAULT;
+
 		if (get_user(id, &kcontrols->id))
 			return -EFAULT;
+
 		/* Do not modify the pointer when copying a pointer control.
 		   The contents of the pointer was changed, not the pointer
 		   itself. */
@@ -736,6 +888,7 @@ static int put_v4l2_ext_controls32(struct file *file,
 			size -= sizeof(ucontrols->value64);
 		if (copy_in_user(ucontrols, kcontrols, size))
 			return -EFAULT;
+
 		ucontrols++;
 		kcontrols++;
 	}
@@ -755,17 +908,18 @@ 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(*up)) ||
-	    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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
-	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
-	    put_user(kp->id, &up->id) ||
-	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
+	    convert_in_user(&up->type, &kp->type) ||
+	    copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
+	    convert_in_user(&up->pending, &kp->pending) ||
+	    convert_in_user(&up->sequence, &kp->sequence) ||
+	    convert_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
+	    convert_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) ||
+	    convert_in_user(&up->id, &kp->id) ||
+	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
 		return -EFAULT;
 	return 0;
 }
@@ -778,31 +932,34 @@ struct v4l2_edid32 {
 	compat_caddr_t edid;
 };
 
-static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
+static int get_v4l2_edid32(struct v4l2_edid __user *kp,
+			   struct v4l2_edid32 __user *up)
 {
-	u32 tmp;
+	compat_uptr_t tmp;
 
 	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
-	    get_user(kp->pad, &up->pad) ||
-	    get_user(kp->start_block, &up->start_block) ||
-	    get_user(kp->blocks, &up->blocks) ||
+	    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(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 = (__force u8 *)compat_ptr(tmp);
 	return 0;
 }
 
-static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
+static int put_v4l2_edid32(struct v4l2_edid __user *kp,
+			   struct v4l2_edid32 __user *up)
 {
-	u32 tmp = (u32)((unsigned long)kp->edid);
+	void *edid;
 
 	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
-	    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(up->reserved, kp->reserved, sizeof(up->reserved)))
+	    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(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;
 }
@@ -835,22 +992,30 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
 #define VIDIOC_G_OUTPUT32	_IOR ('V', 46, s32)
 #define VIDIOC_S_OUTPUT32	_IOWR('V', 47, s32)
 
+static void __user *alloc_userspace(unsigned int size, int aux_space, long *err)
+{
+	void __user *up_native;
+
+	if (aux_space < 0) {
+		*err = aux_space;
+		return NULL;
+	}
+	up_native = compat_alloc_user_space(size + aux_space);
+	if (!up_native)
+		*err = -ENOMEM;
+	else if (clear_user(up_native, size))
+		*err = -EFAULT;
+	else
+		return up_native;
+	return NULL;
+}
+
 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_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;
 
@@ -889,30 +1054,47 @@ 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_userspace(sizeof(unsigned int __user),
+					    0, &err);
+		if (!err && convert_in_user((unsigned int __user *)up_native,
+					    (compat_uint_t __user *)up))
+			return -EFAULT;
 		compatible_arg = 0;
 		break;
 
 	case VIDIOC_G_INPUT:
 	case VIDIOC_G_OUTPUT:
+		up_native = alloc_userspace(sizeof(unsigned int __user),
+					    0, &err);
 		compatible_arg = 0;
 		break;
 
 	case VIDIOC_G_EDID:
 	case VIDIOC_S_EDID:
-		err = get_v4l2_edid32(&karg.v2edid, up);
+		up_native = alloc_userspace(sizeof(struct v4l2_edid), 0, &err);
+		err = err ? : get_v4l2_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);
+		aux_space = bufsize_v4l2_format32(up);
+		up_native = alloc_userspace(sizeof(struct v4l2_format),
+					    aux_space, &err);
+		aux_buf = up_native + sizeof(struct v4l2_format);
+		err = err ? : get_v4l2_format32(up_native, up,
+						aux_buf, aux_space);
 		compatible_arg = 0;
 		break;
 
 	case VIDIOC_CREATE_BUFS:
-		err = get_v4l2_create32(&karg.v2crt, up);
+		aux_space = bufsize_v4l2_create32(up);
+		up_native = alloc_userspace(sizeof(struct v4l2_create_buffers),
+					    aux_space, &err);
+		aux_buf = up_native + sizeof(struct v4l2_create_buffers);
+		err = err ? : get_v4l2_create32(up_native, up,
+						aux_buf, aux_space);
 		compatible_arg = 0;
 		break;
 
@@ -920,36 +1102,54 @@ 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);
+		aux_space = bufsize_v4l2_buffer32(up);
+		up_native = alloc_userspace(sizeof(struct v4l2_buffer),
+					    aux_space, &err);
+		aux_buf = up_native + sizeof(struct v4l2_buffer);
+		err = err ? : get_v4l2_buffer32(up_native, up,
+						aux_buf, aux_space);
 		compatible_arg = 0;
 		break;
 
 	case VIDIOC_S_FBUF:
-		err = get_v4l2_framebuffer32(&karg.v2fb, up);
+		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
+					    0, &err);
+		err = err ? : get_v4l2_framebuffer32(up_native, up);
 		compatible_arg = 0;
 		break;
 
 	case VIDIOC_G_FBUF:
+		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
+					    0, &err);
 		compatible_arg = 0;
 		break;
 
 	case VIDIOC_ENUMSTD:
-		err = get_v4l2_standard32(&karg.v2s, up);
+		up_native = alloc_userspace(sizeof(struct v4l2_standard),
+					    0, &err);
+		err = err ? : get_v4l2_standard32(up_native, up);
 		compatible_arg = 0;
 		break;
 
 	case VIDIOC_ENUMINPUT:
-		err = get_v4l2_input32(&karg.v2i, up);
+		up_native = alloc_userspace(sizeof(struct v4l2_input), 0, &err);
+		err = 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(file, &karg.v2ecs, up);
+		aux_space = bufsize_v4l2_ext_controls32(up);
+		up_native = alloc_userspace(sizeof(struct v4l2_ext_controls),
+					    aux_space, &err);
+		aux_buf = up_native + sizeof(struct v4l2_ext_controls);
+		err = err ? : get_v4l2_ext_controls32(file, up_native, up,
+						      aux_buf, aux_space);
 		compatible_arg = 0;
 		break;
 	case VIDIOC_DQEVENT:
+		up_native = alloc_userspace(sizeof(struct v4l2_event), 0, &err);
 		compatible_arg = 0;
 		break;
 	}
@@ -958,13 +1158,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);
 
 	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
 		return err;
@@ -976,11 +1171,11 @@ 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(file, &karg.v2ecs, up))
+		if (put_v4l2_ext_controls32(file, up_native, up))
 			err = -EFAULT;
 		break;
 	case VIDIOC_S_EDID:
-		if (put_v4l2_edid32(&karg.v2edid, up))
+		if (put_v4l2_edid32(up_native, up))
 			err = -EFAULT;
 		break;
 	}
@@ -992,44 +1187,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((compat_uint_t __user *)up,
+				      ((unsigned int __user *)up_native));
 		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_G_EDID:
-		err = put_v4l2_edid32(&karg.v2edid, up);
+		err = put_v4l2_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_PREPARE_BUF:
 	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;
-- 
2.15.1

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt
  2018-01-26 12:43 ` [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt Hans Verkuil
@ 2018-01-26 14:41   ` Sakari Ailus
  2018-01-29 10:09     ` Hans Verkuil
  2018-01-30  8:44     ` Hans Verkuil
  0 siblings, 2 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-26 14:41 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

On Fri, Jan 26, 2018 at 01:43:17PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Don't duplicate the buffer type checks in enum/g/s/try_fmt.
> The check_fmt function does that already.
> 
> It is hard to keep the checks in sync for all these functions and
> in fact the check for VBI was wrong in the _fmt functions as it
> allowed SDR types as well. This caused a v4l2-compliance failure
> for /dev/swradio0 using vivid.
> 
> This simplifies the code and keeps the check in one place and
> fixes the SDR/VBI bug.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-ioctl.c | 140 ++++++++++++++---------------------
>  1 file changed, 54 insertions(+), 86 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> index 59d2100eeff6..c7f6b65d3ad7 100644
> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> @@ -1316,52 +1316,50 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
>  	struct v4l2_fmtdesc *p = arg;
> -	struct video_device *vfd = video_devdata(file);
> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> -	int ret = -EINVAL;
> +	int ret = check_fmt(file, p->type);

I'd separate this from the variable declaration. The function is doing more
than just fetch something to be used as a shorthand locally. I.e.

	int ret;

	ret = check_fmt(file, p->type);

Same elsewhere.

The patch appears to be making an assumption that get_fmt will be
universally supported on any buffer type, or that buffer type is not
supported at all. I don't see a problem with the approach, but it'd be nice
to document it, perhaps in struct v4l2_ioctl_ops KernelDoc documentation.

check_fmt() allows V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_BUF_TYPE_VBI_OUTPUT,
V4L2_BUF_TYPE_SLICED_VBI_CAPTURE and V4L2_BUF_TYPE_SLICED_VBI_OUTPUT that
the original code did not for VIDIOC_ENUM_FMT. Is the change intentional?

Documentation should be updated regarding SDR and META formats (buffer
type) but that's out of scope of the patchset:

<URL:https://hverkuil.home.xs4all.nl/spec/uapi/v4l/vidioc-enum-fmt.html>

> +
> +	if (ret)
> +		return ret;
> +	ret = -EINVAL;
>  
>  	switch (p->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_enum_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
>  			break;
>  		ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap_mplane))
> +		if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
>  			break;
>  		ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_overlay))
> +		if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
>  			break;
>  		ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_enum_fmt_vid_out))
>  			break;
>  		ret = ops->vidioc_enum_fmt_vid_out(file, fh, arg);
>  		break;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out_mplane))
> +		if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
>  			break;
>  		ret = ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
>  		break;
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_enum_fmt_sdr_cap))
> +		if (unlikely(!ops->vidioc_enum_fmt_sdr_cap))
>  			break;
>  		ret = ops->vidioc_enum_fmt_sdr_cap(file, fh, arg);
>  		break;
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_enum_fmt_sdr_out))
> +		if (unlikely(!ops->vidioc_enum_fmt_sdr_out))
>  			break;
>  		ret = ops->vidioc_enum_fmt_sdr_out(file, fh, arg);
>  		break;
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_meta_cap))
> +		if (unlikely(!ops->vidioc_enum_fmt_meta_cap))
>  			break;
>  		ret = ops->vidioc_enum_fmt_meta_cap(file, fh, arg);
>  		break;
> @@ -1375,13 +1373,10 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
>  	struct v4l2_format *p = arg;
> -	struct video_device *vfd = video_devdata(file);
> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> -	int ret;
> +	int ret = check_fmt(file, p->type);
> +
> +	if (ret)
> +		return ret;
>  
>  	/*
>  	 * fmt can't be cleared for these overlay types due to the 'clips'
> @@ -1409,7 +1404,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>  
>  	switch (p->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_g_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
>  			break;
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
> @@ -1417,23 +1412,15 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane))
> -			break;
>  		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_overlay))
> -			break;
>  		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_vbi_cap))
> -			break;
>  		return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_cap))
> -			break;
>  		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_g_fmt_vid_out))
>  			break;
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
> @@ -1441,32 +1428,18 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane))
> -			break;
>  		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_overlay))
> -			break;
>  		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_vbi_out))
> -			break;
>  		return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_out))
> -			break;
>  		return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_g_fmt_sdr_cap))
> -			break;
>  		return ops->vidioc_g_fmt_sdr_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_g_fmt_sdr_out))
> -			break;
>  		return ops->vidioc_g_fmt_sdr_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_meta_cap))
> -			break;
>  		return ops->vidioc_g_fmt_meta_cap(file, fh, arg);
>  	}
>  	return -EINVAL;
> @@ -1492,12 +1465,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>  {
>  	struct v4l2_format *p = arg;
>  	struct video_device *vfd = video_devdata(file);
> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> -	int ret;
> +	int ret = check_fmt(file, p->type);
> +
> +	if (ret)
> +		return ret;
>  
>  	ret = v4l_enable_media_source(vfd);
>  	if (ret)
> @@ -1506,37 +1477,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>  
>  	switch (p->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_s_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>  		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
>  		/* just in case the driver zeroed it again */
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> -		if (is_tch)
> +		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>  			v4l_pix_format_touch(&p->fmt.pix);
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>  		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_overlay))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.win);
>  		return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_vbi_cap))
> +		if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>  		return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_cap))
> +		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>  		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>  		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
> @@ -1544,37 +1515,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>  		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_overlay))
> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.win);
>  		return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_vbi_out))
> +		if (unlikely(!ops->vidioc_s_fmt_vbi_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>  		return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_out))
> +		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>  		return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_s_fmt_sdr_cap))
> +		if (unlikely(!ops->vidioc_s_fmt_sdr_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>  		return ops->vidioc_s_fmt_sdr_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_s_fmt_sdr_out))
> +		if (unlikely(!ops->vidioc_s_fmt_sdr_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>  		return ops->vidioc_s_fmt_sdr_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_meta_cap))
> +		if (unlikely(!ops->vidioc_s_fmt_meta_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.meta);
>  		return ops->vidioc_s_fmt_meta_cap(file, fh, arg);
> @@ -1586,19 +1557,16 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>  				struct file *file, void *fh, void *arg)
>  {
>  	struct v4l2_format *p = arg;
> -	struct video_device *vfd = video_devdata(file);
> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> -	int ret;
> +	int ret = check_fmt(file, p->type);
> +
> +	if (ret)
> +		return ret;
>  
>  	v4l_sanitize_format(p);
>  
>  	switch (p->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_try_fmt_vid_cap))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>  		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
> @@ -1606,27 +1574,27 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>  		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_overlay))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.win);
>  		return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_vbi_cap))
> +		if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>  		return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_cap))
> +		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>  		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>  		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
> @@ -1634,37 +1602,37 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>  		return ret;
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>  		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_overlay))
> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.win);
>  		return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_vbi_out))
> +		if (unlikely(!ops->vidioc_try_fmt_vbi_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>  		return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_out))
> +		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>  		return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_try_fmt_sdr_cap))
> +		if (unlikely(!ops->vidioc_try_fmt_sdr_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>  		return ops->vidioc_try_fmt_sdr_cap(file, fh, arg);
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_try_fmt_sdr_out))
> +		if (unlikely(!ops->vidioc_try_fmt_sdr_out))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>  		return ops->vidioc_try_fmt_sdr_out(file, fh, arg);
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_meta_cap))
> +		if (unlikely(!ops->vidioc_try_fmt_meta_cap))
>  			break;
>  		CLEAR_AFTER_FIELD(p, fmt.meta);
>  		return ops->vidioc_try_fmt_meta_cap(file, fh, arg);
> -- 
> 2.15.1
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 03/12] v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF
  2018-01-26 12:43 ` [PATCH 03/12] v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF Hans Verkuil
@ 2018-01-26 14:43   ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-26 14:43 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On Fri, Jan 26, 2018 at 01:43:18PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> The result of the VIDIOC_PREPARE_BUF ioctl was never copied back
> to userspace since it was missing in the switch.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index e48d59046086..76ed43e774dd 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -1052,6 +1052,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
>  		err = put_v4l2_create32(&karg.v2crt, up);
>  		break;
>  
> +	case VIDIOC_PREPARE_BUF:
>  	case VIDIOC_QUERYBUF:
>  	case VIDIOC_QBUF:
>  	case VIDIOC_DQBUF:
> -- 
> 2.15.1
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 01/12] vivid: fix module load error when enabling fb and no_error_inj=1
  2018-01-26 12:43 ` [PATCH 01/12] vivid: fix module load error when enabling fb and no_error_inj=1 Hans Verkuil
@ 2018-01-26 14:43   ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-26 14:43 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On Fri, Jan 26, 2018 at 01:43:16PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> If the framebuffer is enabled and error injection is disabled, then
> creating the controls for the video output device would fail with an
> error.
> 
> This is because the Clear Framebuffer control uses the 'vivid control
> class' and that control class isn't added if error injection is disabled.
> 
> In addition, this control was added to e.g. vbi devices as well, which
> makes no sense.
> 
> Move this control to its own control handler and handle it correctly.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 04/12] v4l2-compat-ioctl32.c: fix the indentation
  2018-01-26 12:43 ` [PATCH 04/12] v4l2-compat-ioctl32.c: fix the indentation Hans Verkuil
@ 2018-01-26 14:57   ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-26 14:57 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On Fri, Jan 26, 2018 at 01:43:19PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> The indentation of this source is all over the place. Fix this.
> This patch only changes whitespace.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 212 +++++++++++++-------------
>  1 file changed, 107 insertions(+), 105 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index 76ed43e774dd..83066b21b0b2 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -49,12 +49,12 @@ struct v4l2_window32 {
>  static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
>  {
>  	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) ||
> -		get_user(kp->global_alpha, &up->global_alpha))
> -			return -EFAULT;
> +	    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) ||
> +	    get_user(kp->global_alpha, &up->global_alpha))
> +		return -EFAULT;
>  	if (kp->clipcount > 2048)
>  		return -EINVAL;
>  	if (kp->clipcount) {
> @@ -84,11 +84,11 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
>  static int put_v4l2_window32(struct v4l2_window *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) ||
> -		put_user(kp->global_alpha, &up->global_alpha))
> -			return -EFAULT;
> +	    put_user(kp->field, &up->field) ||
> +	    put_user(kp->chromakey, &up->chromakey) ||
> +	    put_user(kp->clipcount, &up->clipcount) ||
> +	    put_user(kp->global_alpha, &up->global_alpha))
> +		return -EFAULT;
>  	return 0;
>  }
>  
> @@ -100,7 +100,7 @@ static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi
>  }
>  
>  static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
> -				struct v4l2_pix_format_mplane __user *up)
> +					     struct v4l2_pix_format_mplane __user *up)
>  {
>  	if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
>  		return -EFAULT;
> @@ -115,7 +115,7 @@ static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi
>  }
>  
>  static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
> -				struct v4l2_pix_format_mplane __user *up)
> +					     struct v4l2_pix_format_mplane __user *up)
>  {
>  	if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
>  		return -EFAULT;
> @@ -238,7 +238,7 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
>  		return get_v4l2_meta_format(&kp->fmt.meta, &up->fmt.meta);
>  	default:
>  		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
> -								kp->type);
> +			kp->type);
>  		return -EINVAL;
>  	}
>  }
> @@ -287,7 +287,7 @@ static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
>  		return put_v4l2_meta_format(&kp->fmt.meta, &up->fmt.meta);
>  	default:
>  		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
> -								kp->type);
> +			kp->type);
>  		return -EINVAL;
>  	}
>  }
> @@ -321,7 +321,7 @@ static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
>  {
>  	/* 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))
> +	    get_user(kp->index, &up->index))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -329,13 +329,14 @@ static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
>  static int put_v4l2_standard32(struct v4l2_standard *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)))
> -			return -EFAULT;
> +	    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)))
> +		return -EFAULT;
>  	return 0;
>  }
>  
> @@ -375,14 +376,14 @@ struct v4l2_buffer32 {
>  };
>  
>  static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
> -				enum v4l2_memory memory)
> +			    enum v4l2_memory memory)
>  {
>  	void __user *up_pln;
>  	compat_long_t p;
>  
>  	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
> -		copy_in_user(&up->data_offset, &up32->data_offset,
> -				sizeof(__u32)))
> +	    copy_in_user(&up->data_offset, &up32->data_offset,
> +			 sizeof(__u32)))
>  		return -EFAULT;
>  
>  	if (memory == V4L2_MEMORY_USERPTR) {
> @@ -396,7 +397,7 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  			return -EFAULT;
>  	} else {
>  		if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
> -					sizeof(__u32)))
> +				 sizeof(__u32)))
>  			return -EFAULT;
>  	}
>  
> @@ -404,23 +405,23 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  }
>  
>  static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
> -				enum v4l2_memory memory)
> +			    enum v4l2_memory memory)
>  {
>  	if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
> -		copy_in_user(&up32->data_offset, &up->data_offset,
> -				sizeof(__u32)))
> +	    copy_in_user(&up32->data_offset, &up->data_offset,
> +			 sizeof(__u32)))
>  		return -EFAULT;
>  
>  	/* For MMAP, driver might've set up the offset, so copy it back.
>  	 * USERPTR stays the same (was userspace-provided), so no copying. */
>  	if (memory == V4L2_MEMORY_MMAP)
>  		if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
> -					sizeof(__u32)))
> +				 sizeof(__u32)))
>  			return -EFAULT;
>  	/* For DMABUF, driver might've set up the fd, so copy it back. */
>  	if (memory == V4L2_MEMORY_DMABUF)
>  		if (copy_in_user(&up32->m.fd, &up->m.fd,
> -					sizeof(int)))
> +				 sizeof(int)))
>  			return -EFAULT;
>  
>  	return 0;
> @@ -434,19 +435,19 @@ 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))
> -			return -EFAULT;
> +	    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))
> +		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))
> +		    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))
>  			return -EFAULT;
>  
>  	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
> @@ -466,7 +467,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  
>  		uplane32 = compat_ptr(p);
>  		if (!access_ok(VERIFY_READ, uplane32,
> -				kp->length * sizeof(struct v4l2_plane32)))
> +			       kp->length * sizeof(struct v4l2_plane32)))
>  			return -EFAULT;
>  
>  		/* We don't really care if userspace decides to kill itself
> @@ -490,12 +491,12 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  			break;
>  		case V4L2_MEMORY_USERPTR:
>  			{
> -			compat_long_t tmp;
> +				compat_long_t tmp;
>  
> -			if (get_user(tmp, &up->m.userptr))
> -				return -EFAULT;
> +				if (get_user(tmp, &up->m.userptr))
> +					return -EFAULT;
>  
> -			kp->m.userptr = (unsigned long)compat_ptr(tmp);
> +				kp->m.userptr = (unsigned long)compat_ptr(tmp);
>  			}
>  			break;
>  		case V4L2_MEMORY_OVERLAY:
> @@ -521,22 +522,23 @@ 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))
> -			return -EFAULT;
> +	    put_user(kp->index, &up->index) ||
> +	    put_user(kp->type, &up->type) ||
> +	    put_user(kp->flags, &up->flags) ||
> +	    put_user(kp->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))
> -			return -EFAULT;
> +	    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))
> +		return -EFAULT;
>  
>  	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
>  		num_planes = kp->length;
> @@ -600,11 +602,11 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
>  	u32 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) ||
> -		copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
> -			return -EFAULT;
> +	    get_user(tmp, &up->base) ||
> +	    get_user(kp->capability, &up->capability) ||
> +	    get_user(kp->flags, &up->flags) ||
> +	    copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
> +		return -EFAULT;
>  	kp->base = (__force void *)compat_ptr(tmp);
>  	return 0;
>  }
> @@ -614,11 +616,11 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
>  	u32 tmp = (u32)((unsigned long)kp->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) ||
> -		copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
> -			return -EFAULT;
> +	    put_user(tmp, &up->base) ||
> +	    put_user(kp->capability, &up->capability) ||
> +	    put_user(kp->flags, &up->flags) ||
> +	    copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
> +		return -EFAULT;
>  	return 0;
>  }
>  
> @@ -694,12 +696,12 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  	compat_caddr_t p;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
> -		get_user(kp->which, &up->which) ||
> -		get_user(kp->count, &up->count) ||
> -		get_user(kp->error_idx, &up->error_idx) ||
> -		copy_from_user(kp->reserved, up->reserved,
> -			       sizeof(kp->reserved)))
> -			return -EFAULT;
> +	    get_user(kp->which, &up->which) ||
> +	    get_user(kp->count, &up->count) ||
> +	    get_user(kp->error_idx, &up->error_idx) ||
> +	    copy_from_user(kp->reserved, up->reserved,
> +			   sizeof(kp->reserved)))
> +		return -EFAULT;
>  	if (kp->count == 0) {
>  		kp->controls = NULL;
>  		return 0;
> @@ -710,7 +712,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  		return -EFAULT;
>  	ucontrols = compat_ptr(p);
>  	if (!access_ok(VERIFY_READ, ucontrols,
> -			kp->count * sizeof(struct v4l2_ext_control32)))
> +		       kp->count * sizeof(struct v4l2_ext_control32)))
>  		return -EFAULT;
>  	kcontrols = compat_alloc_user_space(kp->count *
>  					    sizeof(struct v4l2_ext_control));
> @@ -746,11 +748,11 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  	compat_caddr_t p;
>  
>  	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
> -		put_user(kp->which, &up->which) ||
> -		put_user(kp->count, &up->count) ||
> -		put_user(kp->error_idx, &up->error_idx) ||
> -		copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
> -			return -EFAULT;
> +	    put_user(kp->which, &up->which) ||
> +	    put_user(kp->count, &up->count) ||
> +	    put_user(kp->error_idx, &up->error_idx) ||
> +	    copy_to_user(up->reserved, kp->reserved, sizeof(up->reserved)))
> +		return -EFAULT;
>  	if (!kp->count)
>  		return 0;
>  
> @@ -758,7 +760,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  		return -EFAULT;
>  	ucontrols = compat_ptr(p);
>  	if (!access_ok(VERIFY_WRITE, ucontrols,
> -			n * sizeof(struct v4l2_ext_control32)))
> +		       n * sizeof(struct v4l2_ext_control32)))
>  		return -EFAULT;
>  
>  	while (--n >= 0) {
> @@ -796,15 +798,15 @@ struct v4l2_event32 {
>  static int put_v4l2_event32(struct v4l2_event *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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
> -		put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
> -		put_user(kp->id, &up->id) ||
> -		copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
> -			return -EFAULT;
> +	    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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
> +	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
> +	    put_user(kp->id, &up->id) ||
> +	    copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
> +		return -EFAULT;
>  	return 0;
>  }
>  
> @@ -821,12 +823,12 @@ static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>  	u32 tmp;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_edid32)) ||
> -		get_user(kp->pad, &up->pad) ||
> -		get_user(kp->start_block, &up->start_block) ||
> -		get_user(kp->blocks, &up->blocks) ||
> -		get_user(tmp, &up->edid) ||
> -		copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
> -			return -EFAULT;
> +	    get_user(kp->pad, &up->pad) ||
> +	    get_user(kp->start_block, &up->start_block) ||
> +	    get_user(kp->blocks, &up->blocks) ||
> +	    get_user(tmp, &up->edid) ||
> +	    copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
> +		return -EFAULT;
>  	kp->edid = (__force u8 *)compat_ptr(tmp);
>  	return 0;
>  }
> @@ -836,12 +838,12 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>  	u32 tmp = (u32)((unsigned long)kp->edid);
>  
>  	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_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(up->reserved, kp->reserved, sizeof(up->reserved)))
> -			return -EFAULT;
> +	    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(up->reserved, kp->reserved, sizeof(up->reserved)))
> +		return -EFAULT;
>  	return 0;
>  }
>  
> -- 
> 2.15.1
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 05/12] v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32
  2018-01-26 12:43 ` [PATCH 05/12] v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32 Hans Verkuil
@ 2018-01-26 15:20   ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-26 15:20 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

On Fri, Jan 26, 2018 at 01:43:20PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> These helper functions do not really help. Move the code to the
> __get/put_v4l2_format32 functions.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 124 +++++---------------------
>  1 file changed, 24 insertions(+), 100 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index 83066b21b0b2..2dd9b42d5859 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -92,92 +92,6 @@ static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
>  	return 0;
>  }
>  
> -static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
> -{
> -	if (copy_from_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,
> -					     struct v4l2_pix_format_mplane __user *up)
> -{
> -	if (copy_from_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)
> -{
> -	if (copy_to_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,
> -					     struct v4l2_pix_format_mplane __user *up)
> -{
> -	if (copy_to_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)
> -{
> -	if (copy_from_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)
> -{
> -	if (copy_to_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)
> -{
> -	if (copy_from_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)
> -{
> -	if (copy_to_user(up, kp, sizeof(struct v4l2_sliced_vbi_format)))
> -		return -EFAULT;
> -	return 0;
> -}
> -
> -static inline int get_v4l2_sdr_format(struct v4l2_sdr_format *kp, struct v4l2_sdr_format __user *up)
> -{
> -	if (copy_from_user(kp, up, sizeof(struct v4l2_sdr_format)))
> -		return -EFAULT;
> -	return 0;
> -}
> -
> -static inline int put_v4l2_sdr_format(struct v4l2_sdr_format *kp, struct v4l2_sdr_format __user *up)
> -{
> -	if (copy_to_user(up, kp, sizeof(struct v4l2_sdr_format)))
> -		return -EFAULT;
> -	return 0;
> -}
> -
> -static inline int get_v4l2_meta_format(struct v4l2_meta_format *kp, struct v4l2_meta_format __user *up)
> -{
> -	if (copy_from_user(kp, up, sizeof(struct v4l2_meta_format)))
> -		return -EFAULT;
> -	return 0;
> -}
> -
> -static inline int put_v4l2_meta_format(struct v4l2_meta_format *kp, struct v4l2_meta_format __user *up)
> -{
> -	if (copy_to_user(up, kp, sizeof(struct v4l2_meta_format)))
> -		return -EFAULT;
> -	return 0;
> -}
> -
>  struct v4l2_format32 {
>  	__u32	type;	/* enum v4l2_buf_type */
>  	union {
> @@ -217,25 +131,30 @@ static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
>  	switch (kp->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
> +		return copy_from_user(&kp->fmt.pix, &up->fmt.pix,
> +				      sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
> -						  &up->fmt.pix_mp);
> +		return copy_from_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
> +				      sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>  		return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> -		return get_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
> +		return copy_from_user(&kp->fmt.vbi, &up->fmt.vbi,
> +				      sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		return get_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
> +		return copy_from_user(&kp->fmt.sliced, &up->fmt.sliced,
> +				      sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		return get_v4l2_sdr_format(&kp->fmt.sdr, &up->fmt.sdr);
> +		return copy_from_user(&kp->fmt.sdr, &up->fmt.sdr,
> +				      sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		return get_v4l2_meta_format(&kp->fmt.meta, &up->fmt.meta);
> +		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
> +				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>  	default:
>  		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
>  			kp->type);
> @@ -266,25 +185,30 @@ static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __us
>  	switch (kp->type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
> +		return copy_to_user(&up->fmt.pix, &kp->fmt.pix,
> +				    sizeof(kp->fmt.pix)) ?  -EFAULT : 0;

Two spaces after "?". Same below. Just one would be nice.

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
> -						  &up->fmt.pix_mp);
> +		return copy_to_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
> +				    sizeof(kp->fmt.pix_mp)) ?  -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>  		return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> -		return put_v4l2_vbi_format(&kp->fmt.vbi, &up->fmt.vbi);
> +		return copy_to_user(&up->fmt.vbi, &kp->fmt.vbi,
> +				    sizeof(kp->fmt.vbi)) ?  -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		return put_v4l2_sliced_vbi_format(&kp->fmt.sliced, &up->fmt.sliced);
> +		return copy_to_user(&up->fmt.sliced, &kp->fmt.sliced,
> +				    sizeof(kp->fmt.sliced)) ?  -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		return put_v4l2_sdr_format(&kp->fmt.sdr, &up->fmt.sdr);
> +		return copy_to_user(&up->fmt.sdr, &kp->fmt.sdr,
> +				    sizeof(kp->fmt.sdr)) ?  -EFAULT : 0;
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		return put_v4l2_meta_format(&kp->fmt.meta, &up->fmt.meta);
> +		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
> +				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
>  	default:
>  		pr_info("compat_ioctl32: unexpected VIDIOC_FMT type %d\n",
>  			kp->type);

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 06/12] v4l2-compat-ioctl32.c: avoid sizeof(type)
  2018-01-26 12:43 ` [PATCH 06/12] v4l2-compat-ioctl32.c: avoid sizeof(type) Hans Verkuil
@ 2018-01-26 15:35   ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-26 15:35 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On Fri, Jan 26, 2018 at 01:43:21PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Instead of doing sizeof(struct foo) use sizeof(*up). There even were
> cases where 4 * sizeof(__u32) was used instead of sizeof(kp->reserved),
> which is very dangerous when the size of the reserved array changes.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 77 ++++++++++++---------------
>  1 file changed, 35 insertions(+), 42 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index 2dd9b42d5859..809448d1b7db 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -48,7 +48,7 @@ struct v4l2_window32 {
>  
>  static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
>  {
> -	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_window32)) ||
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>  	    copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
>  	    get_user(kp->field, &up->field) ||
>  	    get_user(kp->chromakey, &up->chromakey) ||
> @@ -66,7 +66,7 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
>  		if (get_user(p, &up->clips))
>  			return -EFAULT;
>  		uclips = compat_ptr(p);
> -		kclips = compat_alloc_user_space(n * sizeof(struct v4l2_clip));
> +		kclips = compat_alloc_user_space(n * sizeof(*kclips));
>  		kp->clips = kclips;
>  		while (--n >= 0) {
>  			if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
> @@ -164,14 +164,14 @@ 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)
>  {
> -	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)))
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)))
>  		return -EFAULT;
>  	return __get_v4l2_format32(kp, up);
>  }
>  
>  static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
>  {
> -	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) ||
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>  	    copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format)))
>  		return -EFAULT;
>  	return __get_v4l2_format32(&kp->format, &up->format);
> @@ -218,14 +218,14 @@ 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)
>  {
> -	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)))
> +	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)))
>  		return -EFAULT;
>  	return __put_v4l2_format32(kp, up);
>  }
>  
>  static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
>  {
> -	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) ||
> +	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>  	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
>  	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>  		return -EFAULT;
> @@ -244,7 +244,7 @@ struct v4l2_standard32 {
>  static int get_v4l2_standard32(struct v4l2_standard *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)) ||
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>  	    get_user(kp->index, &up->index))
>  		return -EFAULT;
>  	return 0;
> @@ -252,14 +252,14 @@ static int get_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
>  
>  static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32 __user *up)
>  {
> -	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_standard32)) ||
> +	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>  	    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)))
> +	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -307,7 +307,7 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  
>  	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
>  	    copy_in_user(&up->data_offset, &up32->data_offset,
> -			 sizeof(__u32)))
> +			 sizeof(up->data_offset)))
>  		return -EFAULT;
>  
>  	if (memory == V4L2_MEMORY_USERPTR) {
> @@ -317,11 +317,11 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  		if (put_user((unsigned long)up_pln, &up->m.userptr))
>  			return -EFAULT;
>  	} else if (memory == V4L2_MEMORY_DMABUF) {
> -		if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(int)))
> +		if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(up32->m.fd)))
>  			return -EFAULT;
>  	} else {
>  		if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
> -				 sizeof(__u32)))
> +				 sizeof(up32->m.mem_offset)))
>  			return -EFAULT;
>  	}
>  
> @@ -333,19 +333,19 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  {
>  	if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
>  	    copy_in_user(&up32->data_offset, &up->data_offset,
> -			 sizeof(__u32)))
> +			 sizeof(up->data_offset)))
>  		return -EFAULT;
>  
>  	/* For MMAP, driver might've set up the offset, so copy it back.
>  	 * USERPTR stays the same (was userspace-provided), so no copying. */
>  	if (memory == V4L2_MEMORY_MMAP)
>  		if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
> -				 sizeof(__u32)))
> +				 sizeof(up->m.mem_offset)))
>  			return -EFAULT;
>  	/* For DMABUF, driver might've set up the fd, so copy it back. */
>  	if (memory == V4L2_MEMORY_DMABUF)
>  		if (copy_in_user(&up32->m.fd, &up->m.fd,
> -				 sizeof(int)))
> +				 sizeof(up->m.fd)))
>  			return -EFAULT;
>  
>  	return 0;
> @@ -358,7 +358,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  	compat_caddr_t p;
>  	int ret;
>  
> -	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>  	    get_user(kp->index, &up->index) ||
>  	    get_user(kp->type, &up->type) ||
>  	    get_user(kp->flags, &up->flags) ||
> @@ -370,8 +370,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  		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))
> +		    get_user(kp->timestamp.tv_usec, &up->timestamp.tv_usec))
>  			return -EFAULT;
>  
>  	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
> @@ -391,13 +390,12 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  
>  		uplane32 = compat_ptr(p);
>  		if (!access_ok(VERIFY_READ, uplane32,
> -			       kp->length * sizeof(struct v4l2_plane32)))
> +			       kp->length * sizeof(*uplane32)))
>  			return -EFAULT;
>  
>  		/* We don't really care if userspace decides to kill itself
>  		 * by passing a very big num_planes value */
> -		uplane = compat_alloc_user_space(kp->length *
> -						 sizeof(struct v4l2_plane));
> +		uplane = compat_alloc_user_space(kp->length * sizeof(*uplane));
>  		kp->m.planes = (__force struct v4l2_plane *)uplane;
>  
>  		for (num_planes = 0; num_planes < kp->length; num_planes++) {
> @@ -445,7 +443,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  	int num_planes;
>  	int ret;
>  
> -	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
> +	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>  	    put_user(kp->index, &up->index) ||
>  	    put_user(kp->type, &up->type) ||
>  	    put_user(kp->flags, &up->flags) ||
> @@ -456,8 +454,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  	    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)) ||
> +	    copy_to_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
>  	    put_user(kp->sequence, &up->sequence) ||
>  	    put_user(kp->reserved2, &up->reserved2) ||
>  	    put_user(kp->reserved, &up->reserved) ||
> @@ -525,7 +522,7 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
>  {
>  	u32 tmp;
>  
> -	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) ||
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>  	    get_user(tmp, &up->base) ||
>  	    get_user(kp->capability, &up->capability) ||
>  	    get_user(kp->flags, &up->flags) ||
> @@ -539,7 +536,7 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame
>  {
>  	u32 tmp = (u32)((unsigned long)kp->base);
>  
> -	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) ||
> +	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>  	    put_user(tmp, &up->base) ||
>  	    put_user(kp->capability, &up->capability) ||
>  	    put_user(kp->flags, &up->flags) ||
> @@ -564,14 +561,14 @@ struct v4l2_input32 {
>     Otherwise it is identical to the 32-bit version. */
>  static inline int get_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
>  {
> -	if (copy_from_user(kp, up, sizeof(struct v4l2_input32)))
> +	if (copy_from_user(kp, up, sizeof(*up)))
>  		return -EFAULT;
>  	return 0;
>  }
>  
>  static inline int put_v4l2_input32(struct v4l2_input *kp, struct v4l2_input32 __user *up)
>  {
> -	if (copy_to_user(up, kp, sizeof(struct v4l2_input32)))
> +	if (copy_to_user(up, kp, sizeof(*up)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -619,12 +616,11 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  	unsigned int n;
>  	compat_caddr_t p;
>  
> -	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_ext_controls32)) ||
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>  	    get_user(kp->which, &up->which) ||
>  	    get_user(kp->count, &up->count) ||
>  	    get_user(kp->error_idx, &up->error_idx) ||
> -	    copy_from_user(kp->reserved, up->reserved,
> -			   sizeof(kp->reserved)))
> +	    copy_from_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
>  		return -EFAULT;
>  	if (kp->count == 0) {
>  		kp->controls = NULL;
> @@ -635,11 +631,9 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  	if (get_user(p, &up->controls))
>  		return -EFAULT;
>  	ucontrols = compat_ptr(p);
> -	if (!access_ok(VERIFY_READ, ucontrols,
> -		       kp->count * sizeof(struct v4l2_ext_control32)))
> +	if (!access_ok(VERIFY_READ, ucontrols, kp->count * sizeof(*ucontrols)))
>  		return -EFAULT;
> -	kcontrols = compat_alloc_user_space(kp->count *
> -					    sizeof(struct v4l2_ext_control));
> +	kcontrols = compat_alloc_user_space(kp->count * sizeof(*kcontrols));
>  	kp->controls = (__force struct v4l2_ext_control *)kcontrols;
>  	for (n = 0; n < kp->count; n++) {
>  		u32 id;
> @@ -671,7 +665,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  	int n = kp->count;
>  	compat_caddr_t p;
>  
> -	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_ext_controls32)) ||
> +	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>  	    put_user(kp->which, &up->which) ||
>  	    put_user(kp->count, &up->count) ||
>  	    put_user(kp->error_idx, &up->error_idx) ||
> @@ -683,8 +677,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  	if (get_user(p, &up->controls))
>  		return -EFAULT;
>  	ucontrols = compat_ptr(p);
> -	if (!access_ok(VERIFY_WRITE, ucontrols,
> -		       n * sizeof(struct v4l2_ext_control32)))
> +	if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(*ucontrols)))
>  		return -EFAULT;
>  
>  	while (--n >= 0) {
> @@ -721,7 +714,7 @@ struct v4l2_event32 {
>  
>  static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *up)
>  {
> -	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_event32)) ||
> +	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>  	    put_user(kp->type, &up->type) ||
>  	    copy_to_user(&up->u, &kp->u, sizeof(kp->u)) ||
>  	    put_user(kp->pending, &up->pending) ||
> @@ -729,7 +722,7 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u
>  	    put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
>  	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
>  	    put_user(kp->id, &up->id) ||
> -	    copy_to_user(up->reserved, kp->reserved, 8 * sizeof(__u32)))
> +	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -746,7 +739,7 @@ static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>  {
>  	u32 tmp;
>  
> -	if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_edid32)) ||
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>  	    get_user(kp->pad, &up->pad) ||
>  	    get_user(kp->start_block, &up->start_block) ||
>  	    get_user(kp->blocks, &up->blocks) ||
> @@ -761,7 +754,7 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>  {
>  	u32 tmp = (u32)((unsigned long)kp->edid);
>  
> -	if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_edid32)) ||
> +	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>  	    put_user(kp->pad, &up->pad) ||
>  	    put_user(kp->start_block, &up->start_block) ||
>  	    put_user(kp->blocks, &up->blocks) ||
> -- 
> 2.15.1
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 07/12] v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32
  2018-01-26 12:43 ` [PATCH 07/12] v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32 Hans Verkuil
@ 2018-01-26 16:15   ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-26 16:15 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

On Fri, Jan 26, 2018 at 01:43:22PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> The struct v4l2_plane32 should set m.userptr as well. The same
> happens in v4l2_buffer32 and v4l2-compliance tests for this.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 47 ++++++++++++++++-----------
>  1 file changed, 28 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index 809448d1b7db..da8a56818a18 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -310,19 +310,24 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  			 sizeof(up->data_offset)))
>  		return -EFAULT;
>  
> -	if (memory == V4L2_MEMORY_USERPTR) {
> +	switch (memory) {
> +	case V4L2_MEMORY_MMAP:
> +	case V4L2_MEMORY_OVERLAY:
> +		if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
> +				 sizeof(up32->m.mem_offset)))
> +			return -EFAULT;
> +		break;
> +	case 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))
>  			return -EFAULT;
> -	} else if (memory == V4L2_MEMORY_DMABUF) {
> +		break;
> +	case V4L2_MEMORY_DMABUF:
>  		if (copy_in_user(&up->m.fd, &up32->m.fd, sizeof(up32->m.fd)))
>  			return -EFAULT;
> -	} else {
> -		if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
> -				 sizeof(up32->m.mem_offset)))
> -			return -EFAULT;
> +		break;
>  	}
>  
>  	return 0;
> @@ -331,22 +336,32 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
>  			    enum v4l2_memory memory)
>  {
> +	unsigned long p;
> +
>  	if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
>  	    copy_in_user(&up32->data_offset, &up->data_offset,
>  			 sizeof(up->data_offset)))
>  		return -EFAULT;
>  
> -	/* For MMAP, driver might've set up the offset, so copy it back.
> -	 * USERPTR stays the same (was userspace-provided), so no copying. */
> -	if (memory == V4L2_MEMORY_MMAP)
> +	switch (memory) {
> +	case V4L2_MEMORY_MMAP:
> +	case V4L2_MEMORY_OVERLAY:
>  		if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
>  				 sizeof(up->m.mem_offset)))
>  			return -EFAULT;
> -	/* For DMABUF, driver might've set up the fd, so copy it back. */
> -	if (memory == V4L2_MEMORY_DMABUF)
> +		break;
> +	case V4L2_MEMORY_USERPTR:
> +		if (get_user(p, &up->m.userptr) ||
> +		    put_user((compat_long_t)ptr_to_compat((__force void *)p),

compat_ulong_t instead of compat_long_t?

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

> +			     &up32->m.userptr))
> +			return -EFAULT;
> +		break;
> +	case V4L2_MEMORY_DMABUF:
>  		if (copy_in_user(&up32->m.fd, &up->m.fd,
>  				 sizeof(up->m.fd)))
>  			return -EFAULT;
> +		break;
> +	}
>  
>  	return 0;
>  }
> @@ -408,6 +423,7 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  	} else {
>  		switch (kp->memory) {
>  		case V4L2_MEMORY_MMAP:
> +		case V4L2_MEMORY_OVERLAY:
>  			if (get_user(kp->m.offset, &up->m.offset))
>  				return -EFAULT;
>  			break;
> @@ -421,10 +437,6 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  				kp->m.userptr = (unsigned long)compat_ptr(tmp);
>  			}
>  			break;
> -		case V4L2_MEMORY_OVERLAY:
> -			if (get_user(kp->m.offset, &up->m.offset))
> -				return -EFAULT;
> -			break;
>  		case V4L2_MEMORY_DMABUF:
>  			if (get_user(kp->m.fd, &up->m.fd))
>  				return -EFAULT;
> @@ -481,6 +493,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  	} else {
>  		switch (kp->memory) {
>  		case V4L2_MEMORY_MMAP:
> +		case V4L2_MEMORY_OVERLAY:
>  			if (put_user(kp->m.offset, &up->m.offset))
>  				return -EFAULT;
>  			break;
> @@ -488,10 +501,6 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  			if (put_user(kp->m.userptr, &up->m.userptr))
>  				return -EFAULT;
>  			break;
> -		case V4L2_MEMORY_OVERLAY:
> -			if (put_user(kp->m.offset, &up->m.offset))
> -				return -EFAULT;
> -			break;
>  		case V4L2_MEMORY_DMABUF:
>  			if (put_user(kp->m.fd, &up->m.fd))
>  				return -EFAULT;
> -- 
> 2.15.1
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 08/12] v4l2-compat-ioctl32.c: fix ctrl_is_pointer
  2018-01-26 12:43 ` [PATCH 08/12] v4l2-compat-ioctl32.c: fix ctrl_is_pointer Hans Verkuil
@ 2018-01-27 23:18   ` Sakari Ailus
  2018-01-29 14:11     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 38+ messages in thread
From: Sakari Ailus @ 2018-01-27 23:18 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

On Fri, Jan 26, 2018 at 01:43:23PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> ctrl_is_pointer just hardcoded two known string controls, but that
> caused problems when using e.g. custom controls that use a pointer
> for the payload.
> 
> Reimplement this function: it now finds the v4l2_ctrl (if the driver
> uses the control framework) or it calls vidioc_query_ext_ctrl (if the
> driver implements that directly).
> 
> In both cases it can now check if the control is a pointer control
> or not.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 56 ++++++++++++++++++---------
>  1 file changed, 37 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index da8a56818a18..cf3d4bfcd132 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -18,6 +18,8 @@
>  #include <linux/videodev2.h>
>  #include <linux/v4l2-subdev.h>
>  #include <media/v4l2-dev.h>
> +#include <media/v4l2-fh.h>
> +#include <media/v4l2-ctrls.h>
>  #include <media/v4l2-ioctl.h>
>  
>  static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> @@ -601,24 +603,38 @@ struct v4l2_ext_control32 {
>  	};
>  } __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)
> +/* Return true if this control is a pointer type. */
> +static inline bool ctrl_is_pointer(struct file *file, u32 id)
>  {
> -	switch (id) {
> -	case V4L2_CID_RDS_TX_PS_NAME:
> -	case V4L2_CID_RDS_TX_RADIO_TEXT:
> -		return 1;
> -	default:
> -		return 0;
> +	struct video_device *vdev = video_devdata(file);
> +	struct v4l2_fh *fh = NULL;
> +	struct v4l2_ctrl_handler *hdl = NULL;
> +	struct v4l2_query_ext_ctrl qec = { id };
> +	const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops;
> +	int ret = -ENOTTY;
> +
> +	if (test_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags))
> +		fh = file->private_data;
> +
> +	if (fh && fh->ctrl_handler)
> +		hdl = fh->ctrl_handler;
> +	else if (vdev->ctrl_handler)
> +		hdl = vdev->ctrl_handler;
> +
> +	if (hdl) {
> +		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, id);
> +
> +		return ctrl && ctrl->is_ptr;
>  	}
> +
> +	if (ops->vidioc_query_ext_ctrl)
> +		ret = ops->vidioc_query_ext_ctrl(file, fh, &qec);

Is there a need for this?

The only driver implementing vidioc_query_ext_ctrl op is the uvcvideo
driver --- and it does not support string controls.

If you think so, then I'd do here instead:

	if (!ops->vidioc_query_ext_ctrl)
		return false;

	return !ops->vidioc_query_ext_ctrl(file, fh, &qec) &&
		(&qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);

And you can also remove ret.

> +	return !ret && (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
>  }
>  
> -static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext_controls32 __user *up)
> +static int get_v4l2_ext_controls32(struct file *file,
> +				   struct v4l2_ext_controls *kp,
> +				   struct v4l2_ext_controls32 __user *up)
>  {
>  	struct v4l2_ext_control32 __user *ucontrols;
>  	struct v4l2_ext_control __user *kcontrols;
> @@ -651,7 +667,7 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  			return -EFAULT;
>  		if (get_user(id, &kcontrols->id))
>  			return -EFAULT;
> -		if (ctrl_is_pointer(id)) {
> +		if (ctrl_is_pointer(file, id)) {
>  			void __user *s;
>  
>  			if (get_user(p, &ucontrols->string))
> @@ -666,7 +682,9 @@ 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 file *file,
> +				   struct v4l2_ext_controls *kp,
> +				   struct v4l2_ext_controls32 __user *up)
>  {
>  	struct v4l2_ext_control32 __user *ucontrols;
>  	struct v4l2_ext_control __user *kcontrols =
> @@ -698,7 +716,7 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
>  		/* 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(id))
> +		if (ctrl_is_pointer(file, id))
>  			size -= sizeof(ucontrols->value64);
>  		if (copy_in_user(ucontrols, kcontrols, size))
>  			return -EFAULT;
> @@ -912,7 +930,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:
> -		err = get_v4l2_ext_controls32(&karg.v2ecs, up);
> +		err = get_v4l2_ext_controls32(file, &karg.v2ecs, up);
>  		compatible_arg = 0;
>  		break;
>  	case VIDIOC_DQEVENT:
> @@ -939,7 +957,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(file, &karg.v2ecs, up))
>  			err = -EFAULT;
>  		break;
>  	case VIDIOC_S_EDID:

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 09/12] v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32
  2018-01-26 12:43 ` [PATCH 09/12] v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32 Hans Verkuil
@ 2018-01-29  9:47   ` Sakari Ailus
  2018-01-29 14:13     ` Mauro Carvalho Chehab
  0 siblings, 1 reply; 38+ messages in thread
From: Sakari Ailus @ 2018-01-29  9:47 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

On Fri, Jan 26, 2018 at 01:43:24PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> put_v4l2_window32() didn't copy back the clip list to userspace.
> Drivers can update the clip rectangles, so this should be done.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 58 ++++++++++++++++++---------
>  1 file changed, 39 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index cf3d4bfcd132..6e3fbbde2c9c 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -50,6 +50,11 @@ struct v4l2_window32 {
>  
>  static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
>  {
> +	struct v4l2_clip32 __user *uclips;
> +	struct v4l2_clip __user *kclips;
> +	compat_caddr_t p;
> +	u32 n;
> +
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>  	    copy_from_user(&kp->w, &up->w, sizeof(up->w)) ||
>  	    get_user(kp->field, &up->field) ||
> @@ -59,38 +64,53 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
>  		return -EFAULT;
>  	if (kp->clipcount > 2048)
>  		return -EINVAL;
> -	if (kp->clipcount) {
> -		struct v4l2_clip32 __user *uclips;
> -		struct v4l2_clip __user *kclips;
> -		int n = kp->clipcount;
> -		compat_caddr_t p;
> +	if (!kp->clipcount) {
> +		kp->clips = NULL;
> +		return 0;
> +	}
>  
> -		if (get_user(p, &up->clips))
> +	n = kp->clipcount;
> +	if (get_user(p, &up->clips))
> +		return -EFAULT;
> +	uclips = compat_ptr(p);
> +	kclips = compat_alloc_user_space(n * sizeof(*kclips));
> +	kp->clips = kclips;
> +	while (n--) {
> +		if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
>  			return -EFAULT;
> -		uclips = compat_ptr(p);
> -		kclips = compat_alloc_user_space(n * sizeof(*kclips));
> -		kp->clips = kclips;
> -		while (--n >= 0) {
> -			if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
> -				return -EFAULT;
> -			if (put_user(n ? kclips + 1 : NULL, &kclips->next))
> -				return -EFAULT;
> -			uclips += 1;
> -			kclips += 1;
> -		}
> -	} else
> -		kp->clips = NULL;
> +		if (put_user(n ? kclips + 1 : NULL, &kclips->next))
> +			return -EFAULT;
> +		uclips++;
> +		kclips++;
> +	}
>  	return 0;
>  }
>  
>  static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
>  {
> +	struct v4l2_clip __user *kclips = kp->clips;
> +	struct v4l2_clip32 __user *uclips;
> +	int n = kp->clipcount;

u32

> +	compat_caddr_t p;
> +
>  	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) ||
>  	    put_user(kp->global_alpha, &up->global_alpha))
>  		return -EFAULT;

One more newline here?

> +	if (!kp->clipcount)
> +		return 0;
> +
> +	if (get_user(p, &up->clips))
> +		return -EFAULT;
> +	uclips = compat_ptr(p);
> +	while (n--) {
> +		if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
> +			return -EFAULT;
> +		uclips++;
> +		kclips++;
> +	}
>  	return 0;
>  }
>  

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 10/12] v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type
  2018-01-26 12:43 ` [PATCH 10/12] v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type Hans Verkuil
@ 2018-01-29  9:48   ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-29  9:48 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On Fri, Jan 26, 2018 at 01:43:25PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> There is nothing wrong with using an unknown buffer type. So
> stop spamming the kernel log whenever this happens. The kernel
> will just return -EINVAL to signal this.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 11/12] v4l2-compat-ioctl32.c: don't copy back the result for certain errors
  2018-01-26 12:43 ` [PATCH 11/12] v4l2-compat-ioctl32.c: don't copy back the result for certain errors Hans Verkuil
@ 2018-01-29  9:56   ` Sakari Ailus
  2018-01-29 10:02     ` Hans Verkuil
  0 siblings, 1 reply; 38+ messages in thread
From: Sakari Ailus @ 2018-01-29  9:56 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

On Fri, Jan 26, 2018 at 01:43:26PM +0100, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Some ioctls need to copy back the result even if the ioctl returned
> an error. However, don't do this for the error codes -ENOTTY, -EFAULT
> and -ENOIOCTLCMD. It makes no sense in those cases.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

Shouldn't such a change be made to video_usercopy() as well? Doesn't need
to be in this set though.

> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index 790473b45a21..2aa9b43daf60 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -966,6 +966,9 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
>  		set_fs(old_fs);
>  	}
>  
> +	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
> +		return err;
> +
>  	/* Special case: even after an error we need to put the
>  	   results back for these ioctls since the error_idx will
>  	   contain information on which control failed. */

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 11/12] v4l2-compat-ioctl32.c: don't copy back the result for certain errors
  2018-01-29  9:56   ` Sakari Ailus
@ 2018-01-29 10:02     ` Hans Verkuil
  2018-01-29 21:01       ` Sakari Ailus
  0 siblings, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-29 10:02 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On 01/29/2018 10:56 AM, Sakari Ailus wrote:
> Hi Hans,
> 
> On Fri, Jan 26, 2018 at 01:43:26PM +0100, Hans Verkuil wrote:
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Some ioctls need to copy back the result even if the ioctl returned
>> an error. However, don't do this for the error codes -ENOTTY, -EFAULT
>> and -ENOIOCTLCMD. It makes no sense in those cases.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> 
> Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> 
> Shouldn't such a change be made to video_usercopy() as well? Doesn't need
> to be in this set though.

Good point. I'll add that for v2. This is not actually a bug as such, but
it's just weird to copy back results if the ioctl wasn't implemented at all.

I realize that I need to drop the -EFAULT check: if you call VIDIOC_G_EXT_CTRLS
with an incorrect userspace buffer for the payload, then the control framework
will set error_idx to the index of the control with the wrong buffer. So you do
need to copy back the data in case of -EFAULT.

I can also drop -ENOIOCTLCMD since video_usercopy() converts that to -ENOTTY.

Regards,

	Hans

> 
>> ---
>>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 3 +++
>>  1 file changed, 3 insertions(+)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> index 790473b45a21..2aa9b43daf60 100644
>> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> @@ -966,6 +966,9 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
>>  		set_fs(old_fs);
>>  	}
>>  
>> +	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
>> +		return err;
>> +
>>  	/* Special case: even after an error we need to put the
>>  	   results back for these ioctls since the error_idx will
>>  	   contain information on which control failed. */
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt
  2018-01-26 14:41   ` Sakari Ailus
@ 2018-01-29 10:09     ` Hans Verkuil
  2018-01-29 20:57       ` Sakari Ailus
  2018-01-30  8:44     ` Hans Verkuil
  1 sibling, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-29 10:09 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On 01/26/2018 03:41 PM, Sakari Ailus wrote:
> Hi Hans,
> 
> On Fri, Jan 26, 2018 at 01:43:17PM +0100, Hans Verkuil wrote:
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Don't duplicate the buffer type checks in enum/g/s/try_fmt.
>> The check_fmt function does that already.
>>
>> It is hard to keep the checks in sync for all these functions and
>> in fact the check for VBI was wrong in the _fmt functions as it
>> allowed SDR types as well. This caused a v4l2-compliance failure
>> for /dev/swradio0 using vivid.
>>
>> This simplifies the code and keeps the check in one place and
>> fixes the SDR/VBI bug.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-ioctl.c | 140 ++++++++++++++---------------------
>>  1 file changed, 54 insertions(+), 86 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index 59d2100eeff6..c7f6b65d3ad7 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -1316,52 +1316,50 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
>>  				struct file *file, void *fh, void *arg)
>>  {
>>  	struct v4l2_fmtdesc *p = arg;
>> -	struct video_device *vfd = video_devdata(file);
>> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
>> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
>> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
>> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
>> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
>> -	int ret = -EINVAL;
>> +	int ret = check_fmt(file, p->type);
> 
> I'd separate this from the variable declaration. The function is doing more
> than just fetch something to be used as a shorthand locally. I.e.
> 
> 	int ret;
> 
> 	ret = check_fmt(file, p->type);
> 
> Same elsewhere.

OK.

> 
> The patch appears to be making an assumption that get_fmt will be
> universally supported on any buffer type, or that buffer type is not
> supported at all. I don't see a problem with the approach, but it'd be nice
> to document it, perhaps in struct v4l2_ioctl_ops KernelDoc documentation.
> 
> check_fmt() allows V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_BUF_TYPE_VBI_OUTPUT,
> V4L2_BUF_TYPE_SLICED_VBI_CAPTURE and V4L2_BUF_TYPE_SLICED_VBI_OUTPUT that
> the original code did not for VIDIOC_ENUM_FMT. Is the change intentional?

ENUM_FMT isn't supported for VBI. So v4l_enum_fmt() in v4l2-ioctl.c will
always return -EINVAL. So the extra check that check_fmt does is a bit
overkill for this specific ioctl. But applications won't be able to use
ENUM_FMT for VBI anyway, so that extra check is not a problem.

Regards,

	Hans

> 
> Documentation should be updated regarding SDR and META formats (buffer
> type) but that's out of scope of the patchset:
> 
> <URL:https://hverkuil.home.xs4all.nl/spec/uapi/v4l/vidioc-enum-fmt.html>
> 
>> +
>> +	if (ret)
>> +		return ret;
>> +	ret = -EINVAL;
>>  
>>  	switch (p->type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_enum_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap_mplane))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_overlay))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_out))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_out(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out_mplane))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_enum_fmt_sdr_cap))
>> +		if (unlikely(!ops->vidioc_enum_fmt_sdr_cap))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_sdr_cap(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_enum_fmt_sdr_out))
>> +		if (unlikely(!ops->vidioc_enum_fmt_sdr_out))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_sdr_out(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_meta_cap))
>> +		if (unlikely(!ops->vidioc_enum_fmt_meta_cap))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_meta_cap(file, fh, arg);
>>  		break;
>> @@ -1375,13 +1373,10 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>  				struct file *file, void *fh, void *arg)
>>  {
>>  	struct v4l2_format *p = arg;
>> -	struct video_device *vfd = video_devdata(file);
>> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
>> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
>> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
>> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
>> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
>> -	int ret;
>> +	int ret = check_fmt(file, p->type);
>> +
>> +	if (ret)
>> +		return ret;
>>  
>>  	/*
>>  	 * fmt can't be cleared for these overlay types due to the 'clips'
>> @@ -1409,7 +1404,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>  
>>  	switch (p->type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_g_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
>>  			break;
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
>> @@ -1417,23 +1412,15 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane))
>> -			break;
>>  		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_overlay))
>> -			break;
>>  		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_vbi_cap))
>> -			break;
>>  		return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_cap))
>> -			break;
>>  		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_g_fmt_vid_out))
>>  			break;
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
>> @@ -1441,32 +1428,18 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane))
>> -			break;
>>  		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_overlay))
>> -			break;
>>  		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_vbi_out))
>> -			break;
>>  		return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_out))
>> -			break;
>>  		return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_g_fmt_sdr_cap))
>> -			break;
>>  		return ops->vidioc_g_fmt_sdr_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_g_fmt_sdr_out))
>> -			break;
>>  		return ops->vidioc_g_fmt_sdr_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_meta_cap))
>> -			break;
>>  		return ops->vidioc_g_fmt_meta_cap(file, fh, arg);
>>  	}
>>  	return -EINVAL;
>> @@ -1492,12 +1465,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>  {
>>  	struct v4l2_format *p = arg;
>>  	struct video_device *vfd = video_devdata(file);
>> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
>> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
>> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
>> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
>> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
>> -	int ret;
>> +	int ret = check_fmt(file, p->type);
>> +
>> +	if (ret)
>> +		return ret;
>>  
>>  	ret = v4l_enable_media_source(vfd);
>>  	if (ret)
>> @@ -1506,37 +1477,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>  
>>  	switch (p->type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_s_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>>  		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
>>  		/* just in case the driver zeroed it again */
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>> -		if (is_tch)
>> +		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>  			v4l_pix_format_touch(&p->fmt.pix);
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>  		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_overlay))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.win);
>>  		return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_vbi_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>>  		return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>>  		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>>  		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
>> @@ -1544,37 +1515,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>  		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_overlay))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.win);
>>  		return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_vbi_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_vbi_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>>  		return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>>  		return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_s_fmt_sdr_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_sdr_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>>  		return ops->vidioc_s_fmt_sdr_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_s_fmt_sdr_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_sdr_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>>  		return ops->vidioc_s_fmt_sdr_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_meta_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_meta_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.meta);
>>  		return ops->vidioc_s_fmt_meta_cap(file, fh, arg);
>> @@ -1586,19 +1557,16 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>  				struct file *file, void *fh, void *arg)
>>  {
>>  	struct v4l2_format *p = arg;
>> -	struct video_device *vfd = video_devdata(file);
>> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
>> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
>> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
>> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
>> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
>> -	int ret;
>> +	int ret = check_fmt(file, p->type);
>> +
>> +	if (ret)
>> +		return ret;
>>  
>>  	v4l_sanitize_format(p);
>>  
>>  	switch (p->type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_try_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>>  		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
>> @@ -1606,27 +1574,27 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>  		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_overlay))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.win);
>>  		return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_vbi_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>>  		return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>>  		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>>  		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
>> @@ -1634,37 +1602,37 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>  		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_overlay))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.win);
>>  		return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_vbi_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_vbi_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>>  		return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>>  		return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_try_fmt_sdr_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_sdr_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>>  		return ops->vidioc_try_fmt_sdr_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_try_fmt_sdr_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_sdr_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>>  		return ops->vidioc_try_fmt_sdr_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_meta_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_meta_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.meta);
>>  		return ops->vidioc_try_fmt_meta_cap(file, fh, arg);
>> -- 
>> 2.15.1
>>
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 08/12] v4l2-compat-ioctl32.c: fix ctrl_is_pointer
  2018-01-27 23:18   ` Sakari Ailus
@ 2018-01-29 14:11     ` Mauro Carvalho Chehab
  0 siblings, 0 replies; 38+ messages in thread
From: Mauro Carvalho Chehab @ 2018-01-29 14:11 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Hans Verkuil, linux-media, Daniel Mentz, Hans Verkuil

Em Sun, 28 Jan 2018 01:18:57 +0200
Sakari Ailus <sakari.ailus@iki.fi> escreveu:

> Hi Hans,
> 

...

> > +
> > +	if (ops->vidioc_query_ext_ctrl)
> > +		ret = ops->vidioc_query_ext_ctrl(file, fh, &qec);  
> 
> Is there a need for this?
> 
> The only driver implementing vidioc_query_ext_ctrl op is the uvcvideo
> driver --- and it does not support string controls.

Well, one day, it could be added there, or some other driver may
need to implement it. So, better safe than sorry. I would keep it
(either as-is or with your suggestion)


Cheers,
Mauro

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 09/12] v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32
  2018-01-29  9:47   ` Sakari Ailus
@ 2018-01-29 14:13     ` Mauro Carvalho Chehab
  2018-01-29 21:00       ` Sakari Ailus
  0 siblings, 1 reply; 38+ messages in thread
From: Mauro Carvalho Chehab @ 2018-01-29 14:13 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Hans Verkuil, linux-media, Daniel Mentz, Hans Verkuil

Em Mon, 29 Jan 2018 11:47:42 +0200
Sakari Ailus <sakari.ailus@iki.fi> escreveu:


> > +	compat_caddr_t p;
> > +
> >  	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) ||
> >  	    put_user(kp->global_alpha, &up->global_alpha))
> >  		return -EFAULT;  
> 
> One more newline here?

Why? A new line here would be weird.





Cheers,
Mauro

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32
  2018-01-26 12:43 ` [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32 Hans Verkuil
@ 2018-01-29 15:14   ` Mauro Carvalho Chehab
  2018-01-29 17:06   ` Sakari Ailus
  1 sibling, 0 replies; 38+ messages in thread
From: Mauro Carvalho Chehab @ 2018-01-29 15:14 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Em Fri, 26 Jan 2018 13:43:27 +0100
Hans Verkuil <hverkuil@xs4all.nl> escreveu:

> From: Daniel Mentz <danielmentz@google.com>
> 
> The 32-bit compat v4l2 ioctl is implemented based on its 64-bit
> equivalent. It converts 32-bit data structures into its 64-bit
> equivalents and needs to provide the data to the 64-bit ioctl in user
> space memory which is commonly allocated using
> compat_alloc_user_space(). However, due to how that function is
> implemented, it can only be called a single time for every syscall
> invocation.  Supposedly to avoid this limitation, the existing code uses
> a mix of memory from the kernel stack and memory allocated through
> compat_alloc_user_space(). Under normal circumstances, this would not
> work, because the 64-bit ioctl expects all pointers to point to user
> space memory. As a workaround, set_fs(KERNEL_DS) is called to
> temporarily disable this extra safety check and allow kernel pointers.
> However, this might introduce a security vulnerability: The
> result of the 32-bit to 64-bit conversion is writeable by user space
> because the output buffer has been allocated via
> compat_alloc_user_space(). A malicious user space process could then
> manipulate pointers inside this output buffer, and due to the previous
> set_fs(KERNEL_DS) call, functions like get_user() or put_user() no longer
> prevent kernel memory access.
> 
> The new approach is to pre-calculate the total amount of user space
> memory that is needed, allocate it using compat_alloc_user_space() and
> then divide up the allocated memory to accommodate all data structures
> that need to be converted.
> 
> An alternative approach would have been to retain the union type karg
> that they allocated on the kernel stack in do_video_ioctl(), copy all
> data from user space into karg and then back to user space. However,
> we decided against this approach because it does not align with other
> compat syscall implementations. Instead, we tried to replicate the
> get_user/put_user pairs as found in other places in the kernel:
> 
> if (get_user(clipcount, &up->clipcount) ||
>     put_user(clipcount, &kp->clipcount)) return -EFAULT;
> 
> Notes from hans.verkuil@cisco.com:
> 
> This patch was taken from
> https://github.com/LineageOS/android_kernel_samsung_apq8084/commit/97b733953c06e4f0398ade18850f0817778255f7
> 
> Clearly nobody could be bothered to upstream this patch or at minimum
> tell us :-( We only heard about this a week ago.
> 
> This patch was rebased and cleaned up. Compared to the original I
> also swapped the order of the convert_in_user arguments so that they
> matched copy_in_user. It was hard to review otherwise. I also replaced
> the ALLOC_USER_SPACE/ALLOC_AND_GET by a normal function.
> 
> Signed-off-by: Daniel Mentz <danielmentz@google.com>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>

I suspect you should add also a Co-Developed-by tag, as defined by
this changeset (at docs-next):


commit 8ee25f6fcfa967da26c5dfa43c731a61f84f7404
Author: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Date:   Thu Nov 16 14:23:09 2017 +0100

    Documentation/process: add Co-Developed-by: tag for patches with multiple authors
    
    Sometimes a single patch is the result of multiple authors.  As git only
    can have one "author" of a patch, it is still good to properly give
    credit to the other developers of a commit.  To address this, document
    the "Co-Developed-by:" tag which can be used to show other authors of
    the patch.
    
    Note, these other authors must also provide a Signed-off-by: tag as it
    is their work that is being submitted here.
    
    Reported-by: Thomas Gleixner <tglx@linutronix.de>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
    Acked-by: Borislav Petkov <bp@suse.de>
    Signed-off-by: Jonathan Corbet <corbet@lwn.net>

diff --git a/Documentation/process/5.Posting.rst b/Documentation/process/5.Posting.rst
index 1b7728b19ea7..645fa9c7388a 100644
--- a/Documentation/process/5.Posting.rst
+++ b/Documentation/process/5.Posting.rst
@@ -213,6 +213,11 @@ The tags in common use are:
    which can be found in Documentation/process/submitting-patches.rst.  Code without a
    proper signoff cannot be merged into the mainline.
 
+ - Co-Developed-by: states that the patch was also created by another developer
+   along with the original author.  This is useful at times when multiple
+   people work on a single patch.  Note, this person also needs to have a
+   Signed-off-by: line in the patch as well.
+
  - Acked-by: indicates an agreement by another developer (often a
    maintainer of the relevant code) that the patch is appropriate for
    inclusion into the kernel.



> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 700 ++++++++++++++++----------
>  1 file changed, 448 insertions(+), 252 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index 2aa9b43daf60..27a5a0961cbd 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -22,6 +22,14 @@
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-ioctl.h>
>  
> +/* Use the same argument order as copy_in_user */
> +#define convert_in_user(to, from)			\
> +({							\
> +	typeof(*from) val;				\
> +							\
> +	get_user(val, from) || put_user(val, to);	\
> +})
> +
>  static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  {
>  	long ret = -ENOIOCTLCMD;
> @@ -48,37 +56,41 @@ struct v4l2_window32 {
>  	__u8                    global_alpha;
>  };
>  
> -static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
> +static int get_v4l2_window32(struct v4l2_window __user *kp,
> +			     struct v4l2_window32 __user *up,
> +			     void __user *aux_buf, int aux_space)
>  {
>  	struct v4l2_clip32 __user *uclips;
>  	struct v4l2_clip __user *kclips;
>  	compat_caddr_t p;
> -	u32 n;
> +	u32 clipcount;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> -	    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) ||
> -	    get_user(kp->global_alpha, &up->global_alpha))
> +	    copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
> +	    convert_in_user(&kp->field, &up->field) ||
> +	    convert_in_user(&kp->chromakey, &up->chromakey) ||
> +	    convert_in_user(&kp->global_alpha, &up->global_alpha) ||
> +	    get_user(clipcount, &up->clipcount) ||
> +	    put_user(clipcount, &kp->clipcount))
>  		return -EFAULT;
> -	if (kp->clipcount > 2048)
> +	if (clipcount > 2048)
>  		return -EINVAL;
> -	if (!kp->clipcount) {
> -		kp->clips = NULL;
> -		return 0;
> -	}
> +	if (!clipcount)
> +		return put_user(NULL, &kp->clips) ? -EFAULT : 0;
>  
> -	n = kp->clipcount;
>  	if (get_user(p, &up->clips))
>  		return -EFAULT;
>  	uclips = compat_ptr(p);
> -	kclips = compat_alloc_user_space(n * sizeof(*kclips));
> -	kp->clips = kclips;
> -	while (n--) {
> +	if (aux_space < clipcount * sizeof(*kclips))
> +		return -EFAULT;
> +	kclips = aux_buf;
> +	if (put_user(kclips, &kp->clips))
> +		return -EFAULT;
> +
> +	while (clipcount--) {
>  		if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
>  			return -EFAULT;
> -		if (put_user(n ? kclips + 1 : NULL, &kclips->next))
> +		if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next))
>  			return -EFAULT;
>  		uclips++;
>  		kclips++;
> @@ -86,26 +98,28 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
>  	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)
>  {
>  	struct v4l2_clip __user *kclips = kp->clips;
>  	struct v4l2_clip32 __user *uclips;
> -	int n = kp->clipcount;
>  	compat_caddr_t p;
> -
> -	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) ||
> -	    put_user(kp->global_alpha, &up->global_alpha))
> +	u32 clipcount;
> +
> +	if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
> +	    convert_in_user(&up->field, &kp->field) ||
> +	    convert_in_user(&up->chromakey, &kp->chromakey) ||
> +	    convert_in_user(&up->global_alpha, &kp->global_alpha) ||
> +	    get_user(clipcount, &kp->clipcount) ||
> +	    put_user(clipcount, &up->clipcount))
>  		return -EFAULT;
> -	if (!kp->clipcount)
> +	if (!clipcount)
>  		return 0;
>  
>  	if (get_user(p, &up->clips))
>  		return -EFAULT;
>  	uclips = compat_ptr(p);
> -	while (n--) {
> +	while (clipcount--) {
>  		if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
>  			return -EFAULT;
>  		uclips++;
> @@ -145,107 +159,161 @@ 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: {
> +		u32 clipcount;
> +
> +		if (get_user(clipcount, &up->fmt.win.clipcount))
> +			return -EFAULT;
> +		if (clipcount > 2048)
> +			return -EINVAL;
> +		return clipcount * sizeof(struct v4l2_clip);
> +	}
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static int bufsize_v4l2_format32(struct v4l2_format32 __user *up)
> +{
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)))
> +		return -EFAULT;
> +	return __bufsize_v4l2_format32(up);
> +}
> +
> +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 copy_from_user(&kp->fmt.pix, &up->fmt.pix,
> -				      sizeof(kp->fmt.pix)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.pix, &up->fmt.pix,
> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		return copy_from_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
> -				      sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>  	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 copy_from_user(&kp->fmt.vbi, &up->fmt.vbi,
> -				      sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi,
> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		return copy_from_user(&kp->fmt.sliced, &up->fmt.sliced,
> -				      sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced,
> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		return copy_from_user(&kp->fmt.sdr, &up->fmt.sdr,
> -				      sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr,
> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
> -				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.meta, &up->fmt.meta,
> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>  	default:
>  		return -EINVAL;
>  	}
>  }
>  
> -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __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(*up)))
> +		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(*up)))
>  		return -EFAULT;
> -	return __get_v4l2_format32(kp, up);
> +	return __bufsize_v4l2_format32(&up->format);
>  }
>  
> -static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
> +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(*up)) ||
> -	    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)
>  {
> -	if (put_user(kp->type, &up->type))
> +	u32 type;
> +
> +	if (get_user(type, &kp->type))
>  		return -EFAULT;
>  
> -	switch (kp->type) {
> +	switch (type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		return copy_to_user(&up->fmt.pix, &kp->fmt.pix,
> -				    sizeof(kp->fmt.pix)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.pix, &kp->fmt.pix,
> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		return copy_to_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
> -				    sizeof(kp->fmt.pix_mp)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>  		return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> -		return copy_to_user(&up->fmt.vbi, &kp->fmt.vbi,
> -				    sizeof(kp->fmt.vbi)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi,
> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		return copy_to_user(&up->fmt.sliced, &kp->fmt.sliced,
> -				    sizeof(kp->fmt.sliced)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced,
> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		return copy_to_user(&up->fmt.sdr, &kp->fmt.sdr,
> -				    sizeof(kp->fmt.sdr)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr,
> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
> -				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.meta, &kp->fmt.meta,
> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>  	default:
>  		return -EINVAL;
>  	}
>  }
>  
> -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(*up)))
>  		return -EFAULT;
>  	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(*up)) ||
> -	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
> +	    copy_in_user(up, kp,
> +			 offsetof(struct v4l2_create_buffers32, format)) ||
> +	    copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>  		return -EFAULT;
>  	return __put_v4l2_format32(&kp->format, &up->format);
>  }
> @@ -259,25 +327,27 @@ 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(*up)) ||
> -	    get_user(kp->index, &up->index))
> +	    convert_in_user(&kp->index, &up->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(*up)) ||
> -	    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, sizeof(kp->reserved)))
> +	    convert_in_user(&up->index, &kp->index) ||
> +	    copy_in_user(&up->id, &kp->id, sizeof(up->id)) ||
> +	    copy_in_user(up->name, kp->name, 24) ||
> +	    copy_in_user(&up->frameperiod, &kp->frameperiod,
> +			 sizeof(up->frameperiod)) ||
> +	    convert_in_user(&up->framelines, &kp->framelines) ||
> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -317,10 +387,10 @@ struct v4l2_buffer32 {
>  	__u32			reserved;
>  };
>  
> -static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
> +static int get_v4l2_plane32(struct v4l2_plane __user *up,
> +			    struct v4l2_plane32 __user *up32,
>  			    enum v4l2_memory memory)
>  {
> -	void __user *up_pln;
>  	compat_long_t p;
>  
>  	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
> @@ -336,10 +406,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  			return -EFAULT;
>  		break;
>  	case 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;
>  		break;
>  	case V4L2_MEMORY_DMABUF:
> @@ -351,7 +419,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  	return 0;
>  }
>  
> -static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
> +static int put_v4l2_plane32(struct v4l2_plane __user *up,
> +			    struct v4l2_plane32 __user *up32,
>  			    enum v4l2_memory memory)
>  {
>  	unsigned long p;
> @@ -375,8 +444,7 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  			return -EFAULT;
>  		break;
>  	case V4L2_MEMORY_DMABUF:
> -		if (copy_in_user(&up32->m.fd, &up->m.fd,
> -				 sizeof(up->m.fd)))
> +		if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd)))
>  			return -EFAULT;
>  		break;
>  	}
> @@ -384,37 +452,68 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  	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(*up)) ||
> +	    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
> +		 */

Does the above comment makes sense? It is checking if length is
bigger than 8 (e. g. VIDEO_MAX_PLANES).

> +		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;
> +	int num_planes;
>  	int ret;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> -	    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(&kp->index, &up->index) ||
> +	    get_user(type, &up->type) ||
> +	    put_user(type, &kp->type) ||
> +	    convert_in_user(&kp->flags, &up->flags) ||
> +	    get_user(memory, &up->memory) ||
> +	    put_user(memory, &kp->memory) ||
> +	    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(&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))
>  			return -EFAULT;
>  
> -	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
> -		unsigned int num_planes;
> -
> -		if (kp->length == 0) {
> -			kp->m.planes = NULL;
> +	if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
> +		num_planes = length;
> +		if (num_planes == 0) {
>  			/* num_planes == 0 is legal, e.g. when userspace doesn't
>  			 * need planes array on DQBUF*/
> -			return 0;
> -		} else if (kp->length > VIDEO_MAX_PLANES) {
> +			return put_user(NULL, &kp->m.planes);
> +		} else if (num_planes > VIDEO_MAX_PLANES) {
>  			return -EINVAL;
>  		}
>  
> @@ -423,40 +522,47 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  
>  		uplane32 = compat_ptr(p);
>  		if (!access_ok(VERIFY_READ, uplane32,
> -			       kp->length * sizeof(*uplane32)))
> +				num_planes * sizeof(*uplane32)))
>  			return -EFAULT;
>  
>  		/* We don't really care if userspace decides to kill itself
>  		 * by passing a very big num_planes value */
> -		uplane = compat_alloc_user_space(kp->length * sizeof(*uplane));
> -		kp->m.planes = (__force struct v4l2_plane *)uplane;
> +		if (aux_space < num_planes * sizeof(*uplane))
> +			return -EFAULT;
> +
> +		uplane = aux_buf;
> +		if (put_user((__force struct v4l2_plane *)uplane,
> +					&kp->m.planes))
> +			return -EFAULT;
>  
> -		for (num_planes = 0; num_planes < kp->length; num_planes++) {
> -			ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
> +		while (--num_planes >= 0) {
> +			ret = get_v4l2_plane32(uplane, uplane32, memory);
>  			if (ret)
>  				return ret;
> -			++uplane;
> -			++uplane32;
> +			uplane++;
> +			uplane32++;
>  		}
>  	} else {
> -		switch (kp->memory) {
> +		switch (memory) {
>  		case V4L2_MEMORY_MMAP:
> -		case V4L2_MEMORY_OVERLAY:
> -			if (get_user(kp->m.offset, &up->m.offset))
> +			if (convert_in_user(&kp->m.offset, &up->m.offset))
>  				return -EFAULT;
>  			break;
> -		case V4L2_MEMORY_USERPTR:
> -			{
> -				compat_long_t tmp;
> -
> -				if (get_user(tmp, &up->m.userptr))
> -					return -EFAULT;
> +		case V4L2_MEMORY_USERPTR: {
> +			compat_long_t tmp;
>  
> -				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 (convert_in_user(&kp->m.offset, &up->m.offset))
> +				return -EFAULT;
>  			break;
>  		case V4L2_MEMORY_DMABUF:
> -			if (get_user(kp->m.fd, &up->m.fd))
> +			if (convert_in_user(&kp->m.fd, &up->m.fd))
>  				return -EFAULT;
>  			break;
>  		}
> @@ -465,8 +571,12 @@ 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;
> @@ -474,53 +584,60 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  	int ret;
>  
>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
> -	    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(&up->index, &kp->index) ||
> +	    get_user(type, &kp->type) ||
> +	    put_user(type, &up->type) ||
> +	    convert_in_user(&up->flags, &kp->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(kp->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(&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) ||
> +	    copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
> +	    convert_in_user(&up->sequence, &kp->sequence) ||
> +	    convert_in_user(&up->reserved2, &kp->reserved2) ||
> +	    convert_in_user(&up->reserved, &kp->reserved) ||
> +	    get_user(length, &kp->length) ||
> +	    put_user(length, &up->length))
>  		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 = (__force struct v4l2_plane __user *)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:
> -		case V4L2_MEMORY_OVERLAY:
> -			if (put_user(kp->m.offset, &up->m.offset))
> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
>  				return -EFAULT;
>  			break;
>  		case V4L2_MEMORY_USERPTR:
> -			if (put_user(kp->m.userptr, &up->m.userptr))
> +			if (convert_in_user(&up->m.userptr, &kp->m.userptr))
> +				return -EFAULT;
> +			break;
> +		case V4L2_MEMORY_OVERLAY:
> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
>  				return -EFAULT;
>  			break;
>  		case V4L2_MEMORY_DMABUF:
> -			if (put_user(kp->m.fd, &up->m.fd))
> +			if (convert_in_user(&up->m.fd, &kp->m.fd))
>  				return -EFAULT;
>  			break;
>  		}
> @@ -545,29 +662,32 @@ struct v4l2_framebuffer32 {
>  	} 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(*up)) ||
>  	    get_user(tmp, &up->base) ||
> -	    get_user(kp->capability, &up->capability) ||
> -	    get_user(kp->flags, &up->flags) ||
> -	    copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
> +	    put_user((__force void *)compat_ptr(tmp), &kp->base) ||
> +	    convert_in_user(&kp->capability, &up->capability) ||
> +	    convert_in_user(&kp->flags, &up->flags) ||
> +	    copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt)))
>  		return -EFAULT;
> -	kp->base = (__force void *)compat_ptr(tmp);
>  	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(*up)) ||
> -	    put_user(tmp, &up->base) ||
> -	    put_user(kp->capability, &up->capability) ||
> -	    put_user(kp->flags, &up->flags) ||
> -	    copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
> +	    get_user(base, &kp->base) ||
> +	    put_user(ptr_to_compat(base), &up->base) ||
> +	    convert_in_user(&up->capability, &kp->capability) ||
> +	    convert_in_user(&up->flags, &kp->flags) ||
> +	    copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -586,16 +706,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 *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(*up)))
> +	if (copy_in_user(kp, up, sizeof(*up)))
>  		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(*up)))
> +	if (copy_in_user(up, kp, sizeof(*up)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -648,39 +770,58 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id)
>  	return !ret && (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
>  }
>  
> +static int bufsize_v4l2_ext_controls32(struct v4l2_ext_controls32 __user *up)
> +{
> +	u32 count;
> +
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> +	    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 file *file,
> -				   struct v4l2_ext_controls *kp,
> -				   struct v4l2_ext_controls32 __user *up)
> +				   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;
> +	u32 count;
>  	unsigned int n;
>  	compat_caddr_t p;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> -	    get_user(kp->which, &up->which) ||
> -	    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(&kp->which, &up->which) ||
> +	    get_user(count, &up->count) ||
> +	    put_user(count, &kp->count) ||
> +	    convert_in_user(&kp->error_idx, &up->error_idx) ||
> +	    copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
>  		return -EFAULT;
> -	if (kp->count == 0) {
> -		kp->controls = NULL;
> -		return 0;
> -	} else if (kp->count > V4L2_CID_MAX_CTRLS) {
> +	if (count == 0)
> +		return put_user(NULL, &kp->controls);
> +	else if (kp->count > V4L2_CID_MAX_CTRLS)
>  		return -EINVAL;
> -	}
>  	if (get_user(p, &up->controls))
>  		return -EFAULT;
>  	ucontrols = compat_ptr(p);
> -	if (!access_ok(VERIFY_READ, ucontrols, kp->count * sizeof(*ucontrols)))
> +	if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols)))
> +		return -EFAULT;
> +	if (aux_space < count * sizeof(*kcontrols))
>  		return -EFAULT;
> -	kcontrols = compat_alloc_user_space(kp->count * sizeof(*kcontrols));
> -	kp->controls = (__force struct v4l2_ext_control *)kcontrols;
> -	for (n = 0; n < kp->count; n++) {
> +	kcontrols = aux_buf;
> +	if (put_user((__force struct v4l2_ext_control *)kcontrols,
> +		     &kp->controls))
> +		return -EFAULT;
> +
> +	for (n = 0; n < count; n++) {
>  		u32 id;
>  
>  		if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
>  			return -EFAULT;
> +
>  		if (get_user(id, &kcontrols->id))
>  			return -EFAULT;
>  		if (ctrl_is_pointer(file, id)) {
> @@ -699,36 +840,47 @@ static int get_v4l2_ext_controls32(struct file *file,
>  }
>  
>  static int put_v4l2_ext_controls32(struct file *file,
> -				   struct v4l2_ext_controls *kp,
> +				   struct v4l2_ext_controls __user *kp,
>  				   struct v4l2_ext_controls32 __user *up)
>  {
>  	struct v4l2_ext_control32 __user *ucontrols;
> -	struct v4l2_ext_control __user *kcontrols =
> -		(__force struct v4l2_ext_control __user *)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(*up)) ||
> -	    put_user(kp->which, &up->which) ||
> -	    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(&up->which, &kp->which) ||
> +	    get_user(count, &kp->count) ||
> +	    put_user(count, &up->count) ||
> +	    convert_in_user(&up->error_idx, &kp->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(*ucontrols)))
> +	if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols)))
>  		return -EFAULT;
>  
> -	while (--n >= 0) {
> -		unsigned size = sizeof(*ucontrols);
> +	for (n = 0; n < count; n++) {
> +		unsigned int size = sizeof(*ucontrols);
>  		u32 id;
>  
> +		if (copy_in_user(&ucontrols->id, &kcontrols->id,
> +				 sizeof(ucontrols->id)) ||
> +		    copy_in_user(&ucontrols->size, &kcontrols->size,
> +				 sizeof(ucontrols->size)) ||
> +		    copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
> +				 sizeof(ucontrols->reserved2)))
> +			return -EFAULT;
> +
>  		if (get_user(id, &kcontrols->id))
>  			return -EFAULT;
> +
>  		/* Do not modify the pointer when copying a pointer control.
>  		   The contents of the pointer was changed, not the pointer
>  		   itself. */
> @@ -736,6 +888,7 @@ static int put_v4l2_ext_controls32(struct file *file,
>  			size -= sizeof(ucontrols->value64);
>  		if (copy_in_user(ucontrols, kcontrols, size))
>  			return -EFAULT;
> +
>  		ucontrols++;
>  		kcontrols++;
>  	}
> @@ -755,17 +908,18 @@ 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(*up)) ||
> -	    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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
> -	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
> -	    put_user(kp->id, &up->id) ||
> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
> +	    convert_in_user(&up->type, &kp->type) ||
> +	    copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
> +	    convert_in_user(&up->pending, &kp->pending) ||
> +	    convert_in_user(&up->sequence, &kp->sequence) ||
> +	    convert_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
> +	    convert_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) ||
> +	    convert_in_user(&up->id, &kp->id) ||
> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -778,31 +932,34 @@ struct v4l2_edid32 {
>  	compat_caddr_t edid;
>  };
>  
> -static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
> +static int get_v4l2_edid32(struct v4l2_edid __user *kp,
> +			   struct v4l2_edid32 __user *up)
>  {
> -	u32 tmp;
> +	compat_uptr_t tmp;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> -	    get_user(kp->pad, &up->pad) ||
> -	    get_user(kp->start_block, &up->start_block) ||
> -	    get_user(kp->blocks, &up->blocks) ||
> +	    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(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 = (__force u8 *)compat_ptr(tmp);
>  	return 0;
>  }
>  
> -static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
> +static int put_v4l2_edid32(struct v4l2_edid __user *kp,
> +			   struct v4l2_edid32 __user *up)
>  {
> -	u32 tmp = (u32)((unsigned long)kp->edid);
> +	void *edid;
>  
>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
> -	    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(up->reserved, kp->reserved, sizeof(up->reserved)))
> +	    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(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;
>  }
> @@ -835,22 +992,30 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>  #define VIDIOC_G_OUTPUT32	_IOR ('V', 46, s32)
>  #define VIDIOC_S_OUTPUT32	_IOWR('V', 47, s32)
>  
> +static void __user *alloc_userspace(unsigned int size, int aux_space, long *err)
> +{
> +	void __user *up_native;
> +
> +	if (aux_space < 0) {
> +		*err = aux_space;
> +		return NULL;
> +	}
> +	up_native = compat_alloc_user_space(size + aux_space);
> +	if (!up_native)
> +		*err = -ENOMEM;
> +	else if (clear_user(up_native, size))
> +		*err = -EFAULT;
> +	else
> +		return up_native;
> +	return NULL;
> +}
> +
>  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_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;
>  
> @@ -889,30 +1054,47 @@ 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_userspace(sizeof(unsigned int __user),
> +					    0, &err);
> +		if (!err && convert_in_user((unsigned int __user *)up_native,
> +					    (compat_uint_t __user *)up))
> +			return -EFAULT;
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_G_INPUT:
>  	case VIDIOC_G_OUTPUT:
> +		up_native = alloc_userspace(sizeof(unsigned int __user),
> +					    0, &err);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_G_EDID:
>  	case VIDIOC_S_EDID:
> -		err = get_v4l2_edid32(&karg.v2edid, up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_edid), 0, &err);
> +		err = err ? : get_v4l2_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);
> +		aux_space = bufsize_v4l2_format32(up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_format),
> +					    aux_space, &err);
> +		aux_buf = up_native + sizeof(struct v4l2_format);
> +		err = err ? : get_v4l2_format32(up_native, up,
> +						aux_buf, aux_space);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_CREATE_BUFS:
> -		err = get_v4l2_create32(&karg.v2crt, up);
> +		aux_space = bufsize_v4l2_create32(up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_create_buffers),
> +					    aux_space, &err);
> +		aux_buf = up_native + sizeof(struct v4l2_create_buffers);
> +		err = err ? : get_v4l2_create32(up_native, up,
> +						aux_buf, aux_space);
>  		compatible_arg = 0;
>  		break;
>  
> @@ -920,36 +1102,54 @@ 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);
> +		aux_space = bufsize_v4l2_buffer32(up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_buffer),
> +					    aux_space, &err);
> +		aux_buf = up_native + sizeof(struct v4l2_buffer);
> +		err = err ? : get_v4l2_buffer32(up_native, up,
> +						aux_buf, aux_space);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_S_FBUF:
> -		err = get_v4l2_framebuffer32(&karg.v2fb, up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
> +					    0, &err);
> +		err = err ? : get_v4l2_framebuffer32(up_native, up);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_G_FBUF:
> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
> +					    0, &err);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_ENUMSTD:
> -		err = get_v4l2_standard32(&karg.v2s, up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_standard),
> +					    0, &err);
> +		err = err ? : get_v4l2_standard32(up_native, up);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_ENUMINPUT:
> -		err = get_v4l2_input32(&karg.v2i, up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_input), 0, &err);
> +		err = 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(file, &karg.v2ecs, up);
> +		aux_space = bufsize_v4l2_ext_controls32(up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_ext_controls),
> +					    aux_space, &err);
> +		aux_buf = up_native + sizeof(struct v4l2_ext_controls);
> +		err = err ? : get_v4l2_ext_controls32(file, up_native, up,
> +						      aux_buf, aux_space);
>  		compatible_arg = 0;
>  		break;
>  	case VIDIOC_DQEVENT:
> +		up_native = alloc_userspace(sizeof(struct v4l2_event), 0, &err);
>  		compatible_arg = 0;
>  		break;
>  	}
> @@ -958,13 +1158,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);
>  
>  	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
>  		return err;
> @@ -976,11 +1171,11 @@ 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(file, &karg.v2ecs, up))
> +		if (put_v4l2_ext_controls32(file, up_native, up))
>  			err = -EFAULT;
>  		break;
>  	case VIDIOC_S_EDID:
> -		if (put_v4l2_edid32(&karg.v2edid, up))
> +		if (put_v4l2_edid32(up_native, up))
>  			err = -EFAULT;
>  		break;
>  	}
> @@ -992,44 +1187,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((compat_uint_t __user *)up,
> +				      ((unsigned int __user *)up_native));
>  		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_G_EDID:
> -		err = put_v4l2_edid32(&karg.v2edid, up);
> +		err = put_v4l2_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_PREPARE_BUF:
>  	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;




Cheers,
Mauro

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32
  2018-01-26 12:43 ` [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32 Hans Verkuil
  2018-01-29 15:14   ` Mauro Carvalho Chehab
@ 2018-01-29 17:06   ` Sakari Ailus
  2018-01-29 17:41     ` Hans Verkuil
  2018-01-30  9:37     ` Hans Verkuil
  1 sibling, 2 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-29 17:06 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

Thanks for your efforts on this patch and the patchset. Please see my comments below.

On Fri, Jan 26, 2018 at 01:43:27PM +0100, Hans Verkuil wrote:
> From: Daniel Mentz <danielmentz@google.com>
> 
> The 32-bit compat v4l2 ioctl is implemented based on its 64-bit

s/v4l2 ioctl/V4L2 IOCTL handling/

?

> equivalent. It converts 32-bit data structures into its 64-bit
> equivalents and needs to provide the data to the 64-bit ioctl in user
> space memory which is commonly allocated using
> compat_alloc_user_space(). However, due to how that function is
> implemented, it can only be called a single time for every syscall
> invocation.  Supposedly to avoid this limitation, the existing code uses
> a mix of memory from the kernel stack and memory allocated through
> compat_alloc_user_space(). Under normal circumstances, this would not
> work, because the 64-bit ioctl expects all pointers to point to user
> space memory. As a workaround, set_fs(KERNEL_DS) is called to
> temporarily disable this extra safety check and allow kernel pointers.
> However, this might introduce a security vulnerability: The
> result of the 32-bit to 64-bit conversion is writeable by user space
> because the output buffer has been allocated via
> compat_alloc_user_space(). A malicious user space process could then
> manipulate pointers inside this output buffer, and due to the previous
> set_fs(KERNEL_DS) call, functions like get_user() or put_user() no longer
> prevent kernel memory access.
> 
> The new approach is to pre-calculate the total amount of user space
> memory that is needed, allocate it using compat_alloc_user_space() and
> then divide up the allocated memory to accommodate all data structures
> that need to be converted.
> 
> An alternative approach would have been to retain the union type karg
> that they allocated on the kernel stack in do_video_ioctl(), copy all
> data from user space into karg and then back to user space. However,
> we decided against this approach because it does not align with other
> compat syscall implementations. Instead, we tried to replicate the
> get_user/put_user pairs as found in other places in the kernel:
> 
> if (get_user(clipcount, &up->clipcount) ||
>     put_user(clipcount, &kp->clipcount)) return -EFAULT;
> 
> Notes from hans.verkuil@cisco.com:
> 
> This patch was taken from
> https://github.com/LineageOS/android_kernel_samsung_apq8084/commit/97b733953c06e4f0398ade18850f0817778255f7
> 
> Clearly nobody could be bothered to upstream this patch or at minimum
> tell us :-( We only heard about this a week ago.

This is very sad indeed. I would really hope that we'll never run into
something this again!

> 
> This patch was rebased and cleaned up. Compared to the original I
> also swapped the order of the convert_in_user arguments so that they
> matched copy_in_user. It was hard to review otherwise. I also replaced
> the ALLOC_USER_SPACE/ALLOC_AND_GET by a normal function.

The result looks more complicated than it should be.

I wonder if the result could be made cleaner by separating the argument
checking and copying from / to user space in video_usercopy. That
separation almost completely exists already. Rather than mangling the
compat argument struct to look like a user-provided 64-bit argument struct,
copying the arguments from and to the user space would be separately done
to 32-bit compat IOCTL arguments.

I suppose that wouldn't be a trivial change either, so for now I'd maintain
the approach and later consider how to make this more maintainable in the
future. Still, that approach is quite different and it'd be easier to
switch from status quo rather than this last patch, suggesting to start
with a revert, should we embark into this direction.

What do you think?

> 
> Signed-off-by: Daniel Mentz <danielmentz@google.com>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 700 ++++++++++++++++----------
>  1 file changed, 448 insertions(+), 252 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> index 2aa9b43daf60..27a5a0961cbd 100644
> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> @@ -22,6 +22,14 @@
>  #include <media/v4l2-ctrls.h>
>  #include <media/v4l2-ioctl.h>
>  
> +/* Use the same argument order as copy_in_user */
> +#define convert_in_user(to, from)			\

This doesn't really convert anything. How about calling it assign_in_user,
for example?

> +({							\
> +	typeof(*from) val;				\

"val" is very short, easily leading to hard-to-find namespace collisions.
At least the versions of GCC I've used happily get this wrong without a
warning.

How about __convert_in_user_tmp instead?

> +							\
> +	get_user(val, from) || put_user(val, to);	\
> +})
> +
>  static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  {
>  	long ret = -ENOIOCTLCMD;
> @@ -48,37 +56,41 @@ struct v4l2_window32 {
>  	__u8                    global_alpha;
>  };
>  
> -static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
> +static int get_v4l2_window32(struct v4l2_window __user *kp,
> +			     struct v4l2_window32 __user *up,
> +			     void __user *aux_buf, int aux_space)
>  {
>  	struct v4l2_clip32 __user *uclips;
>  	struct v4l2_clip __user *kclips;
>  	compat_caddr_t p;
> -	u32 n;
> +	u32 clipcount;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> -	    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) ||
> -	    get_user(kp->global_alpha, &up->global_alpha))
> +	    copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
> +	    convert_in_user(&kp->field, &up->field) ||
> +	    convert_in_user(&kp->chromakey, &up->chromakey) ||
> +	    convert_in_user(&kp->global_alpha, &up->global_alpha) ||
> +	    get_user(clipcount, &up->clipcount) ||
> +	    put_user(clipcount, &kp->clipcount))
>  		return -EFAULT;
> -	if (kp->clipcount > 2048)
> +	if (clipcount > 2048)
>  		return -EINVAL;
> -	if (!kp->clipcount) {
> -		kp->clips = NULL;
> -		return 0;
> -	}
> +	if (!clipcount)
> +		return put_user(NULL, &kp->clips) ? -EFAULT : 0;

Isn't this what put_user would return anyway?

>  
> -	n = kp->clipcount;
>  	if (get_user(p, &up->clips))
>  		return -EFAULT;
>  	uclips = compat_ptr(p);
> -	kclips = compat_alloc_user_space(n * sizeof(*kclips));
> -	kp->clips = kclips;
> -	while (n--) {
> +	if (aux_space < clipcount * sizeof(*kclips))
> +		return -EFAULT;
> +	kclips = aux_buf;
> +	if (put_user(kclips, &kp->clips))
> +		return -EFAULT;
> +
> +	while (clipcount--) {
>  		if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
>  			return -EFAULT;
> -		if (put_user(n ? kclips + 1 : NULL, &kclips->next))
> +		if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next))
>  			return -EFAULT;
>  		uclips++;
>  		kclips++;
> @@ -86,26 +98,28 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
>  	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)
>  {
>  	struct v4l2_clip __user *kclips = kp->clips;
>  	struct v4l2_clip32 __user *uclips;
> -	int n = kp->clipcount;
>  	compat_caddr_t p;
> -
> -	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) ||
> -	    put_user(kp->global_alpha, &up->global_alpha))
> +	u32 clipcount;
> +
> +	if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
> +	    convert_in_user(&up->field, &kp->field) ||
> +	    convert_in_user(&up->chromakey, &kp->chromakey) ||
> +	    convert_in_user(&up->global_alpha, &kp->global_alpha) ||
> +	    get_user(clipcount, &kp->clipcount) ||
> +	    put_user(clipcount, &up->clipcount))
>  		return -EFAULT;
> -	if (!kp->clipcount)
> +	if (!clipcount)
>  		return 0;
>  
>  	if (get_user(p, &up->clips))
>  		return -EFAULT;
>  	uclips = compat_ptr(p);
> -	while (n--) {
> +	while (clipcount--) {
>  		if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
>  			return -EFAULT;
>  		uclips++;
> @@ -145,107 +159,161 @@ 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)

This is effectively returning the size of the non-compat struct, or just
the part of which is variable. How about calling it __bufsize_v4l2_format
instead? The same for other such functions.

I'd also separate the size calculated by the function and the return value.

>  {
> -	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: {
> +		u32 clipcount;
> +
> +		if (get_user(clipcount, &up->fmt.win.clipcount))
> +			return -EFAULT;
> +		if (clipcount > 2048)
> +			return -EINVAL;
> +		return clipcount * sizeof(struct v4l2_clip);
> +	}
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static int bufsize_v4l2_format32(struct v4l2_format32 __user *up)
> +{
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)))
> +		return -EFAULT;

Newline?

> +	return __bufsize_v4l2_format32(up);
> +}
> +
> +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 copy_from_user(&kp->fmt.pix, &up->fmt.pix,
> -				      sizeof(kp->fmt.pix)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.pix, &up->fmt.pix,
> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		return copy_from_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
> -				      sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>  	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 copy_from_user(&kp->fmt.vbi, &up->fmt.vbi,
> -				      sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi,
> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		return copy_from_user(&kp->fmt.sliced, &up->fmt.sliced,
> -				      sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced,
> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		return copy_from_user(&kp->fmt.sdr, &up->fmt.sdr,
> -				      sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr,
> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
> -				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
> +		return copy_in_user(&kp->fmt.meta, &up->fmt.meta,
> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>  	default:
>  		return -EINVAL;
>  	}
>  }
>  
> -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __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(*up)))
> +		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(*up)))
>  		return -EFAULT;
> -	return __get_v4l2_format32(kp, up);
> +	return __bufsize_v4l2_format32(&up->format);
>  }
>  
> -static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
> +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(*up)) ||
> -	    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)
>  {
> -	if (put_user(kp->type, &up->type))
> +	u32 type;
> +
> +	if (get_user(type, &kp->type))
>  		return -EFAULT;
>  
> -	switch (kp->type) {
> +	switch (type) {
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> -		return copy_to_user(&up->fmt.pix, &kp->fmt.pix,
> -				    sizeof(kp->fmt.pix)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.pix, &kp->fmt.pix,
> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> -		return copy_to_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
> -				    sizeof(kp->fmt.pix_mp)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>  		return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> -		return copy_to_user(&up->fmt.vbi, &kp->fmt.vbi,
> -				    sizeof(kp->fmt.vbi)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi,
> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> -		return copy_to_user(&up->fmt.sliced, &kp->fmt.sliced,
> -				    sizeof(kp->fmt.sliced)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced,
> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> -		return copy_to_user(&up->fmt.sdr, &kp->fmt.sdr,
> -				    sizeof(kp->fmt.sdr)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr,
> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>  	case V4L2_BUF_TYPE_META_CAPTURE:
> -		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
> -				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
> +		return copy_in_user(&up->fmt.meta, &kp->fmt.meta,
> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>  	default:
>  		return -EINVAL;
>  	}
>  }
>  
> -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(*up)))

For some pointers we do perform such checks, for others we don't. While
this patch doesn't change this, it'd be good to align this by either
introducing it everywhere or removing it everywhere.

I guess that can be done separately.

>  		return -EFAULT;
>  	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(*up)) ||
> -	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
> +	    copy_in_user(up, kp,
> +			 offsetof(struct v4l2_create_buffers32, format)) ||
> +	    copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>  		return -EFAULT;
>  	return __put_v4l2_format32(&kp->format, &up->format);
>  }
> @@ -259,25 +327,27 @@ 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(*up)) ||
> -	    get_user(kp->index, &up->index))
> +	    convert_in_user(&kp->index, &up->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(*up)) ||
> -	    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, sizeof(kp->reserved)))
> +	    convert_in_user(&up->index, &kp->index) ||
> +	    copy_in_user(&up->id, &kp->id, sizeof(up->id)) ||

Why not convert_in_user()?

> +	    copy_in_user(up->name, kp->name, 24) ||
> +	    copy_in_user(&up->frameperiod, &kp->frameperiod,
> +			 sizeof(up->frameperiod)) ||
> +	    convert_in_user(&up->framelines, &kp->framelines) ||
> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -317,10 +387,10 @@ struct v4l2_buffer32 {
>  	__u32			reserved;
>  };
>  
> -static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
> +static int get_v4l2_plane32(struct v4l2_plane __user *up,
> +			    struct v4l2_plane32 __user *up32,
>  			    enum v4l2_memory memory)
>  {
> -	void __user *up_pln;
>  	compat_long_t p;

Shouldn't this be compat_ulong_t?

>  
>  	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
> @@ -336,10 +406,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  			return -EFAULT;
>  		break;
>  	case 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;
>  		break;
>  	case V4L2_MEMORY_DMABUF:
> @@ -351,7 +419,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  	return 0;
>  }
>  
> -static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
> +static int put_v4l2_plane32(struct v4l2_plane __user *up,
> +			    struct v4l2_plane32 __user *up32,
>  			    enum v4l2_memory memory)
>  {
>  	unsigned long p;
> @@ -375,8 +444,7 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  			return -EFAULT;
>  		break;
>  	case V4L2_MEMORY_DMABUF:
> -		if (copy_in_user(&up32->m.fd, &up->m.fd,
> -				 sizeof(up->m.fd)))
> +		if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd)))
>  			return -EFAULT;
>  		break;
>  	}
> @@ -384,37 +452,68 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>  	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(*up)) ||
> +	    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;
> +	int num_planes;

u32?

>  	int ret;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> -	    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(&kp->index, &up->index) ||
> +	    get_user(type, &up->type) ||
> +	    put_user(type, &kp->type) ||
> +	    convert_in_user(&kp->flags, &up->flags) ||
> +	    get_user(memory, &up->memory) ||
> +	    put_user(memory, &kp->memory) ||
> +	    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(&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))
>  			return -EFAULT;
>  
> -	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
> -		unsigned int num_planes;
> -
> -		if (kp->length == 0) {
> -			kp->m.planes = NULL;
> +	if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
> +		num_planes = length;

Do you need a local variable for just that?

> +		if (num_planes == 0) {
>  			/* num_planes == 0 is legal, e.g. when userspace doesn't
>  			 * need planes array on DQBUF*/
> -			return 0;
> -		} else if (kp->length > VIDEO_MAX_PLANES) {
> +			return put_user(NULL, &kp->m.planes);
> +		} else if (num_planes > VIDEO_MAX_PLANES) {
>  			return -EINVAL;
>  		}
>  
> @@ -423,40 +522,47 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  
>  		uplane32 = compat_ptr(p);
>  		if (!access_ok(VERIFY_READ, uplane32,
> -			       kp->length * sizeof(*uplane32)))
> +				num_planes * sizeof(*uplane32)))

Alignment.

>  			return -EFAULT;
>  
>  		/* We don't really care if userspace decides to kill itself
>  		 * by passing a very big num_planes value */
> -		uplane = compat_alloc_user_space(kp->length * sizeof(*uplane));
> -		kp->m.planes = (__force struct v4l2_plane *)uplane;
> +		if (aux_space < num_planes * sizeof(*uplane))
> +			return -EFAULT;
> +
> +		uplane = aux_buf;
> +		if (put_user((__force struct v4l2_plane *)uplane,
> +					&kp->m.planes))
> +			return -EFAULT;
>  
> -		for (num_planes = 0; num_planes < kp->length; num_planes++) {
> -			ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
> +		while (--num_planes >= 0) {

while (num_planes--) {

?

> +			ret = get_v4l2_plane32(uplane, uplane32, memory);
>  			if (ret)
>  				return ret;
> -			++uplane;
> -			++uplane32;
> +			uplane++;
> +			uplane32++;
>  		}
>  	} else {
> -		switch (kp->memory) {
> +		switch (memory) {
>  		case V4L2_MEMORY_MMAP:
> -		case V4L2_MEMORY_OVERLAY:
> -			if (get_user(kp->m.offset, &up->m.offset))
> +			if (convert_in_user(&kp->m.offset, &up->m.offset))
>  				return -EFAULT;
>  			break;
> -		case V4L2_MEMORY_USERPTR:
> -			{
> -				compat_long_t tmp;
> -
> -				if (get_user(tmp, &up->m.userptr))
> -					return -EFAULT;
> +		case V4L2_MEMORY_USERPTR: {
> +			compat_long_t tmp;

compat_ulong_t, I'd call it "userptr". The latter is entirely up to you.

>  
> -				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 (convert_in_user(&kp->m.offset, &up->m.offset))
> +				return -EFAULT;

Is there a reason to split this off from MMAP handling? The code is the
same.

>  			break;
>  		case V4L2_MEMORY_DMABUF:
> -			if (get_user(kp->m.fd, &up->m.fd))
> +			if (convert_in_user(&kp->m.fd, &up->m.fd))
>  				return -EFAULT;
>  			break;
>  		}
> @@ -465,8 +571,12 @@ 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;
> @@ -474,53 +584,60 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>  	int ret;
>  
>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
> -	    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(&up->index, &kp->index) ||
> +	    get_user(type, &kp->type) ||
> +	    put_user(type, &up->type) ||
> +	    convert_in_user(&up->flags, &kp->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(kp->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(&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) ||
> +	    copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
> +	    convert_in_user(&up->sequence, &kp->sequence) ||
> +	    convert_in_user(&up->reserved2, &kp->reserved2) ||
> +	    convert_in_user(&up->reserved, &kp->reserved) ||
> +	    get_user(length, &kp->length) ||
> +	    put_user(length, &up->length))
>  		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 = (__force struct v4l2_plane __user *)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) {


while (num_planes--) {

Or rather: s/num_planes/length/

Then you can remove num_planes.

> -			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:
> -		case V4L2_MEMORY_OVERLAY:
> -			if (put_user(kp->m.offset, &up->m.offset))
> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
>  				return -EFAULT;
>  			break;
>  		case V4L2_MEMORY_USERPTR:
> -			if (put_user(kp->m.userptr, &up->m.userptr))
> +			if (convert_in_user(&up->m.userptr, &kp->m.userptr))
> +				return -EFAULT;
> +			break;
> +		case V4L2_MEMORY_OVERLAY:
> +			if (convert_in_user(&up->m.offset, &kp->m.offset))

Combine with MMAP case?

>  				return -EFAULT;
>  			break;
>  		case V4L2_MEMORY_DMABUF:
> -			if (put_user(kp->m.fd, &up->m.fd))
> +			if (convert_in_user(&up->m.fd, &kp->m.fd))
>  				return -EFAULT;
>  			break;
>  		}
> @@ -545,29 +662,32 @@ struct v4l2_framebuffer32 {
>  	} 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(*up)) ||
>  	    get_user(tmp, &up->base) ||
> -	    get_user(kp->capability, &up->capability) ||
> -	    get_user(kp->flags, &up->flags) ||
> -	    copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
> +	    put_user((__force void *)compat_ptr(tmp), &kp->base) ||
> +	    convert_in_user(&kp->capability, &up->capability) ||
> +	    convert_in_user(&kp->flags, &up->flags) ||
> +	    copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt)))
>  		return -EFAULT;
> -	kp->base = (__force void *)compat_ptr(tmp);
>  	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(*up)) ||
> -	    put_user(tmp, &up->base) ||
> -	    put_user(kp->capability, &up->capability) ||
> -	    put_user(kp->flags, &up->flags) ||
> -	    copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
> +	    get_user(base, &kp->base) ||
> +	    put_user(ptr_to_compat(base), &up->base) ||
> +	    convert_in_user(&up->capability, &kp->capability) ||
> +	    convert_in_user(&up->flags, &kp->flags) ||
> +	    copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -586,16 +706,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 *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(*up)))
> +	if (copy_in_user(kp, up, sizeof(*up)))
>  		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(*up)))
> +	if (copy_in_user(up, kp, sizeof(*up)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -648,39 +770,58 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id)
>  	return !ret && (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
>  }
>  
> +static int bufsize_v4l2_ext_controls32(struct v4l2_ext_controls32 __user *up)
> +{
> +	u32 count;
> +
> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> +	    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 file *file,
> -				   struct v4l2_ext_controls *kp,
> -				   struct v4l2_ext_controls32 __user *up)
> +				   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;
> +	u32 count;
>  	unsigned int n;

u32 n

>  	compat_caddr_t p;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> -	    get_user(kp->which, &up->which) ||
> -	    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(&kp->which, &up->which) ||
> +	    get_user(count, &up->count) ||
> +	    put_user(count, &kp->count) ||
> +	    convert_in_user(&kp->error_idx, &up->error_idx) ||
> +	    copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
>  		return -EFAULT;
> -	if (kp->count == 0) {
> -		kp->controls = NULL;
> -		return 0;
> -	} else if (kp->count > V4L2_CID_MAX_CTRLS) {
> +	if (count == 0)
> +		return put_user(NULL, &kp->controls);
> +	else if (kp->count > V4L2_CID_MAX_CTRLS)

count

>  		return -EINVAL;
> -	}
>  	if (get_user(p, &up->controls))
>  		return -EFAULT;
>  	ucontrols = compat_ptr(p);
> -	if (!access_ok(VERIFY_READ, ucontrols, kp->count * sizeof(*ucontrols)))
> +	if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols)))
> +		return -EFAULT;
> +	if (aux_space < count * sizeof(*kcontrols))
>  		return -EFAULT;
> -	kcontrols = compat_alloc_user_space(kp->count * sizeof(*kcontrols));
> -	kp->controls = (__force struct v4l2_ext_control *)kcontrols;
> -	for (n = 0; n < kp->count; n++) {
> +	kcontrols = aux_buf;
> +	if (put_user((__force struct v4l2_ext_control *)kcontrols,
> +		     &kp->controls))
> +		return -EFAULT;
> +
> +	for (n = 0; n < count; n++) {
>  		u32 id;
>  
>  		if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
>  			return -EFAULT;
> +
>  		if (get_user(id, &kcontrols->id))
>  			return -EFAULT;

Newline here, too? Not really a problem with this patch though.

>  		if (ctrl_is_pointer(file, id)) {
> @@ -699,36 +840,47 @@ static int get_v4l2_ext_controls32(struct file *file,
>  }
>  
>  static int put_v4l2_ext_controls32(struct file *file,
> -				   struct v4l2_ext_controls *kp,
> +				   struct v4l2_ext_controls __user *kp,
>  				   struct v4l2_ext_controls32 __user *up)
>  {
>  	struct v4l2_ext_control32 __user *ucontrols;
> -	struct v4l2_ext_control __user *kcontrols =
> -		(__force struct v4l2_ext_control __user *)kp->controls;
> -	int n = kp->count;
> +	struct v4l2_ext_control __user *kcontrols;
> +	u32 count;
> +	unsigned int n;

u32 n

>  	compat_caddr_t p;
>  
>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
> -	    put_user(kp->which, &up->which) ||
> -	    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) ||

How about doing this as last? "controls" is the last member of the struct.

> +	    convert_in_user(&up->which, &kp->which) ||
> +	    get_user(count, &kp->count) ||
> +	    put_user(count, &up->count) ||
> +	    convert_in_user(&up->error_idx, &kp->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(*ucontrols)))
> +	if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols)))
>  		return -EFAULT;
>  
> -	while (--n >= 0) {
> -		unsigned size = sizeof(*ucontrols);
> +	for (n = 0; n < count; n++) {
> +		unsigned int size = sizeof(*ucontrols);
>  		u32 id;
>  
> +		if (copy_in_user(&ucontrols->id, &kcontrols->id,
> +				 sizeof(ucontrols->id)) ||

get_user(id, &kcontrols->id) ||
put_user(id, &ucontrols->id) ||

> +		    copy_in_user(&ucontrols->size, &kcontrols->size,
> +				 sizeof(ucontrols->size)) ||

convert_in_user() ?

> +		    copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
> +				 sizeof(ucontrols->reserved2)))
> +			return -EFAULT;
> +
>  		if (get_user(id, &kcontrols->id))
>  			return -EFAULT;

...and the above if () can be removed.

> +
>  		/* Do not modify the pointer when copying a pointer control.
>  		   The contents of the pointer was changed, not the pointer
>  		   itself. */
> @@ -736,6 +888,7 @@ static int put_v4l2_ext_controls32(struct file *file,
>  			size -= sizeof(ucontrols->value64);

This chunk is yearning for newlines. Not a fault of this patch though.

>  		if (copy_in_user(ucontrols, kcontrols, size))
>  			return -EFAULT;
> +
>  		ucontrols++;
>  		kcontrols++;
>  	}
> @@ -755,17 +908,18 @@ 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(*up)) ||
> -	    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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
> -	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
> -	    put_user(kp->id, &up->id) ||
> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
> +	    convert_in_user(&up->type, &kp->type) ||
> +	    copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
> +	    convert_in_user(&up->pending, &kp->pending) ||
> +	    convert_in_user(&up->sequence, &kp->sequence) ||
> +	    convert_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
> +	    convert_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) ||
> +	    convert_in_user(&up->id, &kp->id) ||
> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
>  		return -EFAULT;
>  	return 0;
>  }
> @@ -778,31 +932,34 @@ struct v4l2_edid32 {
>  	compat_caddr_t edid;
>  };
>  
> -static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
> +static int get_v4l2_edid32(struct v4l2_edid __user *kp,
> +			   struct v4l2_edid32 __user *up)
>  {
> -	u32 tmp;
> +	compat_uptr_t tmp;
>  
>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> -	    get_user(kp->pad, &up->pad) ||
> -	    get_user(kp->start_block, &up->start_block) ||
> -	    get_user(kp->blocks, &up->blocks) ||
> +	    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(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 = (__force u8 *)compat_ptr(tmp);
>  	return 0;
>  }
>  
> -static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
> +static int put_v4l2_edid32(struct v4l2_edid __user *kp,
> +			   struct v4l2_edid32 __user *up)
>  {
> -	u32 tmp = (u32)((unsigned long)kp->edid);
> +	void *edid;
>  
>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
> -	    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(up->reserved, kp->reserved, sizeof(up->reserved)))
> +	    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(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;
>  }
> @@ -835,22 +992,30 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>  #define VIDIOC_G_OUTPUT32	_IOR ('V', 46, s32)
>  #define VIDIOC_S_OUTPUT32	_IOWR('V', 47, s32)
>  
> +static void __user *alloc_userspace(unsigned int size, int aux_space, long *err)

This one is weird. Rather than following the pattern and returning an error
and assigning the result to a pointer passed to it, it does exactly the
opposite. I'd change that. I guess this is a matter of opinion to some
degree at least, though.

> +{
> +	void __user *up_native;
> +
> +	if (aux_space < 0) {
> +		*err = aux_space;
> +		return NULL;
> +	}
> +	up_native = compat_alloc_user_space(size + aux_space);
> +	if (!up_native)
> +		*err = -ENOMEM;
> +	else if (clear_user(up_native, size))
> +		*err = -EFAULT;
> +	else
> +		return up_native;
> +	return NULL;
> +}
> +
>  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_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;

size_t?

>  	int compatible_arg = 1;
>  	long err = 0;
>  
> @@ -889,30 +1054,47 @@ 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_userspace(sizeof(unsigned int __user),
> +					    0, &err);
> +		if (!err && convert_in_user((unsigned int __user *)up_native,
> +					    (compat_uint_t __user *)up))
> +			return -EFAULT;
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_G_INPUT:
>  	case VIDIOC_G_OUTPUT:
> +		up_native = alloc_userspace(sizeof(unsigned int __user),
> +					    0, &err);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_G_EDID:
>  	case VIDIOC_S_EDID:
> -		err = get_v4l2_edid32(&karg.v2edid, up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_edid), 0, &err);
> +		err = err ? : get_v4l2_edid32(up_native, up);

I'd write this as:

		if (!err)
			err = get_v4l2_edid32(native_up, up);

Same below.

>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_G_FMT:
>  	case VIDIOC_S_FMT:
>  	case VIDIOC_TRY_FMT:
> -		err = get_v4l2_format32(&karg.v2f, up);
> +		aux_space = bufsize_v4l2_format32(up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_format),
> +					    aux_space, &err);
> +		aux_buf = up_native + sizeof(struct v4l2_format);
> +		err = err ? : get_v4l2_format32(up_native, up,
> +						aux_buf, aux_space);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_CREATE_BUFS:
> -		err = get_v4l2_create32(&karg.v2crt, up);
> +		aux_space = bufsize_v4l2_create32(up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_create_buffers),
> +					    aux_space, &err);
> +		aux_buf = up_native + sizeof(struct v4l2_create_buffers);
> +		err = err ? : get_v4l2_create32(up_native, up,
> +						aux_buf, aux_space);
>  		compatible_arg = 0;
>  		break;
>  
> @@ -920,36 +1102,54 @@ 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);
> +		aux_space = bufsize_v4l2_buffer32(up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_buffer),
> +					    aux_space, &err);
> +		aux_buf = up_native + sizeof(struct v4l2_buffer);
> +		err = err ? : get_v4l2_buffer32(up_native, up,
> +						aux_buf, aux_space);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_S_FBUF:
> -		err = get_v4l2_framebuffer32(&karg.v2fb, up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
> +					    0, &err);
> +		err = err ? : get_v4l2_framebuffer32(up_native, up);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_G_FBUF:
> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
> +					    0, &err);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_ENUMSTD:
> -		err = get_v4l2_standard32(&karg.v2s, up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_standard),
> +					    0, &err);
> +		err = err ? : get_v4l2_standard32(up_native, up);
>  		compatible_arg = 0;
>  		break;
>  
>  	case VIDIOC_ENUMINPUT:
> -		err = get_v4l2_input32(&karg.v2i, up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_input), 0, &err);
> +		err = 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(file, &karg.v2ecs, up);
> +		aux_space = bufsize_v4l2_ext_controls32(up);
> +		up_native = alloc_userspace(sizeof(struct v4l2_ext_controls),
> +					    aux_space, &err);
> +		aux_buf = up_native + sizeof(struct v4l2_ext_controls);
> +		err = err ? : get_v4l2_ext_controls32(file, up_native, up,
> +						      aux_buf, aux_space);
>  		compatible_arg = 0;
>  		break;
>  	case VIDIOC_DQEVENT:
> +		up_native = alloc_userspace(sizeof(struct v4l2_event), 0, &err);
>  		compatible_arg = 0;
>  		break;
>  	}
> @@ -958,13 +1158,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);
>  
>  	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
>  		return err;
> @@ -976,11 +1171,11 @@ 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(file, &karg.v2ecs, up))
> +		if (put_v4l2_ext_controls32(file, up_native, up))
>  			err = -EFAULT;
>  		break;
>  	case VIDIOC_S_EDID:
> -		if (put_v4l2_edid32(&karg.v2edid, up))
> +		if (put_v4l2_edid32(up_native, up))
>  			err = -EFAULT;
>  		break;
>  	}
> @@ -992,44 +1187,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((compat_uint_t __user *)up,
> +				      ((unsigned int __user *)up_native));

Do note that convert_in_user() returns Boolean. This will effectively yield
1 in case of an error here. How about changing convert_in_user() to return
whatever get_user / put_user do, to avoid errors in the future?

>  		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_G_EDID:
> -		err = put_v4l2_edid32(&karg.v2edid, up);
> +		err = put_v4l2_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_PREPARE_BUF:
>  	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;

-- 
Kind regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32
  2018-01-29 17:06   ` Sakari Ailus
@ 2018-01-29 17:41     ` Hans Verkuil
  2018-01-29 21:14       ` Sakari Ailus
  2018-01-30  9:37     ` Hans Verkuil
  1 sibling, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-29 17:41 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On 01/29/2018 06:06 PM, Sakari Ailus wrote:
> Hi Hans,
> 
> Thanks for your efforts on this patch and the patchset. Please see my comments below.
> 
> On Fri, Jan 26, 2018 at 01:43:27PM +0100, Hans Verkuil wrote:
>> From: Daniel Mentz <danielmentz@google.com>
>>
>> The 32-bit compat v4l2 ioctl is implemented based on its 64-bit
> 
> s/v4l2 ioctl/V4L2 IOCTL handling/
> 
> ?
> 
>> equivalent. It converts 32-bit data structures into its 64-bit
>> equivalents and needs to provide the data to the 64-bit ioctl in user
>> space memory which is commonly allocated using
>> compat_alloc_user_space(). However, due to how that function is
>> implemented, it can only be called a single time for every syscall
>> invocation.  Supposedly to avoid this limitation, the existing code uses
>> a mix of memory from the kernel stack and memory allocated through
>> compat_alloc_user_space(). Under normal circumstances, this would not
>> work, because the 64-bit ioctl expects all pointers to point to user
>> space memory. As a workaround, set_fs(KERNEL_DS) is called to
>> temporarily disable this extra safety check and allow kernel pointers.
>> However, this might introduce a security vulnerability: The
>> result of the 32-bit to 64-bit conversion is writeable by user space
>> because the output buffer has been allocated via
>> compat_alloc_user_space(). A malicious user space process could then
>> manipulate pointers inside this output buffer, and due to the previous
>> set_fs(KERNEL_DS) call, functions like get_user() or put_user() no longer
>> prevent kernel memory access.
>>
>> The new approach is to pre-calculate the total amount of user space
>> memory that is needed, allocate it using compat_alloc_user_space() and
>> then divide up the allocated memory to accommodate all data structures
>> that need to be converted.
>>
>> An alternative approach would have been to retain the union type karg
>> that they allocated on the kernel stack in do_video_ioctl(), copy all
>> data from user space into karg and then back to user space. However,
>> we decided against this approach because it does not align with other
>> compat syscall implementations. Instead, we tried to replicate the
>> get_user/put_user pairs as found in other places in the kernel:
>>
>> if (get_user(clipcount, &up->clipcount) ||
>>     put_user(clipcount, &kp->clipcount)) return -EFAULT;
>>
>> Notes from hans.verkuil@cisco.com:
>>
>> This patch was taken from
>> https://github.com/LineageOS/android_kernel_samsung_apq8084/commit/97b733953c06e4f0398ade18850f0817778255f7
>>
>> Clearly nobody could be bothered to upstream this patch or at minimum
>> tell us :-( We only heard about this a week ago.
> 
> This is very sad indeed. I would really hope that we'll never run into
> something this again!
> 
>>
>> This patch was rebased and cleaned up. Compared to the original I
>> also swapped the order of the convert_in_user arguments so that they
>> matched copy_in_user. It was hard to review otherwise. I also replaced
>> the ALLOC_USER_SPACE/ALLOC_AND_GET by a normal function.
> 
> The result looks more complicated than it should be.
> 
> I wonder if the result could be made cleaner by separating the argument
> checking and copying from / to user space in video_usercopy. That
> separation almost completely exists already. Rather than mangling the
> compat argument struct to look like a user-provided 64-bit argument struct,
> copying the arguments from and to the user space would be separately done
> to 32-bit compat IOCTL arguments.
> 
> I suppose that wouldn't be a trivial change either, so for now I'd maintain
> the approach and later consider how to make this more maintainable in the
> future. Still, that approach is quite different and it'd be easier to
> switch from status quo rather than this last patch, suggesting to start
> with a revert, should we embark into this direction.
> 
> What do you think?

To be honest, I don't really know. Things will get even more complicated
once we have to deal with the year 2038 problem since that impacts this
code as well. I'm waiting for the fence code and request API code to
land before continuing working on that. Perhaps that might be a good
time to take a step back and see if we can do something smarter.

> 
>>
>> Signed-off-by: Daniel Mentz <danielmentz@google.com>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 700 ++++++++++++++++----------
>>  1 file changed, 448 insertions(+), 252 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> index 2aa9b43daf60..27a5a0961cbd 100644
>> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> @@ -22,6 +22,14 @@
>>  #include <media/v4l2-ctrls.h>
>>  #include <media/v4l2-ioctl.h>
>>  
>> +/* Use the same argument order as copy_in_user */
>> +#define convert_in_user(to, from)			\
> 
> This doesn't really convert anything. How about calling it assign_in_user,
> for example?

Sounds reasonable.

> 
>> +({							\
>> +	typeof(*from) val;				\
> 
> "val" is very short, easily leading to hard-to-find namespace collisions.
> At least the versions of GCC I've used happily get this wrong without a
> warning.
> 
> How about __convert_in_user_tmp instead?

Hmm. What about __assign_tmp?

> 
>> +							\
>> +	get_user(val, from) || put_user(val, to);	\
>> +})
>> +
>>  static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>  {
>>  	long ret = -ENOIOCTLCMD;
>> @@ -48,37 +56,41 @@ struct v4l2_window32 {
>>  	__u8                    global_alpha;
>>  };
>>  
>> -static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
>> +static int get_v4l2_window32(struct v4l2_window __user *kp,
>> +			     struct v4l2_window32 __user *up,
>> +			     void __user *aux_buf, int aux_space)
>>  {
>>  	struct v4l2_clip32 __user *uclips;
>>  	struct v4l2_clip __user *kclips;
>>  	compat_caddr_t p;
>> -	u32 n;
>> +	u32 clipcount;
>>  
>>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> -	    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) ||
>> -	    get_user(kp->global_alpha, &up->global_alpha))
>> +	    copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
>> +	    convert_in_user(&kp->field, &up->field) ||
>> +	    convert_in_user(&kp->chromakey, &up->chromakey) ||
>> +	    convert_in_user(&kp->global_alpha, &up->global_alpha) ||
>> +	    get_user(clipcount, &up->clipcount) ||
>> +	    put_user(clipcount, &kp->clipcount))
>>  		return -EFAULT;
>> -	if (kp->clipcount > 2048)
>> +	if (clipcount > 2048)
>>  		return -EINVAL;
>> -	if (!kp->clipcount) {
>> -		kp->clips = NULL;
>> -		return 0;
>> -	}
>> +	if (!clipcount)
>> +		return put_user(NULL, &kp->clips) ? -EFAULT : 0;
> 
> Isn't this what put_user would return anyway?

True, I'll simplify this.

>>  
>> -	n = kp->clipcount;
>>  	if (get_user(p, &up->clips))
>>  		return -EFAULT;
>>  	uclips = compat_ptr(p);
>> -	kclips = compat_alloc_user_space(n * sizeof(*kclips));
>> -	kp->clips = kclips;
>> -	while (n--) {
>> +	if (aux_space < clipcount * sizeof(*kclips))
>> +		return -EFAULT;
>> +	kclips = aux_buf;
>> +	if (put_user(kclips, &kp->clips))
>> +		return -EFAULT;
>> +
>> +	while (clipcount--) {
>>  		if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
>>  			return -EFAULT;
>> -		if (put_user(n ? kclips + 1 : NULL, &kclips->next))
>> +		if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next))
>>  			return -EFAULT;
>>  		uclips++;
>>  		kclips++;
>> @@ -86,26 +98,28 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
>>  	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)
>>  {
>>  	struct v4l2_clip __user *kclips = kp->clips;
>>  	struct v4l2_clip32 __user *uclips;
>> -	int n = kp->clipcount;
>>  	compat_caddr_t p;
>> -
>> -	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) ||
>> -	    put_user(kp->global_alpha, &up->global_alpha))
>> +	u32 clipcount;
>> +
>> +	if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
>> +	    convert_in_user(&up->field, &kp->field) ||
>> +	    convert_in_user(&up->chromakey, &kp->chromakey) ||
>> +	    convert_in_user(&up->global_alpha, &kp->global_alpha) ||
>> +	    get_user(clipcount, &kp->clipcount) ||
>> +	    put_user(clipcount, &up->clipcount))
>>  		return -EFAULT;
>> -	if (!kp->clipcount)
>> +	if (!clipcount)
>>  		return 0;
>>  
>>  	if (get_user(p, &up->clips))
>>  		return -EFAULT;
>>  	uclips = compat_ptr(p);
>> -	while (n--) {
>> +	while (clipcount--) {
>>  		if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
>>  			return -EFAULT;
>>  		uclips++;
>> @@ -145,107 +159,161 @@ 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)
> 
> This is effectively returning the size of the non-compat struct, or just
> the part of which is variable. How about calling it __bufsize_v4l2_format
> instead? The same for other such functions.

Ack.

> I'd also separate the size calculated by the function and the return value.

That's cleaner, yes.

> 
>>  {
>> -	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: {
>> +		u32 clipcount;
>> +
>> +		if (get_user(clipcount, &up->fmt.win.clipcount))
>> +			return -EFAULT;
>> +		if (clipcount > 2048)
>> +			return -EINVAL;
>> +		return clipcount * sizeof(struct v4l2_clip);
>> +	}
>> +	default:
>> +		return 0;
>> +	}
>> +}
>> +
>> +static int bufsize_v4l2_format32(struct v4l2_format32 __user *up)
>> +{
>> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)))
>> +		return -EFAULT;
> 
> Newline?
> 
>> +	return __bufsize_v4l2_format32(up);
>> +}
>> +
>> +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 copy_from_user(&kp->fmt.pix, &up->fmt.pix,
>> -				      sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.pix, &up->fmt.pix,
>> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		return copy_from_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
>> -				      sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
>> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>>  	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 copy_from_user(&kp->fmt.vbi, &up->fmt.vbi,
>> -				      sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi,
>> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		return copy_from_user(&kp->fmt.sliced, &up->fmt.sliced,
>> -				      sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced,
>> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		return copy_from_user(&kp->fmt.sdr, &up->fmt.sdr,
>> -				      sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr,
>> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
>> -				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.meta, &up->fmt.meta,
>> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>>  	default:
>>  		return -EINVAL;
>>  	}
>>  }
>>  
>> -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __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(*up)))
>> +		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(*up)))
>>  		return -EFAULT;
>> -	return __get_v4l2_format32(kp, up);
>> +	return __bufsize_v4l2_format32(&up->format);
>>  }
>>  
>> -static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
>> +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(*up)) ||
>> -	    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)
>>  {
>> -	if (put_user(kp->type, &up->type))
>> +	u32 type;
>> +
>> +	if (get_user(type, &kp->type))
>>  		return -EFAULT;
>>  
>> -	switch (kp->type) {
>> +	switch (type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		return copy_to_user(&up->fmt.pix, &kp->fmt.pix,
>> -				    sizeof(kp->fmt.pix)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.pix, &kp->fmt.pix,
>> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		return copy_to_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
>> -				    sizeof(kp->fmt.pix_mp)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
>> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>  		return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
>>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> -		return copy_to_user(&up->fmt.vbi, &kp->fmt.vbi,
>> -				    sizeof(kp->fmt.vbi)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi,
>> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		return copy_to_user(&up->fmt.sliced, &kp->fmt.sliced,
>> -				    sizeof(kp->fmt.sliced)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced,
>> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		return copy_to_user(&up->fmt.sdr, &kp->fmt.sdr,
>> -				    sizeof(kp->fmt.sdr)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr,
>> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
>> -				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.meta, &kp->fmt.meta,
>> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>>  	default:
>>  		return -EINVAL;
>>  	}
>>  }
>>  
>> -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(*up)))
> 
> For some pointers we do perform such checks, for others we don't. While
> this patch doesn't change this, it'd be good to align this by either
> introducing it everywhere or removing it everywhere.
> 
> I guess that can be done separately.
> 
>>  		return -EFAULT;
>>  	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(*up)) ||
>> -	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
>> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>> +	    copy_in_user(up, kp,
>> +			 offsetof(struct v4l2_create_buffers32, format)) ||
>> +	    copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>>  		return -EFAULT;
>>  	return __put_v4l2_format32(&kp->format, &up->format);
>>  }
>> @@ -259,25 +327,27 @@ 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(*up)) ||
>> -	    get_user(kp->index, &up->index))
>> +	    convert_in_user(&kp->index, &up->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(*up)) ||
>> -	    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, sizeof(kp->reserved)))
>> +	    convert_in_user(&up->index, &kp->index) ||
>> +	    copy_in_user(&up->id, &kp->id, sizeof(up->id)) ||
> 
> Why not convert_in_user()?

Will change.

> 
>> +	    copy_in_user(up->name, kp->name, 24) ||

Urg. I missed this, this has a fixed number as the last argument instead of
sizeof(kp->name).

>> +	    copy_in_user(&up->frameperiod, &kp->frameperiod,
>> +			 sizeof(up->frameperiod)) ||
>> +	    convert_in_user(&up->framelines, &kp->framelines) ||
>> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
>>  		return -EFAULT;
>>  	return 0;
>>  }
>> @@ -317,10 +387,10 @@ struct v4l2_buffer32 {
>>  	__u32			reserved;
>>  };
>>  
>> -static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
>> +static int get_v4l2_plane32(struct v4l2_plane __user *up,
>> +			    struct v4l2_plane32 __user *up32,
>>  			    enum v4l2_memory memory)
>>  {
>> -	void __user *up_pln;
>>  	compat_long_t p;
> 
> Shouldn't this be compat_ulong_t?

That's better, yes.

> 
>>  
>>  	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
>> @@ -336,10 +406,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>>  			return -EFAULT;
>>  		break;
>>  	case 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;
>>  		break;
>>  	case V4L2_MEMORY_DMABUF:
>> @@ -351,7 +419,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>>  	return 0;
>>  }
>>  
>> -static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
>> +static int put_v4l2_plane32(struct v4l2_plane __user *up,
>> +			    struct v4l2_plane32 __user *up32,
>>  			    enum v4l2_memory memory)
>>  {
>>  	unsigned long p;
>> @@ -375,8 +444,7 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>>  			return -EFAULT;
>>  		break;
>>  	case V4L2_MEMORY_DMABUF:
>> -		if (copy_in_user(&up32->m.fd, &up->m.fd,
>> -				 sizeof(up->m.fd)))
>> +		if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd)))
>>  			return -EFAULT;
>>  		break;
>>  	}
>> @@ -384,37 +452,68 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>>  	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(*up)) ||
>> +	    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;
>> +	int num_planes;
> 
> u32?
> 
>>  	int ret;
>>  
>>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> -	    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(&kp->index, &up->index) ||
>> +	    get_user(type, &up->type) ||
>> +	    put_user(type, &kp->type) ||
>> +	    convert_in_user(&kp->flags, &up->flags) ||
>> +	    get_user(memory, &up->memory) ||
>> +	    put_user(memory, &kp->memory) ||
>> +	    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(&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))
>>  			return -EFAULT;
>>  
>> -	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
>> -		unsigned int num_planes;
>> -
>> -		if (kp->length == 0) {
>> -			kp->m.planes = NULL;
>> +	if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
>> +		num_planes = length;
> 
> Do you need a local variable for just that?
> 
>> +		if (num_planes == 0) {
>>  			/* num_planes == 0 is legal, e.g. when userspace doesn't
>>  			 * need planes array on DQBUF*/
>> -			return 0;
>> -		} else if (kp->length > VIDEO_MAX_PLANES) {
>> +			return put_user(NULL, &kp->m.planes);
>> +		} else if (num_planes > VIDEO_MAX_PLANES) {
>>  			return -EINVAL;
>>  		}
>>  
>> @@ -423,40 +522,47 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>>  
>>  		uplane32 = compat_ptr(p);
>>  		if (!access_ok(VERIFY_READ, uplane32,
>> -			       kp->length * sizeof(*uplane32)))
>> +				num_planes * sizeof(*uplane32)))
> 
> Alignment.
> 
>>  			return -EFAULT;
>>  
>>  		/* We don't really care if userspace decides to kill itself
>>  		 * by passing a very big num_planes value */
>> -		uplane = compat_alloc_user_space(kp->length * sizeof(*uplane));
>> -		kp->m.planes = (__force struct v4l2_plane *)uplane;
>> +		if (aux_space < num_planes * sizeof(*uplane))
>> +			return -EFAULT;
>> +
>> +		uplane = aux_buf;
>> +		if (put_user((__force struct v4l2_plane *)uplane,
>> +					&kp->m.planes))
>> +			return -EFAULT;
>>  
>> -		for (num_planes = 0; num_planes < kp->length; num_planes++) {
>> -			ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
>> +		while (--num_planes >= 0) {
> 
> while (num_planes--) {
> 
> ?

Yes. I missed this one :-)

> 
>> +			ret = get_v4l2_plane32(uplane, uplane32, memory);
>>  			if (ret)
>>  				return ret;
>> -			++uplane;
>> -			++uplane32;
>> +			uplane++;
>> +			uplane32++;
>>  		}
>>  	} else {
>> -		switch (kp->memory) {
>> +		switch (memory) {
>>  		case V4L2_MEMORY_MMAP:
>> -		case V4L2_MEMORY_OVERLAY:
>> -			if (get_user(kp->m.offset, &up->m.offset))
>> +			if (convert_in_user(&kp->m.offset, &up->m.offset))
>>  				return -EFAULT;
>>  			break;
>> -		case V4L2_MEMORY_USERPTR:
>> -			{
>> -				compat_long_t tmp;
>> -
>> -				if (get_user(tmp, &up->m.userptr))
>> -					return -EFAULT;
>> +		case V4L2_MEMORY_USERPTR: {
>> +			compat_long_t tmp;
> 
> compat_ulong_t, I'd call it "userptr". The latter is entirely up to you.
> 
>>  
>> -				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 (convert_in_user(&kp->m.offset, &up->m.offset))
>> +				return -EFAULT;
> 
> Is there a reason to split this off from MMAP handling? The code is the
> same.

Huh, I missed this as well. I merged MMAP and OVERLAY elsewhere.

> 
>>  			break;
>>  		case V4L2_MEMORY_DMABUF:
>> -			if (get_user(kp->m.fd, &up->m.fd))
>> +			if (convert_in_user(&kp->m.fd, &up->m.fd))
>>  				return -EFAULT;
>>  			break;
>>  		}
>> @@ -465,8 +571,12 @@ 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;
>> @@ -474,53 +584,60 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>>  	int ret;
>>  
>>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>> -	    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(&up->index, &kp->index) ||
>> +	    get_user(type, &kp->type) ||
>> +	    put_user(type, &up->type) ||
>> +	    convert_in_user(&up->flags, &kp->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(kp->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(&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) ||
>> +	    copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
>> +	    convert_in_user(&up->sequence, &kp->sequence) ||
>> +	    convert_in_user(&up->reserved2, &kp->reserved2) ||
>> +	    convert_in_user(&up->reserved, &kp->reserved) ||
>> +	    get_user(length, &kp->length) ||
>> +	    put_user(length, &up->length))
>>  		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 = (__force struct v4l2_plane __user *)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) {
> 
> 
> while (num_planes--) {
> 
> Or rather: s/num_planes/length/
> 
> Then you can remove num_planes.

I will probably keep num_planes. It's more descriptive than 'length'.

> 
>> -			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:
>> -		case V4L2_MEMORY_OVERLAY:
>> -			if (put_user(kp->m.offset, &up->m.offset))
>> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
>>  				return -EFAULT;
>>  			break;
>>  		case V4L2_MEMORY_USERPTR:
>> -			if (put_user(kp->m.userptr, &up->m.userptr))
>> +			if (convert_in_user(&up->m.userptr, &kp->m.userptr))
>> +				return -EFAULT;
>> +			break;
>> +		case V4L2_MEMORY_OVERLAY:
>> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
> 
> Combine with MMAP case?

Hmm. I did combine this, but I probably lost that change after some later
patch modifications. Thanks for noticing.

> 
>>  				return -EFAULT;
>>  			break;
>>  		case V4L2_MEMORY_DMABUF:
>> -			if (put_user(kp->m.fd, &up->m.fd))
>> +			if (convert_in_user(&up->m.fd, &kp->m.fd))
>>  				return -EFAULT;
>>  			break;
>>  		}
>> @@ -545,29 +662,32 @@ struct v4l2_framebuffer32 {
>>  	} 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(*up)) ||
>>  	    get_user(tmp, &up->base) ||
>> -	    get_user(kp->capability, &up->capability) ||
>> -	    get_user(kp->flags, &up->flags) ||
>> -	    copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
>> +	    put_user((__force void *)compat_ptr(tmp), &kp->base) ||
>> +	    convert_in_user(&kp->capability, &up->capability) ||
>> +	    convert_in_user(&kp->flags, &up->flags) ||
>> +	    copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt)))
>>  		return -EFAULT;
>> -	kp->base = (__force void *)compat_ptr(tmp);
>>  	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(*up)) ||
>> -	    put_user(tmp, &up->base) ||
>> -	    put_user(kp->capability, &up->capability) ||
>> -	    put_user(kp->flags, &up->flags) ||
>> -	    copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
>> +	    get_user(base, &kp->base) ||
>> +	    put_user(ptr_to_compat(base), &up->base) ||
>> +	    convert_in_user(&up->capability, &kp->capability) ||
>> +	    convert_in_user(&up->flags, &kp->flags) ||
>> +	    copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt)))
>>  		return -EFAULT;
>>  	return 0;
>>  }
>> @@ -586,16 +706,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 *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(*up)))
>> +	if (copy_in_user(kp, up, sizeof(*up)))
>>  		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(*up)))
>> +	if (copy_in_user(up, kp, sizeof(*up)))
>>  		return -EFAULT;
>>  	return 0;
>>  }
>> @@ -648,39 +770,58 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id)
>>  	return !ret && (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
>>  }
>>  
>> +static int bufsize_v4l2_ext_controls32(struct v4l2_ext_controls32 __user *up)
>> +{
>> +	u32 count;
>> +
>> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> +	    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 file *file,
>> -				   struct v4l2_ext_controls *kp,
>> -				   struct v4l2_ext_controls32 __user *up)
>> +				   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;
>> +	u32 count;
>>  	unsigned int n;
> 
> u32 n
> 
>>  	compat_caddr_t p;
>>  
>>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> -	    get_user(kp->which, &up->which) ||
>> -	    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(&kp->which, &up->which) ||
>> +	    get_user(count, &up->count) ||
>> +	    put_user(count, &kp->count) ||
>> +	    convert_in_user(&kp->error_idx, &up->error_idx) ||
>> +	    copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
>>  		return -EFAULT;
>> -	if (kp->count == 0) {
>> -		kp->controls = NULL;
>> -		return 0;
>> -	} else if (kp->count > V4L2_CID_MAX_CTRLS) {
>> +	if (count == 0)
>> +		return put_user(NULL, &kp->controls);
>> +	else if (kp->count > V4L2_CID_MAX_CTRLS)
> 
> count
> 
>>  		return -EINVAL;
>> -	}
>>  	if (get_user(p, &up->controls))
>>  		return -EFAULT;
>>  	ucontrols = compat_ptr(p);
>> -	if (!access_ok(VERIFY_READ, ucontrols, kp->count * sizeof(*ucontrols)))
>> +	if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols)))
>> +		return -EFAULT;
>> +	if (aux_space < count * sizeof(*kcontrols))
>>  		return -EFAULT;
>> -	kcontrols = compat_alloc_user_space(kp->count * sizeof(*kcontrols));
>> -	kp->controls = (__force struct v4l2_ext_control *)kcontrols;
>> -	for (n = 0; n < kp->count; n++) {
>> +	kcontrols = aux_buf;
>> +	if (put_user((__force struct v4l2_ext_control *)kcontrols,
>> +		     &kp->controls))
>> +		return -EFAULT;
>> +
>> +	for (n = 0; n < count; n++) {
>>  		u32 id;
>>  
>>  		if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
>>  			return -EFAULT;
>> +
>>  		if (get_user(id, &kcontrols->id))
>>  			return -EFAULT;
> 
> Newline here, too? Not really a problem with this patch though.
> 
>>  		if (ctrl_is_pointer(file, id)) {
>> @@ -699,36 +840,47 @@ static int get_v4l2_ext_controls32(struct file *file,
>>  }
>>  
>>  static int put_v4l2_ext_controls32(struct file *file,
>> -				   struct v4l2_ext_controls *kp,
>> +				   struct v4l2_ext_controls __user *kp,
>>  				   struct v4l2_ext_controls32 __user *up)
>>  {
>>  	struct v4l2_ext_control32 __user *ucontrols;
>> -	struct v4l2_ext_control __user *kcontrols =
>> -		(__force struct v4l2_ext_control __user *)kp->controls;
>> -	int n = kp->count;
>> +	struct v4l2_ext_control __user *kcontrols;
>> +	u32 count;
>> +	unsigned int n;
> 
> u32 n
> 
>>  	compat_caddr_t p;
>>  
>>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>> -	    put_user(kp->which, &up->which) ||
>> -	    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) ||
> 
> How about doing this as last? "controls" is the last member of the struct.
> 
>> +	    convert_in_user(&up->which, &kp->which) ||
>> +	    get_user(count, &kp->count) ||
>> +	    put_user(count, &up->count) ||
>> +	    convert_in_user(&up->error_idx, &kp->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(*ucontrols)))
>> +	if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols)))
>>  		return -EFAULT;
>>  
>> -	while (--n >= 0) {
>> -		unsigned size = sizeof(*ucontrols);
>> +	for (n = 0; n < count; n++) {
>> +		unsigned int size = sizeof(*ucontrols);
>>  		u32 id;
>>  
>> +		if (copy_in_user(&ucontrols->id, &kcontrols->id,
>> +				 sizeof(ucontrols->id)) ||
> 
> get_user(id, &kcontrols->id) ||
> put_user(id, &ucontrols->id) ||
> 
>> +		    copy_in_user(&ucontrols->size, &kcontrols->size,
>> +				 sizeof(ucontrols->size)) ||
> 
> convert_in_user() ?
> 
>> +		    copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
>> +				 sizeof(ucontrols->reserved2)))
>> +			return -EFAULT;
>> +
>>  		if (get_user(id, &kcontrols->id))
>>  			return -EFAULT;
> 
> ...and the above if () can be removed.
> 
>> +
>>  		/* Do not modify the pointer when copying a pointer control.
>>  		   The contents of the pointer was changed, not the pointer
>>  		   itself. */
>> @@ -736,6 +888,7 @@ static int put_v4l2_ext_controls32(struct file *file,
>>  			size -= sizeof(ucontrols->value64);
> 
> This chunk is yearning for newlines. Not a fault of this patch though.
> 
>>  		if (copy_in_user(ucontrols, kcontrols, size))
>>  			return -EFAULT;
>> +
>>  		ucontrols++;
>>  		kcontrols++;
>>  	}
>> @@ -755,17 +908,18 @@ 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(*up)) ||
>> -	    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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
>> -	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
>> -	    put_user(kp->id, &up->id) ||
>> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>> +	    convert_in_user(&up->type, &kp->type) ||
>> +	    copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
>> +	    convert_in_user(&up->pending, &kp->pending) ||
>> +	    convert_in_user(&up->sequence, &kp->sequence) ||
>> +	    convert_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
>> +	    convert_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) ||
>> +	    convert_in_user(&up->id, &kp->id) ||
>> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
>>  		return -EFAULT;
>>  	return 0;
>>  }
>> @@ -778,31 +932,34 @@ struct v4l2_edid32 {
>>  	compat_caddr_t edid;
>>  };
>>  
>> -static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>> +static int get_v4l2_edid32(struct v4l2_edid __user *kp,
>> +			   struct v4l2_edid32 __user *up)
>>  {
>> -	u32 tmp;
>> +	compat_uptr_t tmp;
>>  
>>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> -	    get_user(kp->pad, &up->pad) ||
>> -	    get_user(kp->start_block, &up->start_block) ||
>> -	    get_user(kp->blocks, &up->blocks) ||
>> +	    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(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 = (__force u8 *)compat_ptr(tmp);
>>  	return 0;
>>  }
>>  
>> -static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>> +static int put_v4l2_edid32(struct v4l2_edid __user *kp,
>> +			   struct v4l2_edid32 __user *up)
>>  {
>> -	u32 tmp = (u32)((unsigned long)kp->edid);
>> +	void *edid;
>>  
>>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>> -	    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(up->reserved, kp->reserved, sizeof(up->reserved)))
>> +	    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(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;
>>  }
>> @@ -835,22 +992,30 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>>  #define VIDIOC_G_OUTPUT32	_IOR ('V', 46, s32)
>>  #define VIDIOC_S_OUTPUT32	_IOWR('V', 47, s32)
>>  
>> +static void __user *alloc_userspace(unsigned int size, int aux_space, long *err)
> 
> This one is weird. Rather than following the pattern and returning an error
> and assigning the result to a pointer passed to it, it does exactly the
> opposite. I'd change that. I guess this is a matter of opinion to some
> degree at least, though.

I agree, I'll change that.

> 
>> +{
>> +	void __user *up_native;
>> +
>> +	if (aux_space < 0) {
>> +		*err = aux_space;
>> +		return NULL;
>> +	}
>> +	up_native = compat_alloc_user_space(size + aux_space);
>> +	if (!up_native)
>> +		*err = -ENOMEM;
>> +	else if (clear_user(up_native, size))
>> +		*err = -EFAULT;
>> +	else
>> +		return up_native;
>> +	return NULL;
>> +}
>> +
>>  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_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;
> 
> size_t?
> 
>>  	int compatible_arg = 1;
>>  	long err = 0;
>>  
>> @@ -889,30 +1054,47 @@ 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_userspace(sizeof(unsigned int __user),
>> +					    0, &err);
>> +		if (!err && convert_in_user((unsigned int __user *)up_native,
>> +					    (compat_uint_t __user *)up))
>> +			return -EFAULT;
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_G_INPUT:
>>  	case VIDIOC_G_OUTPUT:
>> +		up_native = alloc_userspace(sizeof(unsigned int __user),
>> +					    0, &err);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_G_EDID:
>>  	case VIDIOC_S_EDID:
>> -		err = get_v4l2_edid32(&karg.v2edid, up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_edid), 0, &err);
>> +		err = err ? : get_v4l2_edid32(up_native, up);
> 
> I'd write this as:
> 
> 		if (!err)
> 			err = get_v4l2_edid32(native_up, up);
> 
> Same below.
> 
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_G_FMT:
>>  	case VIDIOC_S_FMT:
>>  	case VIDIOC_TRY_FMT:
>> -		err = get_v4l2_format32(&karg.v2f, up);
>> +		aux_space = bufsize_v4l2_format32(up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_format),
>> +					    aux_space, &err);
>> +		aux_buf = up_native + sizeof(struct v4l2_format);
>> +		err = err ? : get_v4l2_format32(up_native, up,
>> +						aux_buf, aux_space);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_CREATE_BUFS:
>> -		err = get_v4l2_create32(&karg.v2crt, up);
>> +		aux_space = bufsize_v4l2_create32(up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_create_buffers),
>> +					    aux_space, &err);
>> +		aux_buf = up_native + sizeof(struct v4l2_create_buffers);
>> +		err = err ? : get_v4l2_create32(up_native, up,
>> +						aux_buf, aux_space);
>>  		compatible_arg = 0;
>>  		break;
>>  
>> @@ -920,36 +1102,54 @@ 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);
>> +		aux_space = bufsize_v4l2_buffer32(up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_buffer),
>> +					    aux_space, &err);
>> +		aux_buf = up_native + sizeof(struct v4l2_buffer);
>> +		err = err ? : get_v4l2_buffer32(up_native, up,
>> +						aux_buf, aux_space);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_S_FBUF:
>> -		err = get_v4l2_framebuffer32(&karg.v2fb, up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
>> +					    0, &err);
>> +		err = err ? : get_v4l2_framebuffer32(up_native, up);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_G_FBUF:
>> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
>> +					    0, &err);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_ENUMSTD:
>> -		err = get_v4l2_standard32(&karg.v2s, up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_standard),
>> +					    0, &err);
>> +		err = err ? : get_v4l2_standard32(up_native, up);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_ENUMINPUT:
>> -		err = get_v4l2_input32(&karg.v2i, up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_input), 0, &err);
>> +		err = 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(file, &karg.v2ecs, up);
>> +		aux_space = bufsize_v4l2_ext_controls32(up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_ext_controls),
>> +					    aux_space, &err);
>> +		aux_buf = up_native + sizeof(struct v4l2_ext_controls);
>> +		err = err ? : get_v4l2_ext_controls32(file, up_native, up,
>> +						      aux_buf, aux_space);
>>  		compatible_arg = 0;
>>  		break;
>>  	case VIDIOC_DQEVENT:
>> +		up_native = alloc_userspace(sizeof(struct v4l2_event), 0, &err);
>>  		compatible_arg = 0;
>>  		break;
>>  	}
>> @@ -958,13 +1158,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);
>>  
>>  	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
>>  		return err;
>> @@ -976,11 +1171,11 @@ 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(file, &karg.v2ecs, up))
>> +		if (put_v4l2_ext_controls32(file, up_native, up))
>>  			err = -EFAULT;
>>  		break;
>>  	case VIDIOC_S_EDID:
>> -		if (put_v4l2_edid32(&karg.v2edid, up))
>> +		if (put_v4l2_edid32(up_native, up))
>>  			err = -EFAULT;
>>  		break;
>>  	}
>> @@ -992,44 +1187,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((compat_uint_t __user *)up,
>> +				      ((unsigned int __user *)up_native));
> 
> Do note that convert_in_user() returns Boolean. This will effectively yield
> 1 in case of an error here. How about changing convert_in_user() to return
> whatever get_user / put_user do, to avoid errors in the future?

That's better indeed. Good catch.

> 
>>  		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_G_EDID:
>> -		err = put_v4l2_edid32(&karg.v2edid, up);
>> +		err = put_v4l2_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_PREPARE_BUF:
>>  	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;
> 

Thanks for the thorough review! Much appreciated.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt
  2018-01-29 10:09     ` Hans Verkuil
@ 2018-01-29 20:57       ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-29 20:57 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

On Mon, Jan 29, 2018 at 11:09:32AM +0100, Hans Verkuil wrote:
> On 01/26/2018 03:41 PM, Sakari Ailus wrote:
> > Hi Hans,
> > 
> > On Fri, Jan 26, 2018 at 01:43:17PM +0100, Hans Verkuil wrote:
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> Don't duplicate the buffer type checks in enum/g/s/try_fmt.
> >> The check_fmt function does that already.
> >>
> >> It is hard to keep the checks in sync for all these functions and
> >> in fact the check for VBI was wrong in the _fmt functions as it
> >> allowed SDR types as well. This caused a v4l2-compliance failure
> >> for /dev/swradio0 using vivid.
> >>
> >> This simplifies the code and keeps the check in one place and
> >> fixes the SDR/VBI bug.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> ---
> >>  drivers/media/v4l2-core/v4l2-ioctl.c | 140 ++++++++++++++---------------------
> >>  1 file changed, 54 insertions(+), 86 deletions(-)
> >>
> >> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> >> index 59d2100eeff6..c7f6b65d3ad7 100644
> >> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> >> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> >> @@ -1316,52 +1316,50 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
> >>  				struct file *file, void *fh, void *arg)
> >>  {
> >>  	struct v4l2_fmtdesc *p = arg;
> >> -	struct video_device *vfd = video_devdata(file);
> >> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> >> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> >> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> >> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> >> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> >> -	int ret = -EINVAL;
> >> +	int ret = check_fmt(file, p->type);
> > 
> > I'd separate this from the variable declaration. The function is doing more
> > than just fetch something to be used as a shorthand locally. I.e.
> > 
> > 	int ret;
> > 
> > 	ret = check_fmt(file, p->type);
> > 
> > Same elsewhere.
> 
> OK.

Ack.

With that,

Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>

> 
> > 
> > The patch appears to be making an assumption that get_fmt will be
> > universally supported on any buffer type, or that buffer type is not
> > supported at all. I don't see a problem with the approach, but it'd be nice
> > to document it, perhaps in struct v4l2_ioctl_ops KernelDoc documentation.
> > 
> > check_fmt() allows V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_BUF_TYPE_VBI_OUTPUT,
> > V4L2_BUF_TYPE_SLICED_VBI_CAPTURE and V4L2_BUF_TYPE_SLICED_VBI_OUTPUT that
> > the original code did not for VIDIOC_ENUM_FMT. Is the change intentional?
> 
> ENUM_FMT isn't supported for VBI. So v4l_enum_fmt() in v4l2-ioctl.c will
> always return -EINVAL. So the extra check that check_fmt does is a bit
> overkill for this specific ioctl. But applications won't be able to use
> ENUM_FMT for VBI anyway, so that extra check is not a problem.
> 
> Regards,
> 
> 	Hans
> 
> > 
> > Documentation should be updated regarding SDR and META formats (buffer
> > type) but that's out of scope of the patchset:
> > 
> > <URL:https://hverkuil.home.xs4all.nl/spec/uapi/v4l/vidioc-enum-fmt.html>
> > 
> >> +
> >> +	if (ret)
> >> +		return ret;
> >> +	ret = -EINVAL;
> >>  
> >>  	switch (p->type) {
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_enum_fmt_vid_cap))
> >> +		if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
> >>  			break;
> >>  		ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
> >>  		break;
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap_mplane))
> >> +		if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
> >>  			break;
> >>  		ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
> >>  		break;
> >>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_overlay))
> >> +		if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
> >>  			break;
> >>  		ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
> >>  		break;
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out))
> >> +		if (unlikely(!ops->vidioc_enum_fmt_vid_out))
> >>  			break;
> >>  		ret = ops->vidioc_enum_fmt_vid_out(file, fh, arg);
> >>  		break;
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out_mplane))
> >> +		if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
> >>  			break;
> >>  		ret = ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
> >>  		break;
> >>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> >> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_enum_fmt_sdr_cap))
> >> +		if (unlikely(!ops->vidioc_enum_fmt_sdr_cap))
> >>  			break;
> >>  		ret = ops->vidioc_enum_fmt_sdr_cap(file, fh, arg);
> >>  		break;
> >>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> >> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_enum_fmt_sdr_out))
> >> +		if (unlikely(!ops->vidioc_enum_fmt_sdr_out))
> >>  			break;
> >>  		ret = ops->vidioc_enum_fmt_sdr_out(file, fh, arg);
> >>  		break;
> >>  	case V4L2_BUF_TYPE_META_CAPTURE:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_meta_cap))
> >> +		if (unlikely(!ops->vidioc_enum_fmt_meta_cap))
> >>  			break;
> >>  		ret = ops->vidioc_enum_fmt_meta_cap(file, fh, arg);
> >>  		break;
> >> @@ -1375,13 +1373,10 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
> >>  				struct file *file, void *fh, void *arg)
> >>  {
> >>  	struct v4l2_format *p = arg;
> >> -	struct video_device *vfd = video_devdata(file);
> >> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> >> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> >> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> >> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> >> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> >> -	int ret;
> >> +	int ret = check_fmt(file, p->type);
> >> +
> >> +	if (ret)
> >> +		return ret;
> >>  
> >>  	/*
> >>  	 * fmt can't be cleared for these overlay types due to the 'clips'
> >> @@ -1409,7 +1404,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
> >>  
> >>  	switch (p->type) {
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_g_fmt_vid_cap))
> >> +		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
> >>  			break;
> >>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>  		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
> >> @@ -1417,23 +1412,15 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
> >>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>  		return ret;
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_overlay))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
> >> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_vbi_cap))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> >> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_cap))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out))
> >> +		if (unlikely(!ops->vidioc_g_fmt_vid_out))
> >>  			break;
> >>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>  		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
> >> @@ -1441,32 +1428,18 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
> >>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>  		return ret;
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_overlay))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> >> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_vbi_out))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> >> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_out))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> >> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_g_fmt_sdr_cap))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_sdr_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> >> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_g_fmt_sdr_out))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_sdr_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_META_CAPTURE:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_meta_cap))
> >> -			break;
> >>  		return ops->vidioc_g_fmt_meta_cap(file, fh, arg);
> >>  	}
> >>  	return -EINVAL;
> >> @@ -1492,12 +1465,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
> >>  {
> >>  	struct v4l2_format *p = arg;
> >>  	struct video_device *vfd = video_devdata(file);
> >> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> >> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> >> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> >> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> >> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> >> -	int ret;
> >> +	int ret = check_fmt(file, p->type);
> >> +
> >> +	if (ret)
> >> +		return ret;
> >>  
> >>  	ret = v4l_enable_media_source(vfd);
> >>  	if (ret)
> >> @@ -1506,37 +1477,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
> >>  
> >>  	switch (p->type) {
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_s_fmt_vid_cap))
> >> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.pix);
> >>  		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
> >>  		/* just in case the driver zeroed it again */
> >>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >> -		if (is_tch)
> >> +		if (vfd->vfl_type == VFL_TYPE_TOUCH)
> >>  			v4l_pix_format_touch(&p->fmt.pix);
> >>  		return ret;
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane))
> >> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>  		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_overlay))
> >> +		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.win);
> >>  		return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
> >> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_vbi_cap))
> >> +		if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
> >>  		return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> >> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_cap))
> >> +		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
> >>  		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out))
> >> +		if (unlikely(!ops->vidioc_s_fmt_vid_out))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.pix);
> >>  		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
> >> @@ -1544,37 +1515,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
> >>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>  		return ret;
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane))
> >> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>  		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_overlay))
> >> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.win);
> >>  		return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> >> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_vbi_out))
> >> +		if (unlikely(!ops->vidioc_s_fmt_vbi_out))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
> >>  		return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> >> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_out))
> >> +		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
> >>  		return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> >> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_s_fmt_sdr_cap))
> >> +		if (unlikely(!ops->vidioc_s_fmt_sdr_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
> >>  		return ops->vidioc_s_fmt_sdr_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> >> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_s_fmt_sdr_out))
> >> +		if (unlikely(!ops->vidioc_s_fmt_sdr_out))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
> >>  		return ops->vidioc_s_fmt_sdr_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_META_CAPTURE:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_meta_cap))
> >> +		if (unlikely(!ops->vidioc_s_fmt_meta_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.meta);
> >>  		return ops->vidioc_s_fmt_meta_cap(file, fh, arg);
> >> @@ -1586,19 +1557,16 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
> >>  				struct file *file, void *fh, void *arg)
> >>  {
> >>  	struct v4l2_format *p = arg;
> >> -	struct video_device *vfd = video_devdata(file);
> >> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> >> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> >> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> >> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> >> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> >> -	int ret;
> >> +	int ret = check_fmt(file, p->type);
> >> +
> >> +	if (ret)
> >> +		return ret;
> >>  
> >>  	v4l_sanitize_format(p);
> >>  
> >>  	switch (p->type) {
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_try_fmt_vid_cap))
> >> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.pix);
> >>  		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
> >> @@ -1606,27 +1574,27 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
> >>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>  		return ret;
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane))
> >> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>  		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_overlay))
> >> +		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.win);
> >>  		return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
> >> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_vbi_cap))
> >> +		if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
> >>  		return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> >> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_cap))
> >> +		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
> >>  		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out))
> >> +		if (unlikely(!ops->vidioc_try_fmt_vid_out))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.pix);
> >>  		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
> >> @@ -1634,37 +1602,37 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
> >>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
> >>  		return ret;
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane))
> >> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
> >>  		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_overlay))
> >> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.win);
> >>  		return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> >> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_vbi_out))
> >> +		if (unlikely(!ops->vidioc_try_fmt_vbi_out))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
> >>  		return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> >> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_out))
> >> +		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
> >>  		return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> >> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_try_fmt_sdr_cap))
> >> +		if (unlikely(!ops->vidioc_try_fmt_sdr_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
> >>  		return ops->vidioc_try_fmt_sdr_cap(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> >> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_try_fmt_sdr_out))
> >> +		if (unlikely(!ops->vidioc_try_fmt_sdr_out))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
> >>  		return ops->vidioc_try_fmt_sdr_out(file, fh, arg);
> >>  	case V4L2_BUF_TYPE_META_CAPTURE:
> >> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_meta_cap))
> >> +		if (unlikely(!ops->vidioc_try_fmt_meta_cap))
> >>  			break;
> >>  		CLEAR_AFTER_FIELD(p, fmt.meta);
> >>  		return ops->vidioc_try_fmt_meta_cap(file, fh, arg);
> >> -- 
> >> 2.15.1
> >>
> > 
> 

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 09/12] v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32
  2018-01-29 14:13     ` Mauro Carvalho Chehab
@ 2018-01-29 21:00       ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-29 21:00 UTC (permalink / raw)
  To: Mauro Carvalho Chehab
  Cc: Hans Verkuil, linux-media, Daniel Mentz, Hans Verkuil

On Mon, Jan 29, 2018 at 12:13:14PM -0200, Mauro Carvalho Chehab wrote:
> Em Mon, 29 Jan 2018 11:47:42 +0200
> Sakari Ailus <sakari.ailus@iki.fi> escreveu:
> 
> 
> > > +	compat_caddr_t p;
> > > +
> > >  	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) ||
> > >  	    put_user(kp->global_alpha, &up->global_alpha))
> > >  		return -EFAULT;  
> > 
> > One more newline here?
> 
> Why? A new line here would be weird.

There are two if clauses that are unrelated. I'd say it improves
readability to separate them. That said, this isn't an exact science, so I
leave this up to Hans to decide.

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 11/12] v4l2-compat-ioctl32.c: don't copy back the result for certain errors
  2018-01-29 10:02     ` Hans Verkuil
@ 2018-01-29 21:01       ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-29 21:01 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On Mon, Jan 29, 2018 at 11:02:56AM +0100, Hans Verkuil wrote:
> On 01/29/2018 10:56 AM, Sakari Ailus wrote:
> > Hi Hans,
> > 
> > On Fri, Jan 26, 2018 at 01:43:26PM +0100, Hans Verkuil wrote:
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> Some ioctls need to copy back the result even if the ioctl returned
> >> an error. However, don't do this for the error codes -ENOTTY, -EFAULT
> >> and -ENOIOCTLCMD. It makes no sense in those cases.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> > 
> > Acked-by: Sakari Ailus <sakari.ailus@linux.intel.com>
> > 
> > Shouldn't such a change be made to video_usercopy() as well? Doesn't need
> > to be in this set though.
> 
> Good point. I'll add that for v2. This is not actually a bug as such, but
> it's just weird to copy back results if the ioctl wasn't implemented at all.
> 
> I realize that I need to drop the -EFAULT check: if you call VIDIOC_G_EXT_CTRLS
> with an incorrect userspace buffer for the payload, then the control framework
> will set error_idx to the index of the control with the wrong buffer. So you do
> need to copy back the data in case of -EFAULT.
> 
> I can also drop -ENOIOCTLCMD since video_usercopy() converts that to -ENOTTY.

Agreed.

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32
  2018-01-29 17:41     ` Hans Verkuil
@ 2018-01-29 21:14       ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-29 21:14 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Hans,

On Mon, Jan 29, 2018 at 06:41:20PM +0100, Hans Verkuil wrote:
> On 01/29/2018 06:06 PM, Sakari Ailus wrote:
> > Hi Hans,
> > 
> > Thanks for your efforts on this patch and the patchset. Please see my comments below.
> > 
> > On Fri, Jan 26, 2018 at 01:43:27PM +0100, Hans Verkuil wrote:
> >> From: Daniel Mentz <danielmentz@google.com>
> >>
> >> The 32-bit compat v4l2 ioctl is implemented based on its 64-bit
> > 
> > s/v4l2 ioctl/V4L2 IOCTL handling/
> > 
> > ?
> > 
> >> equivalent. It converts 32-bit data structures into its 64-bit
> >> equivalents and needs to provide the data to the 64-bit ioctl in user
> >> space memory which is commonly allocated using
> >> compat_alloc_user_space(). However, due to how that function is
> >> implemented, it can only be called a single time for every syscall
> >> invocation.  Supposedly to avoid this limitation, the existing code uses
> >> a mix of memory from the kernel stack and memory allocated through
> >> compat_alloc_user_space(). Under normal circumstances, this would not
> >> work, because the 64-bit ioctl expects all pointers to point to user
> >> space memory. As a workaround, set_fs(KERNEL_DS) is called to
> >> temporarily disable this extra safety check and allow kernel pointers.
> >> However, this might introduce a security vulnerability: The
> >> result of the 32-bit to 64-bit conversion is writeable by user space
> >> because the output buffer has been allocated via
> >> compat_alloc_user_space(). A malicious user space process could then
> >> manipulate pointers inside this output buffer, and due to the previous
> >> set_fs(KERNEL_DS) call, functions like get_user() or put_user() no longer
> >> prevent kernel memory access.
> >>
> >> The new approach is to pre-calculate the total amount of user space
> >> memory that is needed, allocate it using compat_alloc_user_space() and
> >> then divide up the allocated memory to accommodate all data structures
> >> that need to be converted.
> >>
> >> An alternative approach would have been to retain the union type karg
> >> that they allocated on the kernel stack in do_video_ioctl(), copy all
> >> data from user space into karg and then back to user space. However,
> >> we decided against this approach because it does not align with other
> >> compat syscall implementations. Instead, we tried to replicate the
> >> get_user/put_user pairs as found in other places in the kernel:
> >>
> >> if (get_user(clipcount, &up->clipcount) ||
> >>     put_user(clipcount, &kp->clipcount)) return -EFAULT;
> >>
> >> Notes from hans.verkuil@cisco.com:
> >>
> >> This patch was taken from
> >> https://github.com/LineageOS/android_kernel_samsung_apq8084/commit/97b733953c06e4f0398ade18850f0817778255f7
> >>
> >> Clearly nobody could be bothered to upstream this patch or at minimum
> >> tell us :-( We only heard about this a week ago.
> > 
> > This is very sad indeed. I would really hope that we'll never run into
> > something this again!
> > 
> >>
> >> This patch was rebased and cleaned up. Compared to the original I
> >> also swapped the order of the convert_in_user arguments so that they
> >> matched copy_in_user. It was hard to review otherwise. I also replaced
> >> the ALLOC_USER_SPACE/ALLOC_AND_GET by a normal function.
> > 
> > The result looks more complicated than it should be.
> > 
> > I wonder if the result could be made cleaner by separating the argument
> > checking and copying from / to user space in video_usercopy. That
> > separation almost completely exists already. Rather than mangling the
> > compat argument struct to look like a user-provided 64-bit argument struct,
> > copying the arguments from and to the user space would be separately done
> > to 32-bit compat IOCTL arguments.
> > 
> > I suppose that wouldn't be a trivial change either, so for now I'd maintain
> > the approach and later consider how to make this more maintainable in the
> > future. Still, that approach is quite different and it'd be easier to
> > switch from status quo rather than this last patch, suggesting to start
> > with a revert, should we embark into this direction.
> > 
> > What do you think?
> 
> To be honest, I don't really know. Things will get even more complicated
> once we have to deal with the year 2038 problem since that impacts this
> code as well. I'm waiting for the fence code and request API code to
> land before continuing working on that. Perhaps that might be a good
> time to take a step back and see if we can do something smarter.

Sounds good to me.

> 
> > 
> >>
> >> Signed-off-by: Daniel Mentz <danielmentz@google.com>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> ---
> >>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 700 ++++++++++++++++----------
> >>  1 file changed, 448 insertions(+), 252 deletions(-)
> >>
> >> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> >> index 2aa9b43daf60..27a5a0961cbd 100644
> >> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> >> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
> >> @@ -22,6 +22,14 @@
> >>  #include <media/v4l2-ctrls.h>
> >>  #include <media/v4l2-ioctl.h>
> >>  
> >> +/* Use the same argument order as copy_in_user */
> >> +#define convert_in_user(to, from)			\
> > 
> > This doesn't really convert anything. How about calling it assign_in_user,
> > for example?
> 
> Sounds reasonable.
> 
> > 
> >> +({							\
> >> +	typeof(*from) val;				\
> > 
> > "val" is very short, easily leading to hard-to-find namespace collisions.
> > At least the versions of GCC I've used happily get this wrong without a
> > warning.
> > 
> > How about __convert_in_user_tmp instead?
> 
> Hmm. What about __assign_tmp?

That seems a little risky still. Perhaps unlikely; I'd use a longer,
unconceivable name to be safe.

> 
> > 
> >> +							\
> >> +	get_user(val, from) || put_user(val, to);	\
> >> +})
> >> +
> >>  static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> >>  {
> >>  	long ret = -ENOIOCTLCMD;
> >> @@ -48,37 +56,41 @@ struct v4l2_window32 {
> >>  	__u8                    global_alpha;
> >>  };
> >>  
> >> -static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
> >> +static int get_v4l2_window32(struct v4l2_window __user *kp,
> >> +			     struct v4l2_window32 __user *up,
> >> +			     void __user *aux_buf, int aux_space)
> >>  {
> >>  	struct v4l2_clip32 __user *uclips;
> >>  	struct v4l2_clip __user *kclips;
> >>  	compat_caddr_t p;
> >> -	u32 n;
> >> +	u32 clipcount;
> >>  
> >>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> >> -	    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) ||
> >> -	    get_user(kp->global_alpha, &up->global_alpha))
> >> +	    copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
> >> +	    convert_in_user(&kp->field, &up->field) ||
> >> +	    convert_in_user(&kp->chromakey, &up->chromakey) ||
> >> +	    convert_in_user(&kp->global_alpha, &up->global_alpha) ||
> >> +	    get_user(clipcount, &up->clipcount) ||
> >> +	    put_user(clipcount, &kp->clipcount))
> >>  		return -EFAULT;
> >> -	if (kp->clipcount > 2048)
> >> +	if (clipcount > 2048)
> >>  		return -EINVAL;
> >> -	if (!kp->clipcount) {
> >> -		kp->clips = NULL;
> >> -		return 0;
> >> -	}
> >> +	if (!clipcount)
> >> +		return put_user(NULL, &kp->clips) ? -EFAULT : 0;
> > 
> > Isn't this what put_user would return anyway?
> 
> True, I'll simplify this.
> 
> >>  
> >> -	n = kp->clipcount;
> >>  	if (get_user(p, &up->clips))
> >>  		return -EFAULT;
> >>  	uclips = compat_ptr(p);
> >> -	kclips = compat_alloc_user_space(n * sizeof(*kclips));
> >> -	kp->clips = kclips;
> >> -	while (n--) {
> >> +	if (aux_space < clipcount * sizeof(*kclips))
> >> +		return -EFAULT;
> >> +	kclips = aux_buf;
> >> +	if (put_user(kclips, &kp->clips))
> >> +		return -EFAULT;
> >> +
> >> +	while (clipcount--) {
> >>  		if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
> >>  			return -EFAULT;
> >> -		if (put_user(n ? kclips + 1 : NULL, &kclips->next))
> >> +		if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next))
> >>  			return -EFAULT;
> >>  		uclips++;
> >>  		kclips++;
> >> @@ -86,26 +98,28 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
> >>  	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)
> >>  {
> >>  	struct v4l2_clip __user *kclips = kp->clips;
> >>  	struct v4l2_clip32 __user *uclips;
> >> -	int n = kp->clipcount;
> >>  	compat_caddr_t p;
> >> -
> >> -	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) ||
> >> -	    put_user(kp->global_alpha, &up->global_alpha))
> >> +	u32 clipcount;
> >> +
> >> +	if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
> >> +	    convert_in_user(&up->field, &kp->field) ||
> >> +	    convert_in_user(&up->chromakey, &kp->chromakey) ||
> >> +	    convert_in_user(&up->global_alpha, &kp->global_alpha) ||
> >> +	    get_user(clipcount, &kp->clipcount) ||
> >> +	    put_user(clipcount, &up->clipcount))
> >>  		return -EFAULT;
> >> -	if (!kp->clipcount)
> >> +	if (!clipcount)
> >>  		return 0;
> >>  
> >>  	if (get_user(p, &up->clips))
> >>  		return -EFAULT;
> >>  	uclips = compat_ptr(p);
> >> -	while (n--) {
> >> +	while (clipcount--) {
> >>  		if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
> >>  			return -EFAULT;
> >>  		uclips++;
> >> @@ -145,107 +159,161 @@ 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)
> > 
> > This is effectively returning the size of the non-compat struct, or just
> > the part of which is variable. How about calling it __bufsize_v4l2_format
> > instead? The same for other such functions.
> 
> Ack.
> 
> > I'd also separate the size calculated by the function and the return value.
> 
> That's cleaner, yes.
> 
> > 
> >>  {
> >> -	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: {
> >> +		u32 clipcount;
> >> +
> >> +		if (get_user(clipcount, &up->fmt.win.clipcount))
> >> +			return -EFAULT;
> >> +		if (clipcount > 2048)
> >> +			return -EINVAL;
> >> +		return clipcount * sizeof(struct v4l2_clip);
> >> +	}
> >> +	default:
> >> +		return 0;
> >> +	}
> >> +}
> >> +
> >> +static int bufsize_v4l2_format32(struct v4l2_format32 __user *up)
> >> +{
> >> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)))
> >> +		return -EFAULT;
> > 
> > Newline?
> > 
> >> +	return __bufsize_v4l2_format32(up);
> >> +}
> >> +
> >> +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 copy_from_user(&kp->fmt.pix, &up->fmt.pix,
> >> -				      sizeof(kp->fmt.pix)) ? -EFAULT : 0;
> >> +		return copy_in_user(&kp->fmt.pix, &up->fmt.pix,
> >> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >> -		return copy_from_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
> >> -				      sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
> >> +		return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
> >> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
> >>  	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 copy_from_user(&kp->fmt.vbi, &up->fmt.vbi,
> >> -				      sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
> >> +		return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi,
> >> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> >> -		return copy_from_user(&kp->fmt.sliced, &up->fmt.sliced,
> >> -				      sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
> >> +		return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced,
> >> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> >>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> >> -		return copy_from_user(&kp->fmt.sdr, &up->fmt.sdr,
> >> -				      sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
> >> +		return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr,
> >> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_META_CAPTURE:
> >> -		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
> >> -				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
> >> +		return copy_in_user(&kp->fmt.meta, &up->fmt.meta,
> >> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
> >>  	default:
> >>  		return -EINVAL;
> >>  	}
> >>  }
> >>  
> >> -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __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(*up)))
> >> +		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(*up)))
> >>  		return -EFAULT;
> >> -	return __get_v4l2_format32(kp, up);
> >> +	return __bufsize_v4l2_format32(&up->format);
> >>  }
> >>  
> >> -static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
> >> +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(*up)) ||
> >> -	    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)
> >>  {
> >> -	if (put_user(kp->type, &up->type))
> >> +	u32 type;
> >> +
> >> +	if (get_user(type, &kp->type))
> >>  		return -EFAULT;
> >>  
> >> -	switch (kp->type) {
> >> +	switch (type) {
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
> >> -		return copy_to_user(&up->fmt.pix, &kp->fmt.pix,
> >> -				    sizeof(kp->fmt.pix)) ?  -EFAULT : 0;
> >> +		return copy_in_user(&up->fmt.pix, &kp->fmt.pix,
> >> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
> >> -		return copy_to_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
> >> -				    sizeof(kp->fmt.pix_mp)) ?  -EFAULT : 0;
> >> +		return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
> >> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
> >>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
> >>  		return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
> >>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
> >>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
> >> -		return copy_to_user(&up->fmt.vbi, &kp->fmt.vbi,
> >> -				    sizeof(kp->fmt.vbi)) ?  -EFAULT : 0;
> >> +		return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi,
> >> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
> >>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
> >> -		return copy_to_user(&up->fmt.sliced, &kp->fmt.sliced,
> >> -				    sizeof(kp->fmt.sliced)) ?  -EFAULT : 0;
> >> +		return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced,
> >> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
> >>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
> >> -		return copy_to_user(&up->fmt.sdr, &kp->fmt.sdr,
> >> -				    sizeof(kp->fmt.sdr)) ?  -EFAULT : 0;
> >> +		return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr,
> >> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
> >>  	case V4L2_BUF_TYPE_META_CAPTURE:
> >> -		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
> >> -				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
> >> +		return copy_in_user(&up->fmt.meta, &kp->fmt.meta,
> >> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
> >>  	default:
> >>  		return -EINVAL;
> >>  	}
> >>  }
> >>  
> >> -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(*up)))
> > 
> > For some pointers we do perform such checks, for others we don't. While
> > this patch doesn't change this, it'd be good to align this by either
> > introducing it everywhere or removing it everywhere.
> > 
> > I guess that can be done separately.
> > 
> >>  		return -EFAULT;
> >>  	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(*up)) ||
> >> -	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
> >> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
> >> +	    copy_in_user(up, kp,
> >> +			 offsetof(struct v4l2_create_buffers32, format)) ||
> >> +	    copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
> >>  		return -EFAULT;
> >>  	return __put_v4l2_format32(&kp->format, &up->format);
> >>  }
> >> @@ -259,25 +327,27 @@ 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(*up)) ||
> >> -	    get_user(kp->index, &up->index))
> >> +	    convert_in_user(&kp->index, &up->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(*up)) ||
> >> -	    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, sizeof(kp->reserved)))
> >> +	    convert_in_user(&up->index, &kp->index) ||
> >> +	    copy_in_user(&up->id, &kp->id, sizeof(up->id)) ||
> > 
> > Why not convert_in_user()?
> 
> Will change.
> 
> > 
> >> +	    copy_in_user(up->name, kp->name, 24) ||
> 
> Urg. I missed this, this has a fixed number as the last argument instead of
> sizeof(kp->name).

Right; I didn't comment on this as it's not a property of this patch. I'd
actually fix this separately. Up to you. It's a very risky habit,
especially with reserved fields.

> 
> >> +	    copy_in_user(&up->frameperiod, &kp->frameperiod,
> >> +			 sizeof(up->frameperiod)) ||
> >> +	    convert_in_user(&up->framelines, &kp->framelines) ||
> >> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
> >>  		return -EFAULT;
> >>  	return 0;
> >>  }
> >> @@ -317,10 +387,10 @@ struct v4l2_buffer32 {
> >>  	__u32			reserved;
> >>  };
> >>  
> >> -static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
> >> +static int get_v4l2_plane32(struct v4l2_plane __user *up,
> >> +			    struct v4l2_plane32 __user *up32,
> >>  			    enum v4l2_memory memory)
> >>  {
> >> -	void __user *up_pln;
> >>  	compat_long_t p;
> > 
> > Shouldn't this be compat_ulong_t?
> 
> That's better, yes.
> 
> > 
> >>  
> >>  	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
> >> @@ -336,10 +406,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
> >>  			return -EFAULT;
> >>  		break;
> >>  	case 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;
> >>  		break;
> >>  	case V4L2_MEMORY_DMABUF:
> >> @@ -351,7 +419,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
> >>  	return 0;
> >>  }
> >>  
> >> -static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
> >> +static int put_v4l2_plane32(struct v4l2_plane __user *up,
> >> +			    struct v4l2_plane32 __user *up32,
> >>  			    enum v4l2_memory memory)
> >>  {
> >>  	unsigned long p;
> >> @@ -375,8 +444,7 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
> >>  			return -EFAULT;
> >>  		break;
> >>  	case V4L2_MEMORY_DMABUF:
> >> -		if (copy_in_user(&up32->m.fd, &up->m.fd,
> >> -				 sizeof(up->m.fd)))
> >> +		if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd)))
> >>  			return -EFAULT;
> >>  		break;
> >>  	}
> >> @@ -384,37 +452,68 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
> >>  	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(*up)) ||
> >> +	    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;
> >> +	int num_planes;
> > 
> > u32?
> > 
> >>  	int ret;
> >>  
> >>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> >> -	    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(&kp->index, &up->index) ||
> >> +	    get_user(type, &up->type) ||
> >> +	    put_user(type, &kp->type) ||
> >> +	    convert_in_user(&kp->flags, &up->flags) ||
> >> +	    get_user(memory, &up->memory) ||
> >> +	    put_user(memory, &kp->memory) ||
> >> +	    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(&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))
> >>  			return -EFAULT;
> >>  
> >> -	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
> >> -		unsigned int num_planes;
> >> -
> >> -		if (kp->length == 0) {
> >> -			kp->m.planes = NULL;
> >> +	if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
> >> +		num_planes = length;
> > 
> > Do you need a local variable for just that?
> > 
> >> +		if (num_planes == 0) {
> >>  			/* num_planes == 0 is legal, e.g. when userspace doesn't
> >>  			 * need planes array on DQBUF*/
> >> -			return 0;
> >> -		} else if (kp->length > VIDEO_MAX_PLANES) {
> >> +			return put_user(NULL, &kp->m.planes);
> >> +		} else if (num_planes > VIDEO_MAX_PLANES) {
> >>  			return -EINVAL;
> >>  		}
> >>  
> >> @@ -423,40 +522,47 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
> >>  
> >>  		uplane32 = compat_ptr(p);
> >>  		if (!access_ok(VERIFY_READ, uplane32,
> >> -			       kp->length * sizeof(*uplane32)))
> >> +				num_planes * sizeof(*uplane32)))
> > 
> > Alignment.
> > 
> >>  			return -EFAULT;
> >>  
> >>  		/* We don't really care if userspace decides to kill itself
> >>  		 * by passing a very big num_planes value */
> >> -		uplane = compat_alloc_user_space(kp->length * sizeof(*uplane));
> >> -		kp->m.planes = (__force struct v4l2_plane *)uplane;
> >> +		if (aux_space < num_planes * sizeof(*uplane))
> >> +			return -EFAULT;
> >> +
> >> +		uplane = aux_buf;
> >> +		if (put_user((__force struct v4l2_plane *)uplane,
> >> +					&kp->m.planes))
> >> +			return -EFAULT;
> >>  
> >> -		for (num_planes = 0; num_planes < kp->length; num_planes++) {
> >> -			ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
> >> +		while (--num_planes >= 0) {
> > 
> > while (num_planes--) {
> > 
> > ?
> 
> Yes. I missed this one :-)
> 
> > 
> >> +			ret = get_v4l2_plane32(uplane, uplane32, memory);
> >>  			if (ret)
> >>  				return ret;
> >> -			++uplane;
> >> -			++uplane32;
> >> +			uplane++;
> >> +			uplane32++;
> >>  		}
> >>  	} else {
> >> -		switch (kp->memory) {
> >> +		switch (memory) {
> >>  		case V4L2_MEMORY_MMAP:
> >> -		case V4L2_MEMORY_OVERLAY:
> >> -			if (get_user(kp->m.offset, &up->m.offset))
> >> +			if (convert_in_user(&kp->m.offset, &up->m.offset))
> >>  				return -EFAULT;
> >>  			break;
> >> -		case V4L2_MEMORY_USERPTR:
> >> -			{
> >> -				compat_long_t tmp;
> >> -
> >> -				if (get_user(tmp, &up->m.userptr))
> >> -					return -EFAULT;
> >> +		case V4L2_MEMORY_USERPTR: {
> >> +			compat_long_t tmp;
> > 
> > compat_ulong_t, I'd call it "userptr". The latter is entirely up to you.
> > 
> >>  
> >> -				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 (convert_in_user(&kp->m.offset, &up->m.offset))
> >> +				return -EFAULT;
> > 
> > Is there a reason to split this off from MMAP handling? The code is the
> > same.
> 
> Huh, I missed this as well. I merged MMAP and OVERLAY elsewhere.
> 
> > 
> >>  			break;
> >>  		case V4L2_MEMORY_DMABUF:
> >> -			if (get_user(kp->m.fd, &up->m.fd))
> >> +			if (convert_in_user(&kp->m.fd, &up->m.fd))
> >>  				return -EFAULT;
> >>  			break;
> >>  		}
> >> @@ -465,8 +571,12 @@ 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;
> >> @@ -474,53 +584,60 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
> >>  	int ret;
> >>  
> >>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
> >> -	    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(&up->index, &kp->index) ||
> >> +	    get_user(type, &kp->type) ||
> >> +	    put_user(type, &up->type) ||
> >> +	    convert_in_user(&up->flags, &kp->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(kp->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(&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) ||
> >> +	    copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
> >> +	    convert_in_user(&up->sequence, &kp->sequence) ||
> >> +	    convert_in_user(&up->reserved2, &kp->reserved2) ||
> >> +	    convert_in_user(&up->reserved, &kp->reserved) ||
> >> +	    get_user(length, &kp->length) ||
> >> +	    put_user(length, &up->length))
> >>  		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 = (__force struct v4l2_plane __user *)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) {
> > 
> > 
> > while (num_planes--) {
> > 
> > Or rather: s/num_planes/length/
> > 
> > Then you can remove num_planes.
> 
> I will probably keep num_planes. It's more descriptive than 'length'.

You have two u32 local variables for the same purpose. I'd suggest to leave
either.

> 
> > 
> >> -			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:
> >> -		case V4L2_MEMORY_OVERLAY:
> >> -			if (put_user(kp->m.offset, &up->m.offset))
> >> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
> >>  				return -EFAULT;
> >>  			break;
> >>  		case V4L2_MEMORY_USERPTR:
> >> -			if (put_user(kp->m.userptr, &up->m.userptr))
> >> +			if (convert_in_user(&up->m.userptr, &kp->m.userptr))
> >> +				return -EFAULT;
> >> +			break;
> >> +		case V4L2_MEMORY_OVERLAY:
> >> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
> > 
> > Combine with MMAP case?
> 
> Hmm. I did combine this, but I probably lost that change after some later
> patch modifications. Thanks for noticing.
> 
> > 
> >>  				return -EFAULT;
> >>  			break;
> >>  		case V4L2_MEMORY_DMABUF:
> >> -			if (put_user(kp->m.fd, &up->m.fd))
> >> +			if (convert_in_user(&up->m.fd, &kp->m.fd))
> >>  				return -EFAULT;
> >>  			break;
> >>  		}
> >> @@ -545,29 +662,32 @@ struct v4l2_framebuffer32 {
> >>  	} 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(*up)) ||
> >>  	    get_user(tmp, &up->base) ||
> >> -	    get_user(kp->capability, &up->capability) ||
> >> -	    get_user(kp->flags, &up->flags) ||
> >> -	    copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
> >> +	    put_user((__force void *)compat_ptr(tmp), &kp->base) ||
> >> +	    convert_in_user(&kp->capability, &up->capability) ||
> >> +	    convert_in_user(&kp->flags, &up->flags) ||
> >> +	    copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt)))
> >>  		return -EFAULT;
> >> -	kp->base = (__force void *)compat_ptr(tmp);
> >>  	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(*up)) ||
> >> -	    put_user(tmp, &up->base) ||
> >> -	    put_user(kp->capability, &up->capability) ||
> >> -	    put_user(kp->flags, &up->flags) ||
> >> -	    copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
> >> +	    get_user(base, &kp->base) ||
> >> +	    put_user(ptr_to_compat(base), &up->base) ||
> >> +	    convert_in_user(&up->capability, &kp->capability) ||
> >> +	    convert_in_user(&up->flags, &kp->flags) ||
> >> +	    copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt)))
> >>  		return -EFAULT;
> >>  	return 0;
> >>  }
> >> @@ -586,16 +706,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 *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(*up)))
> >> +	if (copy_in_user(kp, up, sizeof(*up)))
> >>  		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(*up)))
> >> +	if (copy_in_user(up, kp, sizeof(*up)))
> >>  		return -EFAULT;
> >>  	return 0;
> >>  }
> >> @@ -648,39 +770,58 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id)
> >>  	return !ret && (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
> >>  }
> >>  
> >> +static int bufsize_v4l2_ext_controls32(struct v4l2_ext_controls32 __user *up)
> >> +{
> >> +	u32 count;
> >> +
> >> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> >> +	    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 file *file,
> >> -				   struct v4l2_ext_controls *kp,
> >> -				   struct v4l2_ext_controls32 __user *up)
> >> +				   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;
> >> +	u32 count;
> >>  	unsigned int n;
> > 
> > u32 n
> > 
> >>  	compat_caddr_t p;
> >>  
> >>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> >> -	    get_user(kp->which, &up->which) ||
> >> -	    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(&kp->which, &up->which) ||
> >> +	    get_user(count, &up->count) ||
> >> +	    put_user(count, &kp->count) ||
> >> +	    convert_in_user(&kp->error_idx, &up->error_idx) ||
> >> +	    copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
> >>  		return -EFAULT;
> >> -	if (kp->count == 0) {
> >> -		kp->controls = NULL;
> >> -		return 0;
> >> -	} else if (kp->count > V4L2_CID_MAX_CTRLS) {
> >> +	if (count == 0)
> >> +		return put_user(NULL, &kp->controls);
> >> +	else if (kp->count > V4L2_CID_MAX_CTRLS)
> > 
> > count

    ^

Could you comment on this one...

> > 
> >>  		return -EINVAL;
> >> -	}
> >>  	if (get_user(p, &up->controls))
> >>  		return -EFAULT;
> >>  	ucontrols = compat_ptr(p);
> >> -	if (!access_ok(VERIFY_READ, ucontrols, kp->count * sizeof(*ucontrols)))
> >> +	if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols)))

						^

...for, as far as I see, this may overflow?

> >> +		return -EFAULT;
> >> +	if (aux_space < count * sizeof(*kcontrols))
> >>  		return -EFAULT;
> >> -	kcontrols = compat_alloc_user_space(kp->count * sizeof(*kcontrols));
> >> -	kp->controls = (__force struct v4l2_ext_control *)kcontrols;
> >> -	for (n = 0; n < kp->count; n++) {
> >> +	kcontrols = aux_buf;
> >> +	if (put_user((__force struct v4l2_ext_control *)kcontrols,
> >> +		     &kp->controls))
> >> +		return -EFAULT;
> >> +
> >> +	for (n = 0; n < count; n++) {
> >>  		u32 id;
> >>  
> >>  		if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
> >>  			return -EFAULT;
> >> +
> >>  		if (get_user(id, &kcontrols->id))
> >>  			return -EFAULT;
> > 
> > Newline here, too? Not really a problem with this patch though.
> > 
> >>  		if (ctrl_is_pointer(file, id)) {
> >> @@ -699,36 +840,47 @@ static int get_v4l2_ext_controls32(struct file *file,
> >>  }
> >>  
> >>  static int put_v4l2_ext_controls32(struct file *file,
> >> -				   struct v4l2_ext_controls *kp,
> >> +				   struct v4l2_ext_controls __user *kp,
> >>  				   struct v4l2_ext_controls32 __user *up)
> >>  {
> >>  	struct v4l2_ext_control32 __user *ucontrols;
> >> -	struct v4l2_ext_control __user *kcontrols =
> >> -		(__force struct v4l2_ext_control __user *)kp->controls;
> >> -	int n = kp->count;
> >> +	struct v4l2_ext_control __user *kcontrols;
> >> +	u32 count;
> >> +	unsigned int n;
> > 
> > u32 n
> > 
> >>  	compat_caddr_t p;
> >>  
> >>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
> >> -	    put_user(kp->which, &up->which) ||
> >> -	    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) ||
> > 
> > How about doing this as last? "controls" is the last member of the struct.
> > 
> >> +	    convert_in_user(&up->which, &kp->which) ||
> >> +	    get_user(count, &kp->count) ||
> >> +	    put_user(count, &up->count) ||
> >> +	    convert_in_user(&up->error_idx, &kp->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(*ucontrols)))
> >> +	if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols)))
> >>  		return -EFAULT;
> >>  
> >> -	while (--n >= 0) {
> >> -		unsigned size = sizeof(*ucontrols);
> >> +	for (n = 0; n < count; n++) {
> >> +		unsigned int size = sizeof(*ucontrols);
> >>  		u32 id;
> >>  
> >> +		if (copy_in_user(&ucontrols->id, &kcontrols->id,
> >> +				 sizeof(ucontrols->id)) ||
> > 
> > get_user(id, &kcontrols->id) ||
> > put_user(id, &ucontrols->id) ||
> > 
> >> +		    copy_in_user(&ucontrols->size, &kcontrols->size,
> >> +				 sizeof(ucontrols->size)) ||
> > 
> > convert_in_user() ?
> > 
> >> +		    copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
> >> +				 sizeof(ucontrols->reserved2)))
> >> +			return -EFAULT;
> >> +
> >>  		if (get_user(id, &kcontrols->id))
> >>  			return -EFAULT;
> > 
> > ...and the above if () can be removed.
> > 
> >> +
> >>  		/* Do not modify the pointer when copying a pointer control.
> >>  		   The contents of the pointer was changed, not the pointer
> >>  		   itself. */
> >> @@ -736,6 +888,7 @@ static int put_v4l2_ext_controls32(struct file *file,
> >>  			size -= sizeof(ucontrols->value64);
> > 
> > This chunk is yearning for newlines. Not a fault of this patch though.
> > 
> >>  		if (copy_in_user(ucontrols, kcontrols, size))
> >>  			return -EFAULT;
> >> +
> >>  		ucontrols++;
> >>  		kcontrols++;
> >>  	}
> >> @@ -755,17 +908,18 @@ 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(*up)) ||
> >> -	    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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
> >> -	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
> >> -	    put_user(kp->id, &up->id) ||
> >> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
> >> +	    convert_in_user(&up->type, &kp->type) ||
> >> +	    copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
> >> +	    convert_in_user(&up->pending, &kp->pending) ||
> >> +	    convert_in_user(&up->sequence, &kp->sequence) ||
> >> +	    convert_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
> >> +	    convert_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) ||
> >> +	    convert_in_user(&up->id, &kp->id) ||
> >> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
> >>  		return -EFAULT;
> >>  	return 0;
> >>  }
> >> @@ -778,31 +932,34 @@ struct v4l2_edid32 {
> >>  	compat_caddr_t edid;
> >>  };
> >>  
> >> -static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
> >> +static int get_v4l2_edid32(struct v4l2_edid __user *kp,
> >> +			   struct v4l2_edid32 __user *up)
> >>  {
> >> -	u32 tmp;
> >> +	compat_uptr_t tmp;
> >>  
> >>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
> >> -	    get_user(kp->pad, &up->pad) ||
> >> -	    get_user(kp->start_block, &up->start_block) ||
> >> -	    get_user(kp->blocks, &up->blocks) ||
> >> +	    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(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 = (__force u8 *)compat_ptr(tmp);
> >>  	return 0;
> >>  }
> >>  
> >> -static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
> >> +static int put_v4l2_edid32(struct v4l2_edid __user *kp,
> >> +			   struct v4l2_edid32 __user *up)
> >>  {
> >> -	u32 tmp = (u32)((unsigned long)kp->edid);
> >> +	void *edid;
> >>  
> >>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
> >> -	    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(up->reserved, kp->reserved, sizeof(up->reserved)))
> >> +	    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(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;
> >>  }
> >> @@ -835,22 +992,30 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
> >>  #define VIDIOC_G_OUTPUT32	_IOR ('V', 46, s32)
> >>  #define VIDIOC_S_OUTPUT32	_IOWR('V', 47, s32)
> >>  
> >> +static void __user *alloc_userspace(unsigned int size, int aux_space, long *err)
> > 
> > This one is weird. Rather than following the pattern and returning an error
> > and assigning the result to a pointer passed to it, it does exactly the
> > opposite. I'd change that. I guess this is a matter of opinion to some
> > degree at least, though.
> 
> I agree, I'll change that.
> 
> > 
> >> +{
> >> +	void __user *up_native;
> >> +
> >> +	if (aux_space < 0) {
> >> +		*err = aux_space;
> >> +		return NULL;
> >> +	}
> >> +	up_native = compat_alloc_user_space(size + aux_space);
> >> +	if (!up_native)
> >> +		*err = -ENOMEM;
> >> +	else if (clear_user(up_native, size))
> >> +		*err = -EFAULT;
> >> +	else
> >> +		return up_native;
> >> +	return NULL;
> >> +}
> >> +
> >>  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_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;
> > 
> > size_t?
> > 
> >>  	int compatible_arg = 1;
> >>  	long err = 0;
> >>  
> >> @@ -889,30 +1054,47 @@ 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_userspace(sizeof(unsigned int __user),
> >> +					    0, &err);
> >> +		if (!err && convert_in_user((unsigned int __user *)up_native,
> >> +					    (compat_uint_t __user *)up))
> >> +			return -EFAULT;
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >>  	case VIDIOC_G_INPUT:
> >>  	case VIDIOC_G_OUTPUT:
> >> +		up_native = alloc_userspace(sizeof(unsigned int __user),
> >> +					    0, &err);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >>  	case VIDIOC_G_EDID:
> >>  	case VIDIOC_S_EDID:
> >> -		err = get_v4l2_edid32(&karg.v2edid, up);
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_edid), 0, &err);
> >> +		err = err ? : get_v4l2_edid32(up_native, up);
> > 
> > I'd write this as:
> > 
> > 		if (!err)
> > 			err = get_v4l2_edid32(native_up, up);
> > 
> > Same below.
> > 
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >>  	case VIDIOC_G_FMT:
> >>  	case VIDIOC_S_FMT:
> >>  	case VIDIOC_TRY_FMT:
> >> -		err = get_v4l2_format32(&karg.v2f, up);
> >> +		aux_space = bufsize_v4l2_format32(up);
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_format),
> >> +					    aux_space, &err);
> >> +		aux_buf = up_native + sizeof(struct v4l2_format);
> >> +		err = err ? : get_v4l2_format32(up_native, up,
> >> +						aux_buf, aux_space);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >>  	case VIDIOC_CREATE_BUFS:
> >> -		err = get_v4l2_create32(&karg.v2crt, up);
> >> +		aux_space = bufsize_v4l2_create32(up);
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_create_buffers),
> >> +					    aux_space, &err);
> >> +		aux_buf = up_native + sizeof(struct v4l2_create_buffers);
> >> +		err = err ? : get_v4l2_create32(up_native, up,
> >> +						aux_buf, aux_space);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >> @@ -920,36 +1102,54 @@ 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);
> >> +		aux_space = bufsize_v4l2_buffer32(up);
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_buffer),
> >> +					    aux_space, &err);
> >> +		aux_buf = up_native + sizeof(struct v4l2_buffer);
> >> +		err = err ? : get_v4l2_buffer32(up_native, up,
> >> +						aux_buf, aux_space);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >>  	case VIDIOC_S_FBUF:
> >> -		err = get_v4l2_framebuffer32(&karg.v2fb, up);
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
> >> +					    0, &err);
> >> +		err = err ? : get_v4l2_framebuffer32(up_native, up);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >>  	case VIDIOC_G_FBUF:
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
> >> +					    0, &err);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >>  	case VIDIOC_ENUMSTD:
> >> -		err = get_v4l2_standard32(&karg.v2s, up);
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_standard),
> >> +					    0, &err);
> >> +		err = err ? : get_v4l2_standard32(up_native, up);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  
> >>  	case VIDIOC_ENUMINPUT:
> >> -		err = get_v4l2_input32(&karg.v2i, up);
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_input), 0, &err);
> >> +		err = 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(file, &karg.v2ecs, up);
> >> +		aux_space = bufsize_v4l2_ext_controls32(up);
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_ext_controls),
> >> +					    aux_space, &err);
> >> +		aux_buf = up_native + sizeof(struct v4l2_ext_controls);
> >> +		err = err ? : get_v4l2_ext_controls32(file, up_native, up,
> >> +						      aux_buf, aux_space);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  	case VIDIOC_DQEVENT:
> >> +		up_native = alloc_userspace(sizeof(struct v4l2_event), 0, &err);
> >>  		compatible_arg = 0;
> >>  		break;
> >>  	}
> >> @@ -958,13 +1158,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);
> >>  
> >>  	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
> >>  		return err;
> >> @@ -976,11 +1171,11 @@ 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(file, &karg.v2ecs, up))
> >> +		if (put_v4l2_ext_controls32(file, up_native, up))
> >>  			err = -EFAULT;
> >>  		break;
> >>  	case VIDIOC_S_EDID:
> >> -		if (put_v4l2_edid32(&karg.v2edid, up))
> >> +		if (put_v4l2_edid32(up_native, up))
> >>  			err = -EFAULT;
> >>  		break;
> >>  	}
> >> @@ -992,44 +1187,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((compat_uint_t __user *)up,
> >> +				      ((unsigned int __user *)up_native));
> > 
> > Do note that convert_in_user() returns Boolean. This will effectively yield
> > 1 in case of an error here. How about changing convert_in_user() to return
> > whatever get_user / put_user do, to avoid errors in the future?
> 
> That's better indeed. Good catch.
> 
> > 
> >>  		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_G_EDID:
> >> -		err = put_v4l2_edid32(&karg.v2edid, up);
> >> +		err = put_v4l2_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_PREPARE_BUF:
> >>  	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;
> > 
> 
> Thanks for the thorough review! Much appreciated.

You're welcome. And thanks for working on this!

-- 
Regards,

Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt
  2018-01-26 14:41   ` Sakari Ailus
  2018-01-29 10:09     ` Hans Verkuil
@ 2018-01-30  8:44     ` Hans Verkuil
  2018-01-30 11:14       ` Sakari Ailus
  1 sibling, 1 reply; 38+ messages in thread
From: Hans Verkuil @ 2018-01-30  8:44 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On 01/26/2018 03:41 PM, Sakari Ailus wrote:
> Hi Hans,
> 
> On Fri, Jan 26, 2018 at 01:43:17PM +0100, Hans Verkuil wrote:
>> From: Hans Verkuil <hans.verkuil@cisco.com>
>>
>> Don't duplicate the buffer type checks in enum/g/s/try_fmt.
>> The check_fmt function does that already.
>>
>> It is hard to keep the checks in sync for all these functions and
>> in fact the check for VBI was wrong in the _fmt functions as it
>> allowed SDR types as well. This caused a v4l2-compliance failure
>> for /dev/swradio0 using vivid.
>>
>> This simplifies the code and keeps the check in one place and
>> fixes the SDR/VBI bug.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-ioctl.c | 140 ++++++++++++++---------------------
>>  1 file changed, 54 insertions(+), 86 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
>> index 59d2100eeff6..c7f6b65d3ad7 100644
>> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
>> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
>> @@ -1316,52 +1316,50 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
>>  				struct file *file, void *fh, void *arg)
>>  {
>>  	struct v4l2_fmtdesc *p = arg;
>> -	struct video_device *vfd = video_devdata(file);
>> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
>> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
>> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
>> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
>> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
>> -	int ret = -EINVAL;
>> +	int ret = check_fmt(file, p->type);
> 
> I'd separate this from the variable declaration. The function is doing more
> than just fetch something to be used as a shorthand locally. I.e.
> 
> 	int ret;
> 
> 	ret = check_fmt(file, p->type);
> 
> Same elsewhere.

I'm not making this change. It's been like that since forever, and I don't
feel I should change this in this patch. I personally don't really care one
way or another, and especially in smaller functions like v4l_qbuf it
actually looks kind of weird to change it.

In any case, a change like that doesn't belong here.

Regards,

	Hans

> 
> The patch appears to be making an assumption that get_fmt will be
> universally supported on any buffer type, or that buffer type is not
> supported at all. I don't see a problem with the approach, but it'd be nice
> to document it, perhaps in struct v4l2_ioctl_ops KernelDoc documentation.
> 
> check_fmt() allows V4L2_BUF_TYPE_VBI_CAPTURE, V4L2_BUF_TYPE_VBI_OUTPUT,
> V4L2_BUF_TYPE_SLICED_VBI_CAPTURE and V4L2_BUF_TYPE_SLICED_VBI_OUTPUT that
> the original code did not for VIDIOC_ENUM_FMT. Is the change intentional?
> 
> Documentation should be updated regarding SDR and META formats (buffer
> type) but that's out of scope of the patchset:
> 
> <URL:https://hverkuil.home.xs4all.nl/spec/uapi/v4l/vidioc-enum-fmt.html>
> 
>> +
>> +	if (ret)
>> +		return ret;
>> +	ret = -EINVAL;
>>  
>>  	switch (p->type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_enum_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap_mplane))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_overlay))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_out))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_out(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out_mplane))
>> +		if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_enum_fmt_sdr_cap))
>> +		if (unlikely(!ops->vidioc_enum_fmt_sdr_cap))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_sdr_cap(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_enum_fmt_sdr_out))
>> +		if (unlikely(!ops->vidioc_enum_fmt_sdr_out))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_sdr_out(file, fh, arg);
>>  		break;
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_meta_cap))
>> +		if (unlikely(!ops->vidioc_enum_fmt_meta_cap))
>>  			break;
>>  		ret = ops->vidioc_enum_fmt_meta_cap(file, fh, arg);
>>  		break;
>> @@ -1375,13 +1373,10 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>  				struct file *file, void *fh, void *arg)
>>  {
>>  	struct v4l2_format *p = arg;
>> -	struct video_device *vfd = video_devdata(file);
>> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
>> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
>> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
>> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
>> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
>> -	int ret;
>> +	int ret = check_fmt(file, p->type);
>> +
>> +	if (ret)
>> +		return ret;
>>  
>>  	/*
>>  	 * fmt can't be cleared for these overlay types due to the 'clips'
>> @@ -1409,7 +1404,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>  
>>  	switch (p->type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_g_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_g_fmt_vid_cap))
>>  			break;
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
>> @@ -1417,23 +1412,15 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane))
>> -			break;
>>  		return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_overlay))
>> -			break;
>>  		return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_vbi_cap))
>> -			break;
>>  		return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_cap))
>> -			break;
>>  		return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_g_fmt_vid_out))
>>  			break;
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		ret = ops->vidioc_g_fmt_vid_out(file, fh, arg);
>> @@ -1441,32 +1428,18 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane))
>> -			break;
>>  		return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_overlay))
>> -			break;
>>  		return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_vbi_out))
>> -			break;
>>  		return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_g_fmt_sliced_vbi_out))
>> -			break;
>>  		return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_g_fmt_sdr_cap))
>> -			break;
>>  		return ops->vidioc_g_fmt_sdr_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_g_fmt_sdr_out))
>> -			break;
>>  		return ops->vidioc_g_fmt_sdr_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_meta_cap))
>> -			break;
>>  		return ops->vidioc_g_fmt_meta_cap(file, fh, arg);
>>  	}
>>  	return -EINVAL;
>> @@ -1492,12 +1465,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>  {
>>  	struct v4l2_format *p = arg;
>>  	struct video_device *vfd = video_devdata(file);
>> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
>> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
>> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
>> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
>> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
>> -	int ret;
>> +	int ret = check_fmt(file, p->type);
>> +
>> +	if (ret)
>> +		return ret;
>>  
>>  	ret = v4l_enable_media_source(vfd);
>>  	if (ret)
>> @@ -1506,37 +1477,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>  
>>  	switch (p->type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_s_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>>  		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
>>  		/* just in case the driver zeroed it again */
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>> -		if (is_tch)
>> +		if (vfd->vfl_type == VFL_TYPE_TOUCH)
>>  			v4l_pix_format_touch(&p->fmt.pix);
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>  		return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_overlay))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.win);
>>  		return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_vbi_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>>  		return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>>  		return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>>  		ret = ops->vidioc_s_fmt_vid_out(file, fh, arg);
>> @@ -1544,37 +1515,37 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>  		return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_overlay))
>> +		if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.win);
>>  		return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_vbi_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_vbi_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>>  		return ops->vidioc_s_fmt_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_s_fmt_sliced_vbi_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>>  		return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_s_fmt_sdr_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_sdr_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>>  		return ops->vidioc_s_fmt_sdr_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_s_fmt_sdr_out))
>> +		if (unlikely(!ops->vidioc_s_fmt_sdr_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>>  		return ops->vidioc_s_fmt_sdr_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_meta_cap))
>> +		if (unlikely(!ops->vidioc_s_fmt_meta_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.meta);
>>  		return ops->vidioc_s_fmt_meta_cap(file, fh, arg);
>> @@ -1586,19 +1557,16 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>  				struct file *file, void *fh, void *arg)
>>  {
>>  	struct v4l2_format *p = arg;
>> -	struct video_device *vfd = video_devdata(file);
>> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
>> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
>> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
>> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
>> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
>> -	int ret;
>> +	int ret = check_fmt(file, p->type);
>> +
>> +	if (ret)
>> +		return ret;
>>  
>>  	v4l_sanitize_format(p);
>>  
>>  	switch (p->type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>> -		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_try_fmt_vid_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>>  		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
>> @@ -1606,27 +1574,27 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>  		return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_overlay))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.win);
>>  		return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_vbi_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>>  		return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>> -		if (unlikely(!is_rx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>>  		return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix);
>>  		ret = ops->vidioc_try_fmt_vid_out(file, fh, arg);
>> @@ -1634,37 +1602,37 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
>>  		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
>>  		return ret;
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.pix_mp.xfer_func);
>>  		return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>> -		if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_overlay))
>> +		if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.win);
>>  		return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
>>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_vbi_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_vbi_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.vbi);
>>  		return ops->vidioc_try_fmt_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		if (unlikely(!is_tx || is_vid || !ops->vidioc_try_fmt_sliced_vbi_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sliced);
>>  		return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>> -		if (unlikely(!is_rx || !is_sdr || !ops->vidioc_try_fmt_sdr_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_sdr_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>>  		return ops->vidioc_try_fmt_sdr_cap(file, fh, arg);
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		if (unlikely(!is_tx || !is_sdr || !ops->vidioc_try_fmt_sdr_out))
>> +		if (unlikely(!ops->vidioc_try_fmt_sdr_out))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.sdr);
>>  		return ops->vidioc_try_fmt_sdr_out(file, fh, arg);
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_meta_cap))
>> +		if (unlikely(!ops->vidioc_try_fmt_meta_cap))
>>  			break;
>>  		CLEAR_AFTER_FIELD(p, fmt.meta);
>>  		return ops->vidioc_try_fmt_meta_cap(file, fh, arg);
>> -- 
>> 2.15.1
>>
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32
  2018-01-29 17:06   ` Sakari Ailus
  2018-01-29 17:41     ` Hans Verkuil
@ 2018-01-30  9:37     ` Hans Verkuil
  1 sibling, 0 replies; 38+ messages in thread
From: Hans Verkuil @ 2018-01-30  9:37 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: linux-media, Daniel Mentz, Hans Verkuil

Hi Sakari,

On 01/29/2018 06:06 PM, Sakari Ailus wrote:
> Hi Hans,
> 
> Thanks for your efforts on this patch and the patchset. Please see my comments below.
> 
> On Fri, Jan 26, 2018 at 01:43:27PM +0100, Hans Verkuil wrote:
>> From: Daniel Mentz <danielmentz@google.com>
>>
>> The 32-bit compat v4l2 ioctl is implemented based on its 64-bit
> 
> s/v4l2 ioctl/V4L2 IOCTL handling/
> 
> ?
> 
>> equivalent. It converts 32-bit data structures into its 64-bit
>> equivalents and needs to provide the data to the 64-bit ioctl in user
>> space memory which is commonly allocated using
>> compat_alloc_user_space(). However, due to how that function is
>> implemented, it can only be called a single time for every syscall
>> invocation.  Supposedly to avoid this limitation, the existing code uses
>> a mix of memory from the kernel stack and memory allocated through
>> compat_alloc_user_space(). Under normal circumstances, this would not
>> work, because the 64-bit ioctl expects all pointers to point to user
>> space memory. As a workaround, set_fs(KERNEL_DS) is called to
>> temporarily disable this extra safety check and allow kernel pointers.
>> However, this might introduce a security vulnerability: The
>> result of the 32-bit to 64-bit conversion is writeable by user space
>> because the output buffer has been allocated via
>> compat_alloc_user_space(). A malicious user space process could then
>> manipulate pointers inside this output buffer, and due to the previous
>> set_fs(KERNEL_DS) call, functions like get_user() or put_user() no longer
>> prevent kernel memory access.
>>
>> The new approach is to pre-calculate the total amount of user space
>> memory that is needed, allocate it using compat_alloc_user_space() and
>> then divide up the allocated memory to accommodate all data structures
>> that need to be converted.
>>
>> An alternative approach would have been to retain the union type karg
>> that they allocated on the kernel stack in do_video_ioctl(), copy all
>> data from user space into karg and then back to user space. However,
>> we decided against this approach because it does not align with other
>> compat syscall implementations. Instead, we tried to replicate the
>> get_user/put_user pairs as found in other places in the kernel:
>>
>> if (get_user(clipcount, &up->clipcount) ||
>>     put_user(clipcount, &kp->clipcount)) return -EFAULT;
>>
>> Notes from hans.verkuil@cisco.com:
>>
>> This patch was taken from
>> https://github.com/LineageOS/android_kernel_samsung_apq8084/commit/97b733953c06e4f0398ade18850f0817778255f7
>>
>> Clearly nobody could be bothered to upstream this patch or at minimum
>> tell us :-( We only heard about this a week ago.
> 
> This is very sad indeed. I would really hope that we'll never run into
> something this again!
> 
>>
>> This patch was rebased and cleaned up. Compared to the original I
>> also swapped the order of the convert_in_user arguments so that they
>> matched copy_in_user. It was hard to review otherwise. I also replaced
>> the ALLOC_USER_SPACE/ALLOC_AND_GET by a normal function.
> 
> The result looks more complicated than it should be.
> 
> I wonder if the result could be made cleaner by separating the argument
> checking and copying from / to user space in video_usercopy. That
> separation almost completely exists already. Rather than mangling the
> compat argument struct to look like a user-provided 64-bit argument struct,
> copying the arguments from and to the user space would be separately done
> to 32-bit compat IOCTL arguments.
> 
> I suppose that wouldn't be a trivial change either, so for now I'd maintain
> the approach and later consider how to make this more maintainable in the
> future. Still, that approach is quite different and it'd be easier to
> switch from status quo rather than this last patch, suggesting to start
> with a revert, should we embark into this direction.
> 
> What do you think?
> 
>>
>> Signed-off-by: Daniel Mentz <danielmentz@google.com>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 700 ++++++++++++++++----------
>>  1 file changed, 448 insertions(+), 252 deletions(-)
>>
>> diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> index 2aa9b43daf60..27a5a0961cbd 100644
>> --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c
>> @@ -22,6 +22,14 @@
>>  #include <media/v4l2-ctrls.h>
>>  #include <media/v4l2-ioctl.h>
>>  
>> +/* Use the same argument order as copy_in_user */
>> +#define convert_in_user(to, from)			\
> 
> This doesn't really convert anything. How about calling it assign_in_user,
> for example?

Done.

> 
>> +({							\
>> +	typeof(*from) val;				\
> 
> "val" is very short, easily leading to hard-to-find namespace collisions.
> At least the versions of GCC I've used happily get this wrong without a
> warning.
> 
> How about __convert_in_user_tmp instead?

I stuck to __assign_tmp, I think __convert_in_user_tmp is overly paranoid :-)

> 
>> +							\
>> +	get_user(val, from) || put_user(val, to);	\

I'm keeping this. I thought about changing || to '? :' but I chickened out making
that change. I'll fix the G/S_INPUT/OUTPUT handling instead.

>> +})
>> +
>>  static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>>  {
>>  	long ret = -ENOIOCTLCMD;
>> @@ -48,37 +56,41 @@ struct v4l2_window32 {
>>  	__u8                    global_alpha;
>>  };
>>  
>> -static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up)
>> +static int get_v4l2_window32(struct v4l2_window __user *kp,
>> +			     struct v4l2_window32 __user *up,
>> +			     void __user *aux_buf, int aux_space)
>>  {
>>  	struct v4l2_clip32 __user *uclips;
>>  	struct v4l2_clip __user *kclips;
>>  	compat_caddr_t p;
>> -	u32 n;
>> +	u32 clipcount;
>>  
>>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> -	    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) ||
>> -	    get_user(kp->global_alpha, &up->global_alpha))
>> +	    copy_in_user(&kp->w, &up->w, sizeof(up->w)) ||
>> +	    convert_in_user(&kp->field, &up->field) ||
>> +	    convert_in_user(&kp->chromakey, &up->chromakey) ||
>> +	    convert_in_user(&kp->global_alpha, &up->global_alpha) ||
>> +	    get_user(clipcount, &up->clipcount) ||
>> +	    put_user(clipcount, &kp->clipcount))
>>  		return -EFAULT;
>> -	if (kp->clipcount > 2048)
>> +	if (clipcount > 2048)
>>  		return -EINVAL;
>> -	if (!kp->clipcount) {
>> -		kp->clips = NULL;
>> -		return 0;
>> -	}
>> +	if (!clipcount)
>> +		return put_user(NULL, &kp->clips) ? -EFAULT : 0;
> 
> Isn't this what put_user would return anyway?

Changed.

> 
>>  
>> -	n = kp->clipcount;
>>  	if (get_user(p, &up->clips))
>>  		return -EFAULT;
>>  	uclips = compat_ptr(p);
>> -	kclips = compat_alloc_user_space(n * sizeof(*kclips));
>> -	kp->clips = kclips;
>> -	while (n--) {
>> +	if (aux_space < clipcount * sizeof(*kclips))
>> +		return -EFAULT;
>> +	kclips = aux_buf;
>> +	if (put_user(kclips, &kp->clips))
>> +		return -EFAULT;
>> +
>> +	while (clipcount--) {
>>  		if (copy_in_user(&kclips->c, &uclips->c, sizeof(uclips->c)))
>>  			return -EFAULT;
>> -		if (put_user(n ? kclips + 1 : NULL, &kclips->next))
>> +		if (put_user(clipcount ? kclips + 1 : NULL, &kclips->next))
>>  			return -EFAULT;
>>  		uclips++;
>>  		kclips++;
>> @@ -86,26 +98,28 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user
>>  	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)
>>  {
>>  	struct v4l2_clip __user *kclips = kp->clips;
>>  	struct v4l2_clip32 __user *uclips;
>> -	int n = kp->clipcount;
>>  	compat_caddr_t p;
>> -
>> -	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) ||
>> -	    put_user(kp->global_alpha, &up->global_alpha))
>> +	u32 clipcount;
>> +
>> +	if (copy_in_user(&up->w, &kp->w, sizeof(kp->w)) ||
>> +	    convert_in_user(&up->field, &kp->field) ||
>> +	    convert_in_user(&up->chromakey, &kp->chromakey) ||
>> +	    convert_in_user(&up->global_alpha, &kp->global_alpha) ||
>> +	    get_user(clipcount, &kp->clipcount) ||
>> +	    put_user(clipcount, &up->clipcount))
>>  		return -EFAULT;
>> -	if (!kp->clipcount)
>> +	if (!clipcount)
>>  		return 0;
>>  
>>  	if (get_user(p, &up->clips))
>>  		return -EFAULT;
>>  	uclips = compat_ptr(p);
>> -	while (n--) {
>> +	while (clipcount--) {
>>  		if (copy_in_user(&uclips->c, &kclips->c, sizeof(uclips->c)))
>>  			return -EFAULT;
>>  		uclips++;
>> @@ -145,107 +159,161 @@ 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)
> 
> This is effectively returning the size of the non-compat struct, or just
> the part of which is variable. How about calling it __bufsize_v4l2_format
> instead? The same for other such functions.

Done.

> 
> I'd also separate the size calculated by the function and the return value.

Done. Much nicer indeed.

> 
>>  {
>> -	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: {
>> +		u32 clipcount;
>> +
>> +		if (get_user(clipcount, &up->fmt.win.clipcount))
>> +			return -EFAULT;
>> +		if (clipcount > 2048)
>> +			return -EINVAL;
>> +		return clipcount * sizeof(struct v4l2_clip);
>> +	}
>> +	default:
>> +		return 0;
>> +	}
>> +}
>> +
>> +static int bufsize_v4l2_format32(struct v4l2_format32 __user *up)
>> +{
>> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)))
>> +		return -EFAULT;
> 
> Newline?

Didn't do this. I didn't think it improved the code.

> 
>> +	return __bufsize_v4l2_format32(up);
>> +}
>> +
>> +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 copy_from_user(&kp->fmt.pix, &up->fmt.pix,
>> -				      sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.pix, &up->fmt.pix,
>> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		return copy_from_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
>> -				      sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.pix_mp, &up->fmt.pix_mp,
>> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>>  	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 copy_from_user(&kp->fmt.vbi, &up->fmt.vbi,
>> -				      sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.vbi, &up->fmt.vbi,
>> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		return copy_from_user(&kp->fmt.sliced, &up->fmt.sliced,
>> -				      sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.sliced, &up->fmt.sliced,
>> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		return copy_from_user(&kp->fmt.sdr, &up->fmt.sdr,
>> -				      sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.sdr, &up->fmt.sdr,
>> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		return copy_from_user(&kp->fmt.meta, &up->fmt.meta,
>> -				      sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>> +		return copy_in_user(&kp->fmt.meta, &up->fmt.meta,
>> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>>  	default:
>>  		return -EINVAL;
>>  	}
>>  }
>>  
>> -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __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(*up)))
>> +		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(*up)))
>>  		return -EFAULT;
>> -	return __get_v4l2_format32(kp, up);
>> +	return __bufsize_v4l2_format32(&up->format);
>>  }
>>  
>> -static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up)
>> +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(*up)) ||
>> -	    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)
>>  {
>> -	if (put_user(kp->type, &up->type))
>> +	u32 type;
>> +
>> +	if (get_user(type, &kp->type))
>>  		return -EFAULT;
>>  
>> -	switch (kp->type) {
>> +	switch (type) {
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT:
>> -		return copy_to_user(&up->fmt.pix, &kp->fmt.pix,
>> -				    sizeof(kp->fmt.pix)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.pix, &kp->fmt.pix,
>> +				    sizeof(kp->fmt.pix)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
>> -		return copy_to_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
>> -				    sizeof(kp->fmt.pix_mp)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.pix_mp, &kp->fmt.pix_mp,
>> +				    sizeof(kp->fmt.pix_mp)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
>>  	case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
>>  		return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
>>  	case V4L2_BUF_TYPE_VBI_CAPTURE:
>>  	case V4L2_BUF_TYPE_VBI_OUTPUT:
>> -		return copy_to_user(&up->fmt.vbi, &kp->fmt.vbi,
>> -				    sizeof(kp->fmt.vbi)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.vbi, &kp->fmt.vbi,
>> +				    sizeof(kp->fmt.vbi)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
>>  	case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
>> -		return copy_to_user(&up->fmt.sliced, &kp->fmt.sliced,
>> -				    sizeof(kp->fmt.sliced)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.sliced, &kp->fmt.sliced,
>> +				    sizeof(kp->fmt.sliced)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_SDR_CAPTURE:
>>  	case V4L2_BUF_TYPE_SDR_OUTPUT:
>> -		return copy_to_user(&up->fmt.sdr, &kp->fmt.sdr,
>> -				    sizeof(kp->fmt.sdr)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.sdr, &kp->fmt.sdr,
>> +				    sizeof(kp->fmt.sdr)) ? -EFAULT : 0;
>>  	case V4L2_BUF_TYPE_META_CAPTURE:
>> -		return copy_to_user(&up->fmt.meta, &kp->fmt.meta,
>> -				    sizeof(kp->fmt.meta)) ?  -EFAULT : 0;
>> +		return copy_in_user(&up->fmt.meta, &kp->fmt.meta,
>> +				    sizeof(kp->fmt.meta)) ? -EFAULT : 0;
>>  	default:
>>  		return -EINVAL;
>>  	}
>>  }
>>  
>> -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(*up)))
> 
> For some pointers we do perform such checks, for others we don't. While
> this patch doesn't change this, it'd be good to align this by either
> introducing it everywhere or removing it everywhere.
> 
> I guess that can be done separately.

Didn't touch this. It wouldn't hurt to review the code. I'll see if I can do
that later this week.

> 
>>  		return -EFAULT;
>>  	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(*up)) ||
>> -	    copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format)) ||
>> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>> +	    copy_in_user(up, kp,
>> +			 offsetof(struct v4l2_create_buffers32, format)) ||
>> +	    copy_in_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>>  		return -EFAULT;
>>  	return __put_v4l2_format32(&kp->format, &up->format);
>>  }
>> @@ -259,25 +327,27 @@ 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(*up)) ||
>> -	    get_user(kp->index, &up->index))
>> +	    convert_in_user(&kp->index, &up->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(*up)) ||
>> -	    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, sizeof(kp->reserved)))
>> +	    convert_in_user(&up->index, &kp->index) ||
>> +	    copy_in_user(&up->id, &kp->id, sizeof(up->id)) ||
> 
> Why not convert_in_user()?

Changed.

> 
>> +	    copy_in_user(up->name, kp->name, 24) ||
>> +	    copy_in_user(&up->frameperiod, &kp->frameperiod,
>> +			 sizeof(up->frameperiod)) ||
>> +	    convert_in_user(&up->framelines, &kp->framelines) ||
>> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
>>  		return -EFAULT;
>>  	return 0;
>>  }
>> @@ -317,10 +387,10 @@ struct v4l2_buffer32 {
>>  	__u32			reserved;
>>  };
>>  
>> -static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
>> +static int get_v4l2_plane32(struct v4l2_plane __user *up,
>> +			    struct v4l2_plane32 __user *up32,
>>  			    enum v4l2_memory memory)
>>  {
>> -	void __user *up_pln;
>>  	compat_long_t p;
> 
> Shouldn't this be compat_ulong_t?

Changed.

> 
>>  
>>  	if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
>> @@ -336,10 +406,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>>  			return -EFAULT;
>>  		break;
>>  	case 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;
>>  		break;
>>  	case V4L2_MEMORY_DMABUF:
>> @@ -351,7 +419,8 @@ static int get_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>>  	return 0;
>>  }
>>  
>> -static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __user *up32,
>> +static int put_v4l2_plane32(struct v4l2_plane __user *up,
>> +			    struct v4l2_plane32 __user *up32,
>>  			    enum v4l2_memory memory)
>>  {
>>  	unsigned long p;
>> @@ -375,8 +444,7 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>>  			return -EFAULT;
>>  		break;
>>  	case V4L2_MEMORY_DMABUF:
>> -		if (copy_in_user(&up32->m.fd, &up->m.fd,
>> -				 sizeof(up->m.fd)))
>> +		if (copy_in_user(&up32->m.fd, &up->m.fd, sizeof(up->m.fd)))
>>  			return -EFAULT;
>>  		break;
>>  	}
>> @@ -384,37 +452,68 @@ static int put_v4l2_plane32(struct v4l2_plane __user *up, struct v4l2_plane32 __
>>  	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(*up)) ||
>> +	    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
> 
> /*
>  * ...

Changed here and elsewhere.

> 
>> +		 * 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;
>> +	int num_planes;
> 
> u32?

Changed.

> 
>>  	int ret;
>>  
>>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> -	    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(&kp->index, &up->index) ||
>> +	    get_user(type, &up->type) ||
>> +	    put_user(type, &kp->type) ||
>> +	    convert_in_user(&kp->flags, &up->flags) ||
>> +	    get_user(memory, &up->memory) ||
>> +	    put_user(memory, &kp->memory) ||
>> +	    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(&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))
>>  			return -EFAULT;
>>  
>> -	if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
>> -		unsigned int num_planes;
>> -
>> -		if (kp->length == 0) {
>> -			kp->m.planes = NULL;
>> +	if (V4L2_TYPE_IS_MULTIPLANAR(type)) {
>> +		num_planes = length;
> 
> Do you need a local variable for just that?

I moved the u32 num_planes declaration above to here. I still kept it
though since I think it helps understand what 'length' stands for.

> 
>> +		if (num_planes == 0) {
>>  			/* num_planes == 0 is legal, e.g. when userspace doesn't
>>  			 * need planes array on DQBUF*/
>> -			return 0;
>> -		} else if (kp->length > VIDEO_MAX_PLANES) {
>> +			return put_user(NULL, &kp->m.planes);
>> +		} else if (num_planes > VIDEO_MAX_PLANES) {
>>  			return -EINVAL;
>>  		}
>>  
>> @@ -423,40 +522,47 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>>  
>>  		uplane32 = compat_ptr(p);
>>  		if (!access_ok(VERIFY_READ, uplane32,
>> -			       kp->length * sizeof(*uplane32)))
>> +				num_planes * sizeof(*uplane32)))
> 
> Alignment.

Changed.

> 
>>  			return -EFAULT;
>>  
>>  		/* We don't really care if userspace decides to kill itself
>>  		 * by passing a very big num_planes value */
>> -		uplane = compat_alloc_user_space(kp->length * sizeof(*uplane));
>> -		kp->m.planes = (__force struct v4l2_plane *)uplane;
>> +		if (aux_space < num_planes * sizeof(*uplane))
>> +			return -EFAULT;
>> +
>> +		uplane = aux_buf;
>> +		if (put_user((__force struct v4l2_plane *)uplane,
>> +					&kp->m.planes))
>> +			return -EFAULT;
>>  
>> -		for (num_planes = 0; num_planes < kp->length; num_planes++) {
>> -			ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
>> +		while (--num_planes >= 0) {
> 
> while (num_planes--) {
> 
> ?

Changed.

> 
>> +			ret = get_v4l2_plane32(uplane, uplane32, memory);
>>  			if (ret)
>>  				return ret;
>> -			++uplane;
>> -			++uplane32;
>> +			uplane++;
>> +			uplane32++;
>>  		}
>>  	} else {
>> -		switch (kp->memory) {
>> +		switch (memory) {
>>  		case V4L2_MEMORY_MMAP:
>> -		case V4L2_MEMORY_OVERLAY:
>> -			if (get_user(kp->m.offset, &up->m.offset))
>> +			if (convert_in_user(&kp->m.offset, &up->m.offset))
>>  				return -EFAULT;
>>  			break;
>> -		case V4L2_MEMORY_USERPTR:
>> -			{
>> -				compat_long_t tmp;
>> -
>> -				if (get_user(tmp, &up->m.userptr))
>> -					return -EFAULT;
>> +		case V4L2_MEMORY_USERPTR: {
>> +			compat_long_t tmp;
> 
> compat_ulong_t, I'd call it "userptr". The latter is entirely up to you.

Changed.

> 
>>  
>> -				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 (convert_in_user(&kp->m.offset, &up->m.offset))
>> +				return -EFAULT;
> 
> Is there a reason to split this off from MMAP handling? The code is the
> same.

Changed.

> 
>>  			break;
>>  		case V4L2_MEMORY_DMABUF:
>> -			if (get_user(kp->m.fd, &up->m.fd))
>> +			if (convert_in_user(&kp->m.fd, &up->m.fd))
>>  				return -EFAULT;
>>  			break;
>>  		}
>> @@ -465,8 +571,12 @@ 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;
>> @@ -474,53 +584,60 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
>>  	int ret;
>>  
>>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>> -	    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(&up->index, &kp->index) ||
>> +	    get_user(type, &kp->type) ||
>> +	    put_user(type, &up->type) ||
>> +	    convert_in_user(&up->flags, &kp->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(kp->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(&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) ||
>> +	    copy_in_user(&up->timecode, &kp->timecode, sizeof(kp->timecode)) ||
>> +	    convert_in_user(&up->sequence, &kp->sequence) ||
>> +	    convert_in_user(&up->reserved2, &kp->reserved2) ||
>> +	    convert_in_user(&up->reserved, &kp->reserved) ||
>> +	    get_user(length, &kp->length) ||
>> +	    put_user(length, &up->length))
>>  		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 = (__force struct v4l2_plane __user *)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) {
> 
> 
> while (num_planes--) {

Changed.

> 
> Or rather: s/num_planes/length/
> 
> Then you can remove num_planes.

But kept num_planes.

> 
>> -			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:
>> -		case V4L2_MEMORY_OVERLAY:
>> -			if (put_user(kp->m.offset, &up->m.offset))
>> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
>>  				return -EFAULT;
>>  			break;
>>  		case V4L2_MEMORY_USERPTR:
>> -			if (put_user(kp->m.userptr, &up->m.userptr))
>> +			if (convert_in_user(&up->m.userptr, &kp->m.userptr))
>> +				return -EFAULT;
>> +			break;
>> +		case V4L2_MEMORY_OVERLAY:
>> +			if (convert_in_user(&up->m.offset, &kp->m.offset))
> 
> Combine with MMAP case?

Changed.

> 
>>  				return -EFAULT;
>>  			break;
>>  		case V4L2_MEMORY_DMABUF:
>> -			if (put_user(kp->m.fd, &up->m.fd))
>> +			if (convert_in_user(&up->m.fd, &kp->m.fd))
>>  				return -EFAULT;
>>  			break;
>>  		}
>> @@ -545,29 +662,32 @@ struct v4l2_framebuffer32 {
>>  	} 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(*up)) ||
>>  	    get_user(tmp, &up->base) ||
>> -	    get_user(kp->capability, &up->capability) ||
>> -	    get_user(kp->flags, &up->flags) ||
>> -	    copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt)))
>> +	    put_user((__force void *)compat_ptr(tmp), &kp->base) ||
>> +	    convert_in_user(&kp->capability, &up->capability) ||
>> +	    convert_in_user(&kp->flags, &up->flags) ||
>> +	    copy_in_user(&kp->fmt, &up->fmt, sizeof(kp->fmt)))
>>  		return -EFAULT;
>> -	kp->base = (__force void *)compat_ptr(tmp);
>>  	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(*up)) ||
>> -	    put_user(tmp, &up->base) ||
>> -	    put_user(kp->capability, &up->capability) ||
>> -	    put_user(kp->flags, &up->flags) ||
>> -	    copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt)))
>> +	    get_user(base, &kp->base) ||
>> +	    put_user(ptr_to_compat(base), &up->base) ||
>> +	    convert_in_user(&up->capability, &kp->capability) ||
>> +	    convert_in_user(&up->flags, &kp->flags) ||
>> +	    copy_in_user(&up->fmt, &kp->fmt, sizeof(kp->fmt)))
>>  		return -EFAULT;
>>  	return 0;
>>  }
>> @@ -586,16 +706,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 *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(*up)))
>> +	if (copy_in_user(kp, up, sizeof(*up)))
>>  		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(*up)))
>> +	if (copy_in_user(up, kp, sizeof(*up)))
>>  		return -EFAULT;
>>  	return 0;
>>  }
>> @@ -648,39 +770,58 @@ static inline bool ctrl_is_pointer(struct file *file, u32 id)
>>  	return !ret && (qec.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD);
>>  }
>>  
>> +static int bufsize_v4l2_ext_controls32(struct v4l2_ext_controls32 __user *up)
>> +{
>> +	u32 count;
>> +
>> +	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> +	    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 file *file,
>> -				   struct v4l2_ext_controls *kp,
>> -				   struct v4l2_ext_controls32 __user *up)
>> +				   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;
>> +	u32 count;
>>  	unsigned int n;
> 
> u32 n

Changed.

> 
>>  	compat_caddr_t p;
>>  
>>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> -	    get_user(kp->which, &up->which) ||
>> -	    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(&kp->which, &up->which) ||
>> +	    get_user(count, &up->count) ||
>> +	    put_user(count, &kp->count) ||
>> +	    convert_in_user(&kp->error_idx, &up->error_idx) ||
>> +	    copy_in_user(kp->reserved, up->reserved, sizeof(kp->reserved)))
>>  		return -EFAULT;
>> -	if (kp->count == 0) {
>> -		kp->controls = NULL;
>> -		return 0;
>> -	} else if (kp->count > V4L2_CID_MAX_CTRLS) {
>> +	if (count == 0)
>> +		return put_user(NULL, &kp->controls);
>> +	else if (kp->count > V4L2_CID_MAX_CTRLS)
> 
> count

Changed.

> 
>>  		return -EINVAL;
>> -	}
>>  	if (get_user(p, &up->controls))
>>  		return -EFAULT;
>>  	ucontrols = compat_ptr(p);
>> -	if (!access_ok(VERIFY_READ, ucontrols, kp->count * sizeof(*ucontrols)))
>> +	if (!access_ok(VERIFY_READ, ucontrols, count * sizeof(*ucontrols)))
>> +		return -EFAULT;
>> +	if (aux_space < count * sizeof(*kcontrols))
>>  		return -EFAULT;
>> -	kcontrols = compat_alloc_user_space(kp->count * sizeof(*kcontrols));
>> -	kp->controls = (__force struct v4l2_ext_control *)kcontrols;
>> -	for (n = 0; n < kp->count; n++) {
>> +	kcontrols = aux_buf;
>> +	if (put_user((__force struct v4l2_ext_control *)kcontrols,
>> +		     &kp->controls))
>> +		return -EFAULT;
>> +
>> +	for (n = 0; n < count; n++) {
>>  		u32 id;
>>  
>>  		if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
>>  			return -EFAULT;
>> +
>>  		if (get_user(id, &kcontrols->id))
>>  			return -EFAULT;
> 
> Newline here, too? Not really a problem with this patch though.

Changed.

> 
>>  		if (ctrl_is_pointer(file, id)) {
>> @@ -699,36 +840,47 @@ static int get_v4l2_ext_controls32(struct file *file,
>>  }
>>  
>>  static int put_v4l2_ext_controls32(struct file *file,
>> -				   struct v4l2_ext_controls *kp,
>> +				   struct v4l2_ext_controls __user *kp,
>>  				   struct v4l2_ext_controls32 __user *up)
>>  {
>>  	struct v4l2_ext_control32 __user *ucontrols;
>> -	struct v4l2_ext_control __user *kcontrols =
>> -		(__force struct v4l2_ext_control __user *)kp->controls;
>> -	int n = kp->count;
>> +	struct v4l2_ext_control __user *kcontrols;
>> +	u32 count;
>> +	unsigned int n;
> 
> u32 n

Changed.

> 
>>  	compat_caddr_t p;
>>  
>>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>> -	    put_user(kp->which, &up->which) ||
>> -	    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) ||
> 
> How about doing this as last? "controls" is the last member of the struct.

Changed.

> 
>> +	    convert_in_user(&up->which, &kp->which) ||
>> +	    get_user(count, &kp->count) ||
>> +	    put_user(count, &up->count) ||
>> +	    convert_in_user(&up->error_idx, &kp->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(*ucontrols)))
>> +	if (!access_ok(VERIFY_WRITE, ucontrols, count * sizeof(*ucontrols)))
>>  		return -EFAULT;
>>  
>> -	while (--n >= 0) {
>> -		unsigned size = sizeof(*ucontrols);
>> +	for (n = 0; n < count; n++) {
>> +		unsigned int size = sizeof(*ucontrols);
>>  		u32 id;
>>  
>> +		if (copy_in_user(&ucontrols->id, &kcontrols->id,
>> +				 sizeof(ucontrols->id)) ||
> 
> get_user(id, &kcontrols->id) ||
> put_user(id, &ucontrols->id) ||

Changed.

> 
>> +		    copy_in_user(&ucontrols->size, &kcontrols->size,
>> +				 sizeof(ucontrols->size)) ||
> 
> convert_in_user() ?

Changed.

> 
>> +		    copy_in_user(&ucontrols->reserved2, &kcontrols->reserved2,
>> +				 sizeof(ucontrols->reserved2)))
>> +			return -EFAULT;
>> +
>>  		if (get_user(id, &kcontrols->id))
>>  			return -EFAULT;
> 
> ...and the above if () can be removed.

Done.

> 
>> +
>>  		/* Do not modify the pointer when copying a pointer control.
>>  		   The contents of the pointer was changed, not the pointer
>>  		   itself. */
>> @@ -736,6 +888,7 @@ static int put_v4l2_ext_controls32(struct file *file,
>>  			size -= sizeof(ucontrols->value64);
> 
> This chunk is yearning for newlines. Not a fault of this patch though.
> 
>>  		if (copy_in_user(ucontrols, kcontrols, size))
>>  			return -EFAULT;
>> +
>>  		ucontrols++;
>>  		kcontrols++;
>>  	}
>> @@ -755,17 +908,18 @@ 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(*up)) ||
>> -	    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_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
>> -	    put_user(kp->timestamp.tv_nsec, &up->timestamp.tv_nsec) ||
>> -	    put_user(kp->id, &up->id) ||
>> -	    copy_to_user(up->reserved, kp->reserved, sizeof(kp->reserved)))
>> +	    convert_in_user(&up->type, &kp->type) ||
>> +	    copy_in_user(&up->u, &kp->u, sizeof(kp->u)) ||
>> +	    convert_in_user(&up->pending, &kp->pending) ||
>> +	    convert_in_user(&up->sequence, &kp->sequence) ||
>> +	    convert_in_user(&up->timestamp.tv_sec, &kp->timestamp.tv_sec) ||
>> +	    convert_in_user(&up->timestamp.tv_nsec, &kp->timestamp.tv_nsec) ||
>> +	    convert_in_user(&up->id, &kp->id) ||
>> +	    copy_in_user(up->reserved, kp->reserved, sizeof(up->reserved)))
>>  		return -EFAULT;
>>  	return 0;
>>  }
>> @@ -778,31 +932,34 @@ struct v4l2_edid32 {
>>  	compat_caddr_t edid;
>>  };
>>  
>> -static int get_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>> +static int get_v4l2_edid32(struct v4l2_edid __user *kp,
>> +			   struct v4l2_edid32 __user *up)
>>  {
>> -	u32 tmp;
>> +	compat_uptr_t tmp;
>>  
>>  	if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
>> -	    get_user(kp->pad, &up->pad) ||
>> -	    get_user(kp->start_block, &up->start_block) ||
>> -	    get_user(kp->blocks, &up->blocks) ||
>> +	    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(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 = (__force u8 *)compat_ptr(tmp);
>>  	return 0;
>>  }
>>  
>> -static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>> +static int put_v4l2_edid32(struct v4l2_edid __user *kp,
>> +			   struct v4l2_edid32 __user *up)
>>  {
>> -	u32 tmp = (u32)((unsigned long)kp->edid);
>> +	void *edid;
>>  
>>  	if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
>> -	    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(up->reserved, kp->reserved, sizeof(up->reserved)))
>> +	    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(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;
>>  }
>> @@ -835,22 +992,30 @@ static int put_v4l2_edid32(struct v4l2_edid *kp, struct v4l2_edid32 __user *up)
>>  #define VIDIOC_G_OUTPUT32	_IOR ('V', 46, s32)
>>  #define VIDIOC_S_OUTPUT32	_IOWR('V', 47, s32)
>>  
>> +static void __user *alloc_userspace(unsigned int size, int aux_space, long *err)
> 
> This one is weird. Rather than following the pattern and returning an error
> and assigning the result to a pointer passed to it, it does exactly the
> opposite. I'd change that. I guess this is a matter of opinion to some
> degree at least, though.

Changed. It much nicer now.

> 
>> +{
>> +	void __user *up_native;
>> +
>> +	if (aux_space < 0) {
>> +		*err = aux_space;
>> +		return NULL;
>> +	}
>> +	up_native = compat_alloc_user_space(size + aux_space);
>> +	if (!up_native)
>> +		*err = -ENOMEM;
>> +	else if (clear_user(up_native, size))
>> +		*err = -EFAULT;
>> +	else
>> +		return up_native;
>> +	return NULL;
>> +}
>> +
>>  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_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;
> 
> size_t?

I went with u32 :-)

> 
>>  	int compatible_arg = 1;
>>  	long err = 0;
>>  
>> @@ -889,30 +1054,47 @@ 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_userspace(sizeof(unsigned int __user),
>> +					    0, &err);
>> +		if (!err && convert_in_user((unsigned int __user *)up_native,
>> +					    (compat_uint_t __user *)up))
>> +			return -EFAULT;
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_G_INPUT:
>>  	case VIDIOC_G_OUTPUT:
>> +		up_native = alloc_userspace(sizeof(unsigned int __user),
>> +					    0, &err);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_G_EDID:
>>  	case VIDIOC_S_EDID:
>> -		err = get_v4l2_edid32(&karg.v2edid, up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_edid), 0, &err);
>> +		err = err ? : get_v4l2_edid32(up_native, up);
> 
> I'd write this as:
> 
> 		if (!err)
> 			err = get_v4l2_edid32(native_up, up);
> 
> Same below.

Done.

> 
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_G_FMT:
>>  	case VIDIOC_S_FMT:
>>  	case VIDIOC_TRY_FMT:
>> -		err = get_v4l2_format32(&karg.v2f, up);
>> +		aux_space = bufsize_v4l2_format32(up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_format),
>> +					    aux_space, &err);
>> +		aux_buf = up_native + sizeof(struct v4l2_format);
>> +		err = err ? : get_v4l2_format32(up_native, up,
>> +						aux_buf, aux_space);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_CREATE_BUFS:
>> -		err = get_v4l2_create32(&karg.v2crt, up);
>> +		aux_space = bufsize_v4l2_create32(up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_create_buffers),
>> +					    aux_space, &err);
>> +		aux_buf = up_native + sizeof(struct v4l2_create_buffers);
>> +		err = err ? : get_v4l2_create32(up_native, up,
>> +						aux_buf, aux_space);
>>  		compatible_arg = 0;
>>  		break;
>>  
>> @@ -920,36 +1102,54 @@ 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);
>> +		aux_space = bufsize_v4l2_buffer32(up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_buffer),
>> +					    aux_space, &err);
>> +		aux_buf = up_native + sizeof(struct v4l2_buffer);
>> +		err = err ? : get_v4l2_buffer32(up_native, up,
>> +						aux_buf, aux_space);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_S_FBUF:
>> -		err = get_v4l2_framebuffer32(&karg.v2fb, up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
>> +					    0, &err);
>> +		err = err ? : get_v4l2_framebuffer32(up_native, up);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_G_FBUF:
>> +		up_native = alloc_userspace(sizeof(struct v4l2_framebuffer),
>> +					    0, &err);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_ENUMSTD:
>> -		err = get_v4l2_standard32(&karg.v2s, up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_standard),
>> +					    0, &err);
>> +		err = err ? : get_v4l2_standard32(up_native, up);
>>  		compatible_arg = 0;
>>  		break;
>>  
>>  	case VIDIOC_ENUMINPUT:
>> -		err = get_v4l2_input32(&karg.v2i, up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_input), 0, &err);
>> +		err = 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(file, &karg.v2ecs, up);
>> +		aux_space = bufsize_v4l2_ext_controls32(up);
>> +		up_native = alloc_userspace(sizeof(struct v4l2_ext_controls),
>> +					    aux_space, &err);
>> +		aux_buf = up_native + sizeof(struct v4l2_ext_controls);
>> +		err = err ? : get_v4l2_ext_controls32(file, up_native, up,
>> +						      aux_buf, aux_space);
>>  		compatible_arg = 0;
>>  		break;
>>  	case VIDIOC_DQEVENT:
>> +		up_native = alloc_userspace(sizeof(struct v4l2_event), 0, &err);
>>  		compatible_arg = 0;
>>  		break;
>>  	}
>> @@ -958,13 +1158,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);
>>  
>>  	if (err == -ENOTTY || err == -EFAULT || err == -ENOIOCTLCMD)
>>  		return err;
>> @@ -976,11 +1171,11 @@ 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(file, &karg.v2ecs, up))
>> +		if (put_v4l2_ext_controls32(file, up_native, up))
>>  			err = -EFAULT;
>>  		break;
>>  	case VIDIOC_S_EDID:
>> -		if (put_v4l2_edid32(&karg.v2edid, up))
>> +		if (put_v4l2_edid32(up_native, up))
>>  			err = -EFAULT;
>>  		break;
>>  	}
>> @@ -992,44 +1187,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((compat_uint_t __user *)up,
>> +				      ((unsigned int __user *)up_native));
> 
> Do note that convert_in_user() returns Boolean. This will effectively yield
> 1 in case of an error here. How about changing convert_in_user() to return
> whatever get_user / put_user do, to avoid errors in the future?

As mentioned at the top I decided not to change convert_in_user (now assign_in_user)
and modified this code here instead.

> 
>>  		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_G_EDID:
>> -		err = put_v4l2_edid32(&karg.v2edid, up);
>> +		err = put_v4l2_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_PREPARE_BUF:
>>  	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;
> 

Regards,

	Hans

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt
  2018-01-30  8:44     ` Hans Verkuil
@ 2018-01-30 11:14       ` Sakari Ailus
  0 siblings, 0 replies; 38+ messages in thread
From: Sakari Ailus @ 2018-01-30 11:14 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Daniel Mentz, Hans Verkuil

On Tue, Jan 30, 2018 at 09:44:25AM +0100, Hans Verkuil wrote:
> On 01/26/2018 03:41 PM, Sakari Ailus wrote:
> > Hi Hans,
> > 
> > On Fri, Jan 26, 2018 at 01:43:17PM +0100, Hans Verkuil wrote:
> >> From: Hans Verkuil <hans.verkuil@cisco.com>
> >>
> >> Don't duplicate the buffer type checks in enum/g/s/try_fmt.
> >> The check_fmt function does that already.
> >>
> >> It is hard to keep the checks in sync for all these functions and
> >> in fact the check for VBI was wrong in the _fmt functions as it
> >> allowed SDR types as well. This caused a v4l2-compliance failure
> >> for /dev/swradio0 using vivid.
> >>
> >> This simplifies the code and keeps the check in one place and
> >> fixes the SDR/VBI bug.
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> ---
> >>  drivers/media/v4l2-core/v4l2-ioctl.c | 140 ++++++++++++++---------------------
> >>  1 file changed, 54 insertions(+), 86 deletions(-)
> >>
> >> diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
> >> index 59d2100eeff6..c7f6b65d3ad7 100644
> >> --- a/drivers/media/v4l2-core/v4l2-ioctl.c
> >> +++ b/drivers/media/v4l2-core/v4l2-ioctl.c
> >> @@ -1316,52 +1316,50 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
> >>  				struct file *file, void *fh, void *arg)
> >>  {
> >>  	struct v4l2_fmtdesc *p = arg;
> >> -	struct video_device *vfd = video_devdata(file);
> >> -	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
> >> -	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
> >> -	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
> >> -	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
> >> -	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
> >> -	int ret = -EINVAL;
> >> +	int ret = check_fmt(file, p->type);
> > 
> > I'd separate this from the variable declaration. The function is doing more
> > than just fetch something to be used as a shorthand locally. I.e.
> > 
> > 	int ret;
> > 
> > 	ret = check_fmt(file, p->type);
> > 
> > Same elsewhere.
> 
> I'm not making this change. It's been like that since forever, and I don't
> feel I should change this in this patch. I personally don't really care one
> way or another, and especially in smaller functions like v4l_qbuf it
> actually looks kind of weird to change it.
> 
> In any case, a change like that doesn't belong here.

Ack, let's address that separately if we decide to change it.

-- 
Sakari Ailus
e-mail: sakari.ailus@iki.fi

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2018-01-30 11:14 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-01-26 12:43 [PATCH 00/12] v4l2-compat-ioctl32.c: remove set_fs(KERNEL_DS) Hans Verkuil
2018-01-26 12:43 ` [PATCH 01/12] vivid: fix module load error when enabling fb and no_error_inj=1 Hans Verkuil
2018-01-26 14:43   ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 02/12] v4l2-ioctl.c: use check_fmt for enum/g/s/try_fmt Hans Verkuil
2018-01-26 14:41   ` Sakari Ailus
2018-01-29 10:09     ` Hans Verkuil
2018-01-29 20:57       ` Sakari Ailus
2018-01-30  8:44     ` Hans Verkuil
2018-01-30 11:14       ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 03/12] v4l2-compat-ioctl32.c: add missing VIDIOC_PREPARE_BUF Hans Verkuil
2018-01-26 14:43   ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 04/12] v4l2-compat-ioctl32.c: fix the indentation Hans Verkuil
2018-01-26 14:57   ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 05/12] v4l2-compat-ioctl32.c: move 'helper' functions to __get/put_v4l2_format32 Hans Verkuil
2018-01-26 15:20   ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 06/12] v4l2-compat-ioctl32.c: avoid sizeof(type) Hans Verkuil
2018-01-26 15:35   ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 07/12] v4l2-compat-ioctl32.c: copy m.userptr in put_v4l2_plane32 Hans Verkuil
2018-01-26 16:15   ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 08/12] v4l2-compat-ioctl32.c: fix ctrl_is_pointer Hans Verkuil
2018-01-27 23:18   ` Sakari Ailus
2018-01-29 14:11     ` Mauro Carvalho Chehab
2018-01-26 12:43 ` [PATCH 09/12] v4l2-compat-ioctl32.c: copy clip list in put_v4l2_window32 Hans Verkuil
2018-01-29  9:47   ` Sakari Ailus
2018-01-29 14:13     ` Mauro Carvalho Chehab
2018-01-29 21:00       ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 10/12] v4l2-compat-ioctl32.c: drop pr_info for unknown buffer type Hans Verkuil
2018-01-29  9:48   ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 11/12] v4l2-compat-ioctl32.c: don't copy back the result for certain errors Hans Verkuil
2018-01-29  9:56   ` Sakari Ailus
2018-01-29 10:02     ` Hans Verkuil
2018-01-29 21:01       ` Sakari Ailus
2018-01-26 12:43 ` [PATCH 12/12] v4l2-compat-ioctl32.c: refactor, fix security bug in compat ioctl32 Hans Verkuil
2018-01-29 15:14   ` Mauro Carvalho Chehab
2018-01-29 17:06   ` Sakari Ailus
2018-01-29 17:41     ` Hans Verkuil
2018-01-29 21:14       ` Sakari Ailus
2018-01-30  9:37     ` Hans Verkuil

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.