On 5/10/19 12:09 AM, Janusz Krzysztofik wrote:
> 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
> reimplementing those checks inside drivers.
> 
> Signed-off-by: Janusz Krzysztofik <jmkrzy...@gmail.com>

I'm very much opposed to this as it creates one horrible macro.

The checks must be done for ioctls, since that's called by userspace and
it is therefor untrusted. But v4l2_subdev_call is just an internal
function call, and if you pass wrong arguments, then that's a driver bug.

If you want to avoid code duplication, then a much better approach is to
create helpers functions such as check_format() in e.g. v4l2-common.c that
can be called from wherever it is needed.

Regards,

        Hans

> ---
>  drivers/media/v4l2-core/v4l2-subdev.c | 121 +++-----------------------
>  include/media/v4l2-subdev.h           |  79 +++++++++++++++++
>  2 files changed, 89 insertions(+), 111 deletions(-)
> 
> diff --git a/drivers/media/v4l2-core/v4l2-subdev.c 
> b/drivers/media/v4l2-core/v4l2-subdev.c
> index d75815ab0d7b..186749d31abf 100644
> --- a/drivers/media/v4l2-core/v4l2-subdev.c
> +++ b/drivers/media/v4l2-core/v4l2-subdev.c
> @@ -121,53 +121,17 @@ static int subdev_close(struct file *file)
>  }
>  
>  #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
> -static int check_format(struct v4l2_subdev *sd,
> -                     struct v4l2_subdev_format *format)
> +void *v4l2_subdev_call_va_arg(int n, ...)
>  {
> -     if (format->which != V4L2_SUBDEV_FORMAT_TRY &&
> -         format->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> -             return -EINVAL;
> -
> -     if (format->pad >= sd->entity.num_pads)
> -             return -EINVAL;
> -
> -     return 0;
> -}
> -
> -static int check_crop(struct v4l2_subdev *sd, struct v4l2_subdev_crop *crop)
> -{
> -     if (crop->which != V4L2_SUBDEV_FORMAT_TRY &&
> -         crop->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> -             return -EINVAL;
> -
> -     if (crop->pad >= sd->entity.num_pads)
> -             return -EINVAL;
> -
> -     return 0;
> -}
> -
> -static int check_selection(struct v4l2_subdev *sd,
> -                        struct v4l2_subdev_selection *sel)
> -{
> -     if (sel->which != V4L2_SUBDEV_FORMAT_TRY &&
> -         sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
> -             return -EINVAL;
> -
> -     if (sel->pad >= sd->entity.num_pads)
> -             return -EINVAL;
> -
> -     return 0;
> -}
> -
> -static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid)
> -{
> -     if (edid->pad >= sd->entity.num_pads)
> -             return -EINVAL;
> -
> -     if (edid->blocks && edid->edid == NULL)
> -             return -EINVAL;
> -
> -     return 0;
> +     va_list ap;
> +     int i;
> +     void *p;
> +
> +     va_start(ap, n);
> +     for (i = 9; i < n; i++)
> +             p = va_arg(ap, void *);
> +     va_end(ap);
> +     return p;
>  }
>  #endif
>  
> @@ -292,10 +256,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 +264,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 +273,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 +292,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 +309,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 +317,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 +325,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 +332,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 +339,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 +347,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 +355,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 +363,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..a0ad8c6f588b 100644
> --- a/include/media/v4l2-subdev.h
> +++ b/include/media/v4l2-subdev.h
> @@ -1091,6 +1091,82 @@ 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);
>  
> +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
> +void *v4l2_subdev_call_va_arg(int n, ...);
> +
> +#define v4l2_subdev_call_chk_args(sd, o, f, args...)                 \
> +({                                                                   \
> +     __u32 __which = V4L2_SUBDEV_FORMAT_ACTIVE;                      \
> +     __u32 __pad = 0;                                                \
> +     if ((void *)&sd->ops->o == &sd->ops->pad) {                     \
> +             if ((void *)&sd->ops->o->f == &sd->ops->pad->get_fmt || \
> +                 (void *)&sd->ops->o->f == &sd->ops->pad->set_fmt) { \
> +                     struct v4l2_subdev_format *__fmt;               \
> +                     __fmt = v4l2_subdev_call_va_arg(2, ##args);     \
> +                     __which = __fmt->which;                         \
> +                     __pad = __fmt->pad;                             \
> +             } else if ((void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->enum_mbus_code) {     \
> +                     struct v4l2_subdev_mbus_code_enum *__code;      \
> +                     __code = v4l2_subdev_call_va_arg(2, ##args);    \
> +                     __which = __code->which;                        \
> +                     __pad = __code->pad;                            \
> +             } else if ((void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->enum_frame_size) {    \
> +                     struct v4l2_subdev_frame_size_enum *__fse;      \
> +                     __fse = v4l2_subdev_call_va_arg(2, ##args);     \
> +                     __which = __fse->which;                         \
> +                     __pad = __fse->pad;                             \
> +             } else if ((void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->enum_frame_interval) {\
> +                     struct v4l2_subdev_frame_interval_enum *__fie;  \
> +                     __fie = v4l2_subdev_call_va_arg(2, ##args);     \
> +                     __which = __fie->which;                         \
> +                     __pad = __fie->pad;                             \
> +             } else if ((void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->get_selection ||      \
> +                        (void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->set_selection) {      \
> +                     struct v4l2_subdev_selection *__sel;            \
> +                     __sel = v4l2_subdev_call_va_arg(2, ##args);     \
> +                     __which = __sel->which;                         \
> +                     __pad = __sel->pad;                             \
> +             } else if ((void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->get_edid ||           \
> +                        (void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->set_edid) {           \
> +                     struct v4l2_subdev_edid *__edid;                \
> +                     __edid = v4l2_subdev_call_va_arg(1, ##args);    \
> +                     __pad = __edid->pad;                            \
> +             } else if ((void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->dv_timings_cap) {     \
> +                     struct v4l2_dv_timings_cap *__cap;              \
> +                     __cap = v4l2_subdev_call_va_arg(1, ##args);     \
> +                     __pad = __cap->pad;                             \
> +             } else if ((void *)&sd->ops->o->f ==                    \
> +                                &sd->ops->pad->enum_dv_timings) {    \
> +                     struct v4l2_enum_dv_timings *__dvt;             \
> +                     __dvt = v4l2_subdev_call_va_arg(1, ##args);     \
> +                     __pad = __dvt->pad;                             \
> +             }                                                       \
> +     } else if ((void *)&sd->ops->o == &sd->ops->video) {            \
> +             if ((void *)&sd->ops->o->f ==                           \
> +                         &sd->ops->video->g_frame_interval ||        \
> +                 (void *)&sd->ops->o->f ==                           \
> +                         &sd->ops->video->s_frame_interval) {        \
> +                     struct v4l2_subdev_frame_interval *__fi;        \
> +                     __fi = v4l2_subdev_call_va_arg(1, ##args);      \
> +                     __pad = __fi->pad;                              \
> +             }                                                       \
> +     }                                                               \
> +     (__which == V4L2_SUBDEV_FORMAT_ACTIVE ||                        \
> +      __which == V4L2_SUBDEV_FORMAT_TRY) &&                          \
> +     __pad < (sd->entity.num_pads ? : 1) ? 0 : -EINVAL;              \
> +})
> +#else
> +#define v4l2_subdev_call_chk_args(sd, o, f, args...) 0
> +#endif
> +
>  /**
>   * v4l2_subdev_call - call an operation of a v4l2_subdev.
>   *
> @@ -1112,6 +1188,9 @@ 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_chk_args(sd, o, f, ##args))   \
> +                     __result = v4l2_subdev_call_chk_args(sd, o, f,  \
> +                                                          ##args);   \
>               else                                                    \
>                       __result = __sd->ops->o->f(__sd, ##args);       \
>               __result;                                               \
> 

Reply via email to