Correctness of format type (try or active) and pad ID parameters passed
to subdevice operation callbacks is now verified only for IOCTL calls.
However, those callbacks are also used by drivers, e.g., V4L2 host
interfaces.
Since both subdev_do_ioctl() and drivers are using v4l2_subdev_call()
macro while calling subdevice operations, move those parameter checks
from subdev_do_ioctl() to v4l2_subdev_call(). Also, add check for
non-NULL pointers, including pad config if V4L2_SUBDEV_FORMAT_TRY is
requested.
Having that done, we can avoid taking care of those checks inside
drivers.
Janusz Krzysztofik (3):
media: v4l2-subdev: Verify arguments in v4l2_subdev_call()
media: v4l2-subdev: Verify v4l2_subdev_call() pointer arguments
media: v4l2-subdev: Verify v4l2_subdev_call() pad config argument
drivers/media/v4l2-core/v4l2-subdev.c | 268 +++++++++++++++++---------
include/media/v4l2-subdev.h | 6 +
2 files changed, 188 insertions(+), 86 deletions(-)
Changelog:
v6->v7:
Changes suggested by Sakari - thanks!
- never succeed pad check on media entities with pad_num == 0,
- allow pad 0 on subdevies not registered as media entities.
v5->v6:
- rename wrappers to call_something() as suggested by Sakari - thanks!
- make check_ functions inline - also on Sakari's suggestion, thanks!
- drop patch 2/4 and remove WARN_ONs from remaining patches to avoid
kernel WARNs on non-kernel bugs - thanks Hans for pointing this out!
v4->v5:
- a few coding style and code formatting changes,
- require CONFIG_MEDIA_CONTROLLER, not CONFIG_VIDEO_V4L2_SUBDEV_API,
for a valid pad ID check,
- perform pad ID check only if at least one pad is configured so
drivers which don't configure pads are not affected if built with
CONFIG_MEDIA_CONTROLLER defined,
- issue kernel warnings on invalid parameters (new patch - 2/4),
- validate pointers before using them (new patch - 3/4).
v3->v4:
- fix 'struct' keyword missing from patch 2/2,
- fix checkpatch reported style issue in patch 2/2
Sorry for that.
v2->v3:
- add patch 2/2 with pad config check,
- adjust continuation line alignments in patch 1/2 to match those
used in 2/2.
v1->v2:
- replace the horrible macro with a structure of wrapper functions;
inspired by Hans' and Sakari's comments - thanks!
--
2.21.0
Parameters passed to check helpers are now obtained by dereferencing
unverified pointer arguments. Check validity of those pointers first.
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 59fdc0f08870..957c8e5cdfe1 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -147,6 +147,9 @@ static inline int check_pad(struct v4l2_subdev *sd, __u32 pad)
static inline int check_format(struct v4l2_subdev *sd,
struct v4l2_subdev_format *format)
{
+ if (!format)
+ return -EINVAL;
+
return check_which(format->which) ? : check_pad(sd, format->pad);
}
@@ -170,6 +173,9 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
+ if (!code)
+ return -EINVAL;
+
return check_which(code->which) ? : check_pad(sd, code->pad) ? :
sd->ops->pad->enum_mbus_code(sd, cfg, code);
}
@@ -178,6 +184,9 @@ static int call_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
+ if (!fse)
+ return -EINVAL;
+
return check_which(fse->which) ? : check_pad(sd, fse->pad) ? :
sd->ops->pad->enum_frame_size(sd, cfg, fse);
}
@@ -185,6 +194,9 @@ static int call_enum_frame_size(struct v4l2_subdev *sd,
static inline int check_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_frame_interval *fi)
{
+ if (!fi)
+ return -EINVAL;
+
return check_pad(sd, fi->pad);
}
@@ -206,6 +218,9 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_interval_enum *fie)
{
+ if (!fie)
+ return -EINVAL;
+
return check_which(fie->which) ? : check_pad(sd, fie->pad) ? :
sd->ops->pad->enum_frame_interval(sd, cfg, fie);
}
@@ -213,6 +228,9 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd,
static inline int check_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_selection *sel)
{
+ if (!sel)
+ return -EINVAL;
+
return check_which(sel->which) ? : check_pad(sd, sel->pad);
}
@@ -235,6 +253,9 @@ static int call_set_selection(struct v4l2_subdev *sd,
static inline int check_edid(struct v4l2_subdev *sd,
struct v4l2_subdev_edid *edid)
{
+ if (!edid)
+ return -EINVAL;
+
if (edid->blocks && edid->edid == NULL)
return -EINVAL;
@@ -254,6 +275,9 @@ static int call_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
static int call_dv_timings_cap(struct v4l2_subdev *sd,
struct v4l2_dv_timings_cap *cap)
{
+ if (!cap)
+ return -EINVAL;
+
return check_pad(sd, cap->pad) ? :
sd->ops->pad->dv_timings_cap(sd, cap);
}
@@ -261,6 +285,9 @@ static int call_dv_timings_cap(struct v4l2_subdev *sd,
static int call_enum_dv_timings(struct v4l2_subdev *sd,
struct v4l2_enum_dv_timings *dvt)
{
+ if (!dvt)
+ return -EINVAL;
+
return check_pad(sd, dvt->pad) ? :
sd->ops->pad->enum_dv_timings(sd, dvt);
}
--
2.21.0
Correctness of format type (try or active) and pad number parameters
passed to subdevice operation callbacks is now verified only for IOCTL
calls. However, those callbacks are also used by drivers, e.g., V4L2
host interfaces.
Since both subdev_do_ioctl() and drivers are using v4l2_subdev_call()
macro while calling subdevice operations, move those parameter checks
from subdev_do_ioctl() to v4l2_subdev_call() so we can avoid taking care
of those checks inside drivers.
Define a wrapper function for each operation callback in scope, then
gather those wrappers in a static v4l2_subdev_ops structure so the
v4l2_subdev_call() macro can find them easy if provided.
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/media/v4l2-core/v4l2-subdev.c | 238 ++++++++++++++++----------
include/media/v4l2-subdev.h | 6 +
2 files changed, 152 insertions(+), 92 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index d75815ab0d7b..59fdc0f08870 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -120,56 +120,175 @@ static int subdev_close(struct file *file)
return 0;
}
-#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
-static int check_format(struct v4l2_subdev *sd,
- struct v4l2_subdev_format *format)
+static inline int check_which(__u32 which)
{
- if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
- format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (format->pad >= sd->entity.num_pads)
+ if (which != V4L2_SUBDEV_FORMAT_TRY &&
+ which != V4L2_SUBDEV_FORMAT_ACTIVE)
return -EINVAL;
return 0;
}
-static int check_crop(struct v4l2_subdev *sd, struct v4l2_subdev_crop *crop)
+static inline int check_pad(struct v4l2_subdev *sd, __u32 pad)
{
- if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
- crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ if (sd->entity.graph_obj.mdev) {
+ if (pad >= sd->entity.num_pads)
+ return -EINVAL;
+ return 0;
+ }
+#endif
+ /* allow pad 0 on subdevices not registered as media entities */
+ if (pad > 0)
return -EINVAL;
+ return 0;
+}
- if (crop->pad >= sd->entity.num_pads)
- return -EINVAL;
+static inline int check_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_format *format)
+{
+ return check_which(format->which) ? : check_pad(sd, format->pad);
+}
- return 0;
+static int call_get_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ return check_format(sd, format) ? :
+ sd->ops->pad->get_fmt(sd, cfg, format);
}
-static int check_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_selection *sel)
+static int call_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
{
- if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
- sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
+ return check_format(sd, format) ? :
+ sd->ops->pad->set_fmt(sd, cfg, format);
+}
- if (sel->pad >= sd->entity.num_pads)
- return -EINVAL;
+static int call_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ return check_which(code->which) ? : check_pad(sd, code->pad) ? :
+ sd->ops->pad->enum_mbus_code(sd, cfg, code);
+}
- return 0;
+static int call_enum_frame_size(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ return check_which(fse->which) ? : check_pad(sd, fse->pad) ? :
+ sd->ops->pad->enum_frame_size(sd, cfg, fse);
}
-static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+static inline int check_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
{
- if (edid->pad >= sd->entity.num_pads)
- return -EINVAL;
+ return check_pad(sd, fi->pad);
+}
+
+static int call_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ return check_frame_interval(sd, fi) ? :
+ sd->ops->video->g_frame_interval(sd, fi);
+}
+
+static int call_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ return check_frame_interval(sd, fi) ? :
+ sd->ops->video->s_frame_interval(sd, fi);
+}
+
+static int call_enum_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_frame_interval_enum *fie)
+{
+ return check_which(fie->which) ? : check_pad(sd, fie->pad) ? :
+ sd->ops->pad->enum_frame_interval(sd, cfg, fie);
+}
+static inline int check_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_selection *sel)
+{
+ return check_which(sel->which) ? : check_pad(sd, sel->pad);
+}
+
+static int call_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ return check_selection(sd, sel) ? :
+ sd->ops->pad->get_selection(sd, cfg, sel);
+}
+
+static int call_set_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_selection *sel)
+{
+ return check_selection(sd, sel) ? :
+ sd->ops->pad->set_selection(sd, cfg, sel);
+}
+
+static inline int check_edid(struct v4l2_subdev *sd,
+ struct v4l2_subdev_edid *edid)
+{
if (edid->blocks && edid->edid == NULL)
return -EINVAL;
- return 0;
+ return check_pad(sd, edid->pad);
}
-#endif
+
+static int call_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+{
+ return check_edid(sd, edid) ? : sd->ops->pad->get_edid(sd, edid);
+}
+
+static int call_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
+{
+ return check_edid(sd, edid) ? : sd->ops->pad->set_edid(sd, edid);
+}
+
+static int call_dv_timings_cap(struct v4l2_subdev *sd,
+ struct v4l2_dv_timings_cap *cap)
+{
+ return check_pad(sd, cap->pad) ? :
+ sd->ops->pad->dv_timings_cap(sd, cap);
+}
+
+static int call_enum_dv_timings(struct v4l2_subdev *sd,
+ struct v4l2_enum_dv_timings *dvt)
+{
+ return check_pad(sd, dvt->pad) ? :
+ sd->ops->pad->enum_dv_timings(sd, dvt);
+}
+
+static const struct v4l2_subdev_pad_ops v4l2_subdev_call_pad_wrappers = {
+ .get_fmt = call_get_fmt,
+ .set_fmt = call_set_fmt,
+ .enum_mbus_code = call_enum_mbus_code,
+ .enum_frame_size = call_enum_frame_size,
+ .enum_frame_interval = call_enum_frame_interval,
+ .get_selection = call_get_selection,
+ .set_selection = call_set_selection,
+ .get_edid = call_get_edid,
+ .set_edid = call_set_edid,
+ .dv_timings_cap = call_dv_timings_cap,
+ .enum_dv_timings = call_enum_dv_timings,
+};
+
+static const struct v4l2_subdev_video_ops v4l2_subdev_call_video_wrappers = {
+ .g_frame_interval = call_g_frame_interval,
+ .s_frame_interval = call_s_frame_interval,
+};
+
+const struct v4l2_subdev_ops v4l2_subdev_call_wrappers = {
+ .pad = &v4l2_subdev_call_pad_wrappers,
+ .video = &v4l2_subdev_call_video_wrappers,
+};
+EXPORT_SYMBOL(v4l2_subdev_call_wrappers);
static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
{
@@ -292,10 +411,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_G_FMT: {
struct v4l2_subdev_format *format = arg;
- rval = check_format(sd, format);
- if (rval)
- return rval;
-
memset(format->reserved, 0, sizeof(format->reserved));
memset(format->format.reserved, 0, sizeof(format->format.reserved));
return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh->pad, format);
@@ -304,10 +419,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_S_FMT: {
struct v4l2_subdev_format *format = arg;
- rval = check_format(sd, format);
- if (rval)
- return rval;
-
memset(format->reserved, 0, sizeof(format->reserved));
memset(format->format.reserved, 0, sizeof(format->format.reserved));
return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh->pad, format);
@@ -317,10 +428,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_subdev_crop *crop = arg;
struct v4l2_subdev_selection sel;
- rval = check_crop(sd, crop);
- if (rval)
- return rval;
-
memset(crop->reserved, 0, sizeof(crop->reserved));
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
@@ -340,10 +447,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
struct v4l2_subdev_selection sel;
memset(crop->reserved, 0, sizeof(crop->reserved));
- rval = check_crop(sd, crop);
- if (rval)
- return rval;
-
memset(&sel, 0, sizeof(sel));
sel.which = crop->which;
sel.pad = crop->pad;
@@ -361,13 +464,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_ENUM_MBUS_CODE: {
struct v4l2_subdev_mbus_code_enum *code = arg;
- if (code->which != V4L2_SUBDEV_FORMAT_TRY &&
- code->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (code->pad >= sd->entity.num_pads)
- return -EINVAL;
-
memset(code->reserved, 0, sizeof(code->reserved));
return v4l2_subdev_call(sd, pad, enum_mbus_code, subdev_fh->pad,
code);
@@ -376,13 +472,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_ENUM_FRAME_SIZE: {
struct v4l2_subdev_frame_size_enum *fse = arg;
- if (fse->which != V4L2_SUBDEV_FORMAT_TRY &&
- fse->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (fse->pad >= sd->entity.num_pads)
- return -EINVAL;
-
memset(fse->reserved, 0, sizeof(fse->reserved));
return v4l2_subdev_call(sd, pad, enum_frame_size, subdev_fh->pad,
fse);
@@ -391,9 +480,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_G_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval *fi = arg;
- if (fi->pad >= sd->entity.num_pads)
- return -EINVAL;
-
memset(fi->reserved, 0, sizeof(fi->reserved));
return v4l2_subdev_call(sd, video, g_frame_interval, arg);
}
@@ -401,9 +487,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_S_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval *fi = arg;
- if (fi->pad >= sd->entity.num_pads)
- return -EINVAL;
-
memset(fi->reserved, 0, sizeof(fi->reserved));
return v4l2_subdev_call(sd, video, s_frame_interval, arg);
}
@@ -411,13 +494,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL: {
struct v4l2_subdev_frame_interval_enum *fie = arg;
- if (fie->which != V4L2_SUBDEV_FORMAT_TRY &&
- fie->which != V4L2_SUBDEV_FORMAT_ACTIVE)
- return -EINVAL;
-
- if (fie->pad >= sd->entity.num_pads)
- return -EINVAL;
-
memset(fie->reserved, 0, sizeof(fie->reserved));
return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh->pad,
fie);
@@ -426,10 +502,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_G_SELECTION: {
struct v4l2_subdev_selection *sel = arg;
- rval = check_selection(sd, sel);
- if (rval)
- return rval;
-
memset(sel->reserved, 0, sizeof(sel->reserved));
return v4l2_subdev_call(
sd, pad, get_selection, subdev_fh->pad, sel);
@@ -438,10 +510,6 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_SUBDEV_S_SELECTION: {
struct v4l2_subdev_selection *sel = arg;
- rval = check_selection(sd, sel);
- if (rval)
- return rval;
-
memset(sel->reserved, 0, sizeof(sel->reserved));
return v4l2_subdev_call(
sd, pad, set_selection, subdev_fh->pad, sel);
@@ -450,38 +518,24 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
case VIDIOC_G_EDID: {
struct v4l2_subdev_edid *edid = arg;
- rval = check_edid(sd, edid);
- if (rval)
- return rval;
-
return v4l2_subdev_call(sd, pad, get_edid, edid);
}
case VIDIOC_S_EDID: {
struct v4l2_subdev_edid *edid = arg;
- rval = check_edid(sd, edid);
- if (rval)
- return rval;
-
return v4l2_subdev_call(sd, pad, set_edid, edid);
}
case VIDIOC_SUBDEV_DV_TIMINGS_CAP: {
struct v4l2_dv_timings_cap *cap = arg;
- if (cap->pad >= sd->entity.num_pads)
- return -EINVAL;
-
return v4l2_subdev_call(sd, pad, dv_timings_cap, cap);
}
case VIDIOC_SUBDEV_ENUM_DV_TIMINGS: {
struct v4l2_enum_dv_timings *dvt = arg;
- if (dvt->pad >= sd->entity.num_pads)
- return -EINVAL;
-
return v4l2_subdev_call(sd, pad, enum_dv_timings, dvt);
}
diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
index a7fa5b80915a..e1e3c18c3fd6 100644
--- a/include/media/v4l2-subdev.h
+++ b/include/media/v4l2-subdev.h
@@ -1091,6 +1091,8 @@ void v4l2_subdev_free_pad_config(struct v4l2_subdev_pad_config *cfg);
void v4l2_subdev_init(struct v4l2_subdev *sd,
const struct v4l2_subdev_ops *ops);
+extern const struct v4l2_subdev_ops v4l2_subdev_call_wrappers;
+
/**
* v4l2_subdev_call - call an operation of a v4l2_subdev.
*
@@ -1112,6 +1114,10 @@ void v4l2_subdev_init(struct v4l2_subdev *sd,
__result = -ENODEV; \
else if (!(__sd->ops->o && __sd->ops->o->f)) \
__result = -ENOIOCTLCMD; \
+ else if (v4l2_subdev_call_wrappers.o && \
+ v4l2_subdev_call_wrappers.o->f) \
+ __result = v4l2_subdev_call_wrappers.o->f( \
+ __sd, ##args); \
else \
__result = __sd->ops->o->f(__sd, ##args); \
__result; \
--
2.21.0
Extend parameter checks performed by v4l2_subdev_call() with a check for
a non-NULL pad config pointer if V4L2_SUBDEV_FORMAT_TRY format type is
requested so drivers don't need to care.
Signed-off-by: Janusz Krzysztofik <[email protected]>
---
drivers/media/v4l2-core/v4l2-subdev.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c
index 957c8e5cdfe1..34219e489be2 100644
--- a/drivers/media/v4l2-core/v4l2-subdev.c
+++ b/drivers/media/v4l2-core/v4l2-subdev.c
@@ -144,20 +144,30 @@ static inline int check_pad(struct v4l2_subdev *sd, __u32 pad)
return 0;
}
+static int check_cfg(__u32 which, struct v4l2_subdev_pad_config *cfg)
+{
+ if (which == V4L2_SUBDEV_FORMAT_TRY && !cfg)
+ return -EINVAL;
+
+ return 0;
+}
+
static inline int check_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
if (!format)
return -EINVAL;
- return check_which(format->which) ? : check_pad(sd, format->pad);
+ return check_which(format->which) ? : check_pad(sd, format->pad) ? :
+ check_cfg(format->which, cfg);
}
static int call_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
- return check_format(sd, format) ? :
+ return check_format(sd, cfg, format) ? :
sd->ops->pad->get_fmt(sd, cfg, format);
}
@@ -165,7 +175,7 @@ static int call_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
- return check_format(sd, format) ? :
+ return check_format(sd, cfg, format) ? :
sd->ops->pad->set_fmt(sd, cfg, format);
}
@@ -177,6 +187,7 @@ static int call_enum_mbus_code(struct v4l2_subdev *sd,
return -EINVAL;
return check_which(code->which) ? : check_pad(sd, code->pad) ? :
+ check_cfg(code->which, cfg) ? :
sd->ops->pad->enum_mbus_code(sd, cfg, code);
}
@@ -188,6 +199,7 @@ static int call_enum_frame_size(struct v4l2_subdev *sd,
return -EINVAL;
return check_which(fse->which) ? : check_pad(sd, fse->pad) ? :
+ check_cfg(fse->which, cfg) ? :
sd->ops->pad->enum_frame_size(sd, cfg, fse);
}
@@ -222,23 +234,26 @@ static int call_enum_frame_interval(struct v4l2_subdev *sd,
return -EINVAL;
return check_which(fie->which) ? : check_pad(sd, fie->pad) ? :
+ check_cfg(fie->which, cfg) ? :
sd->ops->pad->enum_frame_interval(sd, cfg, fie);
}
static inline int check_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
if (!sel)
return -EINVAL;
- return check_which(sel->which) ? : check_pad(sd, sel->pad);
+ return check_which(sel->which) ? : check_pad(sd, sel->pad) ? :
+ check_cfg(sel->which, cfg);
}
static int call_get_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
- return check_selection(sd, sel) ? :
+ return check_selection(sd, cfg, sel) ? :
sd->ops->pad->get_selection(sd, cfg, sel);
}
@@ -246,7 +261,7 @@ static int call_set_selection(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_selection *sel)
{
- return check_selection(sd, sel) ? :
+ return check_selection(sd, cfg, sel) ? :
sd->ops->pad->set_selection(sd, cfg, sel);
}
--
2.21.0
Hi Janusz,
On Mon, May 20, 2019 at 11:27:44PM +0200, Janusz Krzysztofik wrote:
> Correctness of format type (try or active) and pad ID parameters passed
> to subdevice operation callbacks is now verified only for IOCTL calls.
> However, those callbacks are also used by drivers, e.g., V4L2 host
> interfaces.
>
> Since both subdev_do_ioctl() and drivers are using v4l2_subdev_call()
> macro while calling subdevice operations, move those parameter checks
> from subdev_do_ioctl() to v4l2_subdev_call(). Also, add check for
> non-NULL pointers, including pad config if V4L2_SUBDEV_FORMAT_TRY is
> requested.
>
> Having that done, we can avoid taking care of those checks inside
> drivers.
>
> Janusz Krzysztofik (3):
> media: v4l2-subdev: Verify arguments in v4l2_subdev_call()
> media: v4l2-subdev: Verify v4l2_subdev_call() pointer arguments
> media: v4l2-subdev: Verify v4l2_subdev_call() pad config argument
For the set:
Reviewed-by: Sakari Ailus <[email protected]>
On the 1st patch __u32 should be u32. I'd suggest to fix that in a separate
patch.
This was a really nice set. Thank you!
--
Kind regards,
Sakari Ailus
[email protected]
Sakari,
Are you OK with this series? Please Ack if that's the case, so that I can
merge it.
Regards,
Hans
On 5/20/19 11:27 PM, Janusz Krzysztofik wrote:
> Correctness of format type (try or active) and pad ID parameters passed
> to subdevice operation callbacks is now verified only for IOCTL calls.
> However, those callbacks are also used by drivers, e.g., V4L2 host
> interfaces.
>
> Since both subdev_do_ioctl() and drivers are using v4l2_subdev_call()
> macro while calling subdevice operations, move those parameter checks
> from subdev_do_ioctl() to v4l2_subdev_call(). Also, add check for
> non-NULL pointers, including pad config if V4L2_SUBDEV_FORMAT_TRY is
> requested.
>
> Having that done, we can avoid taking care of those checks inside
> drivers.
>
> Janusz Krzysztofik (3):
> media: v4l2-subdev: Verify arguments in v4l2_subdev_call()
> media: v4l2-subdev: Verify v4l2_subdev_call() pointer arguments
> media: v4l2-subdev: Verify v4l2_subdev_call() pad config argument
>
> drivers/media/v4l2-core/v4l2-subdev.c | 268 +++++++++++++++++---------
> include/media/v4l2-subdev.h | 6 +
> 2 files changed, 188 insertions(+), 86 deletions(-)
>
> Changelog:
> v6->v7:
> Changes suggested by Sakari - thanks!
> - never succeed pad check on media entities with pad_num == 0,
> - allow pad 0 on subdevies not registered as media entities.
>
> v5->v6:
> - rename wrappers to call_something() as suggested by Sakari - thanks!
> - make check_ functions inline - also on Sakari's suggestion, thanks!
> - drop patch 2/4 and remove WARN_ONs from remaining patches to avoid
> kernel WARNs on non-kernel bugs - thanks Hans for pointing this out!
>
> v4->v5:
> - a few coding style and code formatting changes,
> - require CONFIG_MEDIA_CONTROLLER, not CONFIG_VIDEO_V4L2_SUBDEV_API,
> for a valid pad ID check,
> - perform pad ID check only if at least one pad is configured so
> drivers which don't configure pads are not affected if built with
> CONFIG_MEDIA_CONTROLLER defined,
> - issue kernel warnings on invalid parameters (new patch - 2/4),
> - validate pointers before using them (new patch - 3/4).
>
> v3->v4:
> - fix 'struct' keyword missing from patch 2/2,
> - fix checkpatch reported style issue in patch 2/2
> Sorry for that.
>
> v2->v3:
> - add patch 2/2 with pad config check,
> - adjust continuation line alignments in patch 1/2 to match those
> used in 2/2.
>
> v1->v2:
> - replace the horrible macro with a structure of wrapper functions;
> inspired by Hans' and Sakari's comments - thanks!
>
Hi,
This patch breaks rcar-vin. I'm sorry I did not find out before it was
merged as a8fa55078a7784a9 ("media: v4l2-subdev: Verify arguments in
v4l2_subdev_call()").
The problem is that rcar-vin calls enum_mbus_code in its bound callback.
At this point call_enum_mbus_code() is invoked which then calls
check_pad(). At this point sd->entity.graph_obj.mdev is not set so the
check if (pad > 0) fails and the binding of the subdevice in rcar-vin
fails.
I'm not sure how to best solve this, suggestions are appreciated. I see
two options, move the call to enum_mbus_code from the bound to the
complete callback or make sure the mdev is associated with the subdev
before the bound callback is invoked. I don't like the former as I think
the complete callback should be removed ;-)
On 2019-05-20 23:27:44 +0200, Janusz Krzysztofik wrote:
> Correctness of format type (try or active) and pad ID parameters passed
> to subdevice operation callbacks is now verified only for IOCTL calls.
> However, those callbacks are also used by drivers, e.g., V4L2 host
> interfaces.
>
> Since both subdev_do_ioctl() and drivers are using v4l2_subdev_call()
> macro while calling subdevice operations, move those parameter checks
> from subdev_do_ioctl() to v4l2_subdev_call(). Also, add check for
> non-NULL pointers, including pad config if V4L2_SUBDEV_FORMAT_TRY is
> requested.
>
> Having that done, we can avoid taking care of those checks inside
> drivers.
>
> Janusz Krzysztofik (3):
> media: v4l2-subdev: Verify arguments in v4l2_subdev_call()
> media: v4l2-subdev: Verify v4l2_subdev_call() pointer arguments
> media: v4l2-subdev: Verify v4l2_subdev_call() pad config argument
>
> drivers/media/v4l2-core/v4l2-subdev.c | 268 +++++++++++++++++---------
> include/media/v4l2-subdev.h | 6 +
> 2 files changed, 188 insertions(+), 86 deletions(-)
>
> Changelog:
> v6->v7:
> Changes suggested by Sakari - thanks!
> - never succeed pad check on media entities with pad_num == 0,
> - allow pad 0 on subdevies not registered as media entities.
>
> v5->v6:
> - rename wrappers to call_something() as suggested by Sakari - thanks!
> - make check_ functions inline - also on Sakari's suggestion, thanks!
> - drop patch 2/4 and remove WARN_ONs from remaining patches to avoid
> kernel WARNs on non-kernel bugs - thanks Hans for pointing this out!
>
> v4->v5:
> - a few coding style and code formatting changes,
> - require CONFIG_MEDIA_CONTROLLER, not CONFIG_VIDEO_V4L2_SUBDEV_API,
> for a valid pad ID check,
> - perform pad ID check only if at least one pad is configured so
> drivers which don't configure pads are not affected if built with
> CONFIG_MEDIA_CONTROLLER defined,
> - issue kernel warnings on invalid parameters (new patch - 2/4),
> - validate pointers before using them (new patch - 3/4).
>
> v3->v4:
> - fix 'struct' keyword missing from patch 2/2,
> - fix checkpatch reported style issue in patch 2/2
> Sorry for that.
>
> v2->v3:
> - add patch 2/2 with pad config check,
> - adjust continuation line alignments in patch 1/2 to match those
> used in 2/2.
>
> v1->v2:
> - replace the horrible macro with a structure of wrapper functions;
> inspired by Hans' and Sakari's comments - thanks!
>
> --
> 2.21.0
>
On 6/29/19 5:28 AM, Niklas Söderlund wrote:
> Hi,
>
> This patch breaks rcar-vin. I'm sorry I did not find out before it was
> merged as a8fa55078a7784a9 ("media: v4l2-subdev: Verify arguments in
> v4l2_subdev_call()").
>
> The problem is that rcar-vin calls enum_mbus_code in its bound callback.
> At this point call_enum_mbus_code() is invoked which then calls
> check_pad(). At this point sd->entity.graph_obj.mdev is not set so the
> check if (pad > 0) fails and the binding of the subdevice in rcar-vin
> fails.
>
> I'm not sure how to best solve this, suggestions are appreciated. I see
> two options, move the call to enum_mbus_code from the bound to the
> complete callback or make sure the mdev is associated with the subdev
> before the bound callback is invoked. I don't like the former as I think
> the complete callback should be removed ;-)
I don't think check_pad() should check mdev. Try this instead:
static inline int check_pad(struct v4l2_subdev *sd, __u32 pad)
{
#if defined(CONFIG_MEDIA_CONTROLLER)
if (sd->entity.num_pads)
return pad >= sd->entity.num_pads ? -EINVAL : 0;
#endif
/* allow pad 0 on subdevices not registered as media entities */
return pad ? -EINVAL : 0;
}
If the subdev defines pads, then sd->entity.num_pads is non-zero and it can
be used to check the pad value, otherwise it should just only accept pad 0.
And it shouldn't depend on mdev, since that (as you discovered) may not be
set yet.
Regards,
Hans
>
> On 2019-05-20 23:27:44 +0200, Janusz Krzysztofik wrote:
>> Correctness of format type (try or active) and pad ID parameters passed
>> to subdevice operation callbacks is now verified only for IOCTL calls.
>> However, those callbacks are also used by drivers, e.g., V4L2 host
>> interfaces.
>>
>> Since both subdev_do_ioctl() and drivers are using v4l2_subdev_call()
>> macro while calling subdevice operations, move those parameter checks
>> from subdev_do_ioctl() to v4l2_subdev_call(). Also, add check for
>> non-NULL pointers, including pad config if V4L2_SUBDEV_FORMAT_TRY is
>> requested.
>>
>> Having that done, we can avoid taking care of those checks inside
>> drivers.
>>
>> Janusz Krzysztofik (3):
>> media: v4l2-subdev: Verify arguments in v4l2_subdev_call()
>> media: v4l2-subdev: Verify v4l2_subdev_call() pointer arguments
>> media: v4l2-subdev: Verify v4l2_subdev_call() pad config argument
>>
>> drivers/media/v4l2-core/v4l2-subdev.c | 268 +++++++++++++++++---------
>> include/media/v4l2-subdev.h | 6 +
>> 2 files changed, 188 insertions(+), 86 deletions(-)
>>
>> Changelog:
>> v6->v7:
>> Changes suggested by Sakari - thanks!
>> - never succeed pad check on media entities with pad_num == 0,
>> - allow pad 0 on subdevies not registered as media entities.
>>
>> v5->v6:
>> - rename wrappers to call_something() as suggested by Sakari - thanks!
>> - make check_ functions inline - also on Sakari's suggestion, thanks!
>> - drop patch 2/4 and remove WARN_ONs from remaining patches to avoid
>> kernel WARNs on non-kernel bugs - thanks Hans for pointing this out!
>>
>> v4->v5:
>> - a few coding style and code formatting changes,
>> - require CONFIG_MEDIA_CONTROLLER, not CONFIG_VIDEO_V4L2_SUBDEV_API,
>> for a valid pad ID check,
>> - perform pad ID check only if at least one pad is configured so
>> drivers which don't configure pads are not affected if built with
>> CONFIG_MEDIA_CONTROLLER defined,
>> - issue kernel warnings on invalid parameters (new patch - 2/4),
>> - validate pointers before using them (new patch - 3/4).
>>
>> v3->v4:
>> - fix 'struct' keyword missing from patch 2/2,
>> - fix checkpatch reported style issue in patch 2/2
>> Sorry for that.
>>
>> v2->v3:
>> - add patch 2/2 with pad config check,
>> - adjust continuation line alignments in patch 1/2 to match those
>> used in 2/2.
>>
>> v1->v2:
>> - replace the horrible macro with a structure of wrapper functions;
>> inspired by Hans' and Sakari's comments - thanks!
>>
>> --
>> 2.21.0
>>