diff mbox series

uvcvideo: uvc_ctrl_is_accessible: check for INACTIVE

Message ID b1c94f21-4ae1-148c-fa5f-f9e4719049b9@xs4all.nl (mailing list archive)
State New, archived
Headers show
Series uvcvideo: uvc_ctrl_is_accessible: check for INACTIVE | expand

Commit Message

Hans Verkuil March 22, 2021, 12:04 p.m. UTC
Check for inactive controls in uvc_ctrl_is_accessible().
Use the new value for the master_id controls if present,
otherwise use the existing value to determine if it is OK
to set the control. Doing this here avoids attempting to
set an inactive control, which will return an error from the
USB device.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
Ricardo: this can be added to your uvc series. It avoids attempts to set
inactive controls.
---
 drivers/media/usb/uvc/uvc_ctrl.c | 28 +++++++++++++++++++++++++++-
 drivers/media/usb/uvc/uvc_v4l2.c |  4 ++--
 drivers/media/usb/uvc/uvcvideo.h |  3 ++-
 3 files changed, 31 insertions(+), 4 deletions(-)

Comments

Ricardo Ribalda Delgado March 23, 2021, 3:29 p.m. UTC | #1
Hi Hans

On Mon, Mar 22, 2021 at 1:06 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:
>
> Check for inactive controls in uvc_ctrl_is_accessible().
> Use the new value for the master_id controls if present,
> otherwise use the existing value to determine if it is OK
> to set the control. Doing this here avoids attempting to
> set an inactive control, which will return an error from the
> USB device.

Dont you think that this patch is not needed if this is used:
https://patchwork.linuxtv.org/project/linux-media/patch/20210319170906.278238-17-ribalda@chromium.org/
?


Also Maybe it is wrong. (Maybe it is the keyword here ;).
I think the initial assumption was that uvc_ctrl_is_accessible shall
not access the hardware, but with this patch it is accessing it.
>
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
> Ricardo: this can be added to your uvc series. It avoids attempts to set
> inactive controls.
> ---
>  drivers/media/usb/uvc/uvc_ctrl.c | 28 +++++++++++++++++++++++++++-
>  drivers/media/usb/uvc/uvc_v4l2.c |  4 ++--
>  drivers/media/usb/uvc/uvcvideo.h |  3 ++-
>  3 files changed, 31 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> index d9d4add1e813..6e7b904bc33d 100644
> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> @@ -1047,10 +1047,18 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
>  }
>
>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> -                          bool read)
> +                          const struct v4l2_ext_controls *ctrls,
> +                          unsigned long ioctl)
>  {
> +       struct uvc_control_mapping *master_map = NULL;
> +       struct uvc_control *master_ctrl = NULL;
>         struct uvc_control_mapping *mapping;
>         struct uvc_control *ctrl;
> +       bool read = ioctl == VIDIOC_G_EXT_CTRLS;
> +       bool try = ioctl == VIDIOC_TRY_EXT_CTRLS;
> +       s32 val;
> +       int ret;
> +       int i;
>
>         if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
>                 return -EACCES;
> @@ -1065,6 +1073,24 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>         if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
>                 return -EACCES;
>
> +       if (read || try || !mapping->master_id)
> +               return 0;
> +
> +       for (i = ctrls->count - 1; i >= 0; i--)
> +               if (ctrls->controls[i].id == mapping->master_id)
> +                       return ctrls->controls[i].value ==
> +                                       mapping->master_manual ? 0 : -EACCES;
> +
> +       __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
> +                          &master_ctrl, 0);
> +
> +       if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
> +               return 0;
> +
> +       ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
> +       if (ret >= 0 && val != mapping->master_manual)
> +               return -EACCES;
> +
>         return 0;
>  }
>
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 12362e0f9870..e40db7ae18b1 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -803,8 +803,8 @@ static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
>         int ret = 0;
>
>         for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> -               ret = uvc_ctrl_is_accessible(chain, ctrl->id,
> -                                           ioctl == VIDIOC_G_EXT_CTRLS);
> +               ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls,
> +                                           ioctl);
>                 if (ret)
>                         break;
>         }
> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> index aedb4d3d4db9..8849d7953767 100644
> --- a/drivers/media/usb/uvc/uvcvideo.h
> +++ b/drivers/media/usb/uvc/uvcvideo.h
> @@ -869,7 +869,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
>  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> -                          bool read);
> +                          const struct v4l2_ext_controls *ctrls,
> +                          unsigned long ioctl);
>
>  int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>                       struct uvc_xu_control_query *xqry);
> --
> 2.30.0
>
>
Hans Verkuil March 25, 2021, 12:27 p.m. UTC | #2
On 23/03/2021 16:29, Ricardo Ribalda Delgado wrote:
> Hi Hans
> 
> On Mon, Mar 22, 2021 at 1:06 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:
>>
>> Check for inactive controls in uvc_ctrl_is_accessible().
>> Use the new value for the master_id controls if present,
>> otherwise use the existing value to determine if it is OK
>> to set the control. Doing this here avoids attempting to
>> set an inactive control, which will return an error from the
>> USB device.
> 
> Dont you think that this patch is not needed if this is used:
> https://patchwork.linuxtv.org/project/linux-media/patch/20210319170906.278238-17-ribalda@chromium.org/
> ?

That might well be the case :-)

I haven't really looked at that.

> 
> 
> Also Maybe it is wrong. (Maybe it is the keyword here ;).
> I think the initial assumption was that uvc_ctrl_is_accessible shall
> not access the hardware, but with this patch it is accessing it.

It's only accessing it if master_ctrl->loaded is 0 (see __uvc_ctrl_get()).
If loaded is 0, and __uvc_ctrl_get() returns an error, then that error is
ignored and the code will assume that the control is accessible.

Note that what we try to avoid is *setting* an inactive control. In this
case we're reading the master control, which should be safe except for
USB hardware problems that can return an error.

Regards,

	Hans

>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>> Ricardo: this can be added to your uvc series. It avoids attempts to set
>> inactive controls.
>> ---
>>  drivers/media/usb/uvc/uvc_ctrl.c | 28 +++++++++++++++++++++++++++-
>>  drivers/media/usb/uvc/uvc_v4l2.c |  4 ++--
>>  drivers/media/usb/uvc/uvcvideo.h |  3 ++-
>>  3 files changed, 31 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
>> index d9d4add1e813..6e7b904bc33d 100644
>> --- a/drivers/media/usb/uvc/uvc_ctrl.c
>> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
>> @@ -1047,10 +1047,18 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
>>  }
>>
>>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>> -                          bool read)
>> +                          const struct v4l2_ext_controls *ctrls,
>> +                          unsigned long ioctl)
>>  {
>> +       struct uvc_control_mapping *master_map = NULL;
>> +       struct uvc_control *master_ctrl = NULL;
>>         struct uvc_control_mapping *mapping;
>>         struct uvc_control *ctrl;
>> +       bool read = ioctl == VIDIOC_G_EXT_CTRLS;
>> +       bool try = ioctl == VIDIOC_TRY_EXT_CTRLS;
>> +       s32 val;
>> +       int ret;
>> +       int i;
>>
>>         if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
>>                 return -EACCES;
>> @@ -1065,6 +1073,24 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>>         if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
>>                 return -EACCES;
>>
>> +       if (read || try || !mapping->master_id)
>> +               return 0;
>> +
>> +       for (i = ctrls->count - 1; i >= 0; i--)
>> +               if (ctrls->controls[i].id == mapping->master_id)
>> +                       return ctrls->controls[i].value ==
>> +                                       mapping->master_manual ? 0 : -EACCES;
>> +
>> +       __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
>> +                          &master_ctrl, 0);
>> +
>> +       if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
>> +               return 0;
>> +
>> +       ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
>> +       if (ret >= 0 && val != mapping->master_manual)
>> +               return -EACCES;
>> +
>>         return 0;
>>  }
>>
>> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
>> index 12362e0f9870..e40db7ae18b1 100644
>> --- a/drivers/media/usb/uvc/uvc_v4l2.c
>> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
>> @@ -803,8 +803,8 @@ static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
>>         int ret = 0;
>>
>>         for (i = 0; i < ctrls->count; ++ctrl, ++i) {
>> -               ret = uvc_ctrl_is_accessible(chain, ctrl->id,
>> -                                           ioctl == VIDIOC_G_EXT_CTRLS);
>> +               ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls,
>> +                                           ioctl);
>>                 if (ret)
>>                         break;
>>         }
>> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
>> index aedb4d3d4db9..8849d7953767 100644
>> --- a/drivers/media/usb/uvc/uvcvideo.h
>> +++ b/drivers/media/usb/uvc/uvcvideo.h
>> @@ -869,7 +869,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
>>  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
>>  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
>>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
>> -                          bool read);
>> +                          const struct v4l2_ext_controls *ctrls,
>> +                          unsigned long ioctl);
>>
>>  int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
>>                       struct uvc_xu_control_query *xqry);
>> --
>> 2.30.0
>>
>>
> 
>
Ricardo Ribalda March 25, 2021, 2:02 p.m. UTC | #3
Hi Hans

On Thu, Mar 25, 2021 at 1:27 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:
>
> On 23/03/2021 16:29, Ricardo Ribalda Delgado wrote:
> > Hi Hans
> >
> > On Mon, Mar 22, 2021 at 1:06 PM Hans Verkuil <hverkuil@xs4all.nl> wrote:
> >>
> >> Check for inactive controls in uvc_ctrl_is_accessible().
> >> Use the new value for the master_id controls if present,
> >> otherwise use the existing value to determine if it is OK
> >> to set the control. Doing this here avoids attempting to
> >> set an inactive control, which will return an error from the
> >> USB device.
> >
> > Dont you think that this patch is not needed if this is used:
> > https://patchwork.linuxtv.org/project/linux-media/patch/20210319170906.278238-17-ribalda@chromium.org/
> > ?
>
> That might well be the case :-)
>
> I haven't really looked at that.

They overlap a bit. Not sure if we can remove one of them. Lets see
what Laurent thinks.

Thanks!

>
> >
> >
> > Also Maybe it is wrong. (Maybe it is the keyword here ;).
> > I think the initial assumption was that uvc_ctrl_is_accessible shall
> > not access the hardware, but with this patch it is accessing it.
>
> It's only accessing it if master_ctrl->loaded is 0 (see __uvc_ctrl_get()).
> If loaded is 0, and __uvc_ctrl_get() returns an error, then that error is
> ignored and the code will assume that the control is accessible.
>
> Note that what we try to avoid is *setting* an inactive control. In this
> case we're reading the master control, which should be safe except for
> USB hardware problems that can return an error.
>
> Regards,
>
>         Hans
>
> >>
> >> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >> ---
> >> Ricardo: this can be added to your uvc series. It avoids attempts to set
> >> inactive controls.
> >> ---
> >>  drivers/media/usb/uvc/uvc_ctrl.c | 28 +++++++++++++++++++++++++++-
> >>  drivers/media/usb/uvc/uvc_v4l2.c |  4 ++--
> >>  drivers/media/usb/uvc/uvcvideo.h |  3 ++-
> >>  3 files changed, 31 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
> >> index d9d4add1e813..6e7b904bc33d 100644
> >> --- a/drivers/media/usb/uvc/uvc_ctrl.c
> >> +++ b/drivers/media/usb/uvc/uvc_ctrl.c
> >> @@ -1047,10 +1047,18 @@ static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
> >>  }
> >>
> >>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> >> -                          bool read)
> >> +                          const struct v4l2_ext_controls *ctrls,
> >> +                          unsigned long ioctl)
> >>  {
> >> +       struct uvc_control_mapping *master_map = NULL;
> >> +       struct uvc_control *master_ctrl = NULL;
> >>         struct uvc_control_mapping *mapping;
> >>         struct uvc_control *ctrl;
> >> +       bool read = ioctl == VIDIOC_G_EXT_CTRLS;
> >> +       bool try = ioctl == VIDIOC_TRY_EXT_CTRLS;
> >> +       s32 val;
> >> +       int ret;
> >> +       int i;
> >>
> >>         if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
> >>                 return -EACCES;
> >> @@ -1065,6 +1073,24 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> >>         if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
> >>                 return -EACCES;
> >>
> >> +       if (read || try || !mapping->master_id)
> >> +               return 0;
> >> +
> >> +       for (i = ctrls->count - 1; i >= 0; i--)
> >> +               if (ctrls->controls[i].id == mapping->master_id)
> >> +                       return ctrls->controls[i].value ==
> >> +                                       mapping->master_manual ? 0 : -EACCES;
> >> +
> >> +       __uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
> >> +                          &master_ctrl, 0);
> >> +
> >> +       if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
> >> +               return 0;
> >> +
> >> +       ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
> >> +       if (ret >= 0 && val != mapping->master_manual)
> >> +               return -EACCES;
> >> +
> >>         return 0;
> >>  }
> >>
> >> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> >> index 12362e0f9870..e40db7ae18b1 100644
> >> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> >> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> >> @@ -803,8 +803,8 @@ static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
> >>         int ret = 0;
> >>
> >>         for (i = 0; i < ctrls->count; ++ctrl, ++i) {
> >> -               ret = uvc_ctrl_is_accessible(chain, ctrl->id,
> >> -                                           ioctl == VIDIOC_G_EXT_CTRLS);
> >> +               ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls,
> >> +                                           ioctl);
> >>                 if (ret)
> >>                         break;
> >>         }
> >> diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
> >> index aedb4d3d4db9..8849d7953767 100644
> >> --- a/drivers/media/usb/uvc/uvcvideo.h
> >> +++ b/drivers/media/usb/uvc/uvcvideo.h
> >> @@ -869,7 +869,8 @@ static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
> >>  int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
> >>  int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
> >>  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
> >> -                          bool read);
> >> +                          const struct v4l2_ext_controls *ctrls,
> >> +                          unsigned long ioctl);
> >>
> >>  int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
> >>                       struct uvc_xu_control_query *xqry);
> >> --
> >> 2.30.0
> >>
> >>
> >
> >
>


--
Ricardo Ribalda
diff mbox series

Patch

diff --git a/drivers/media/usb/uvc/uvc_ctrl.c b/drivers/media/usb/uvc/uvc_ctrl.c
index d9d4add1e813..6e7b904bc33d 100644
--- a/drivers/media/usb/uvc/uvc_ctrl.c
+++ b/drivers/media/usb/uvc/uvc_ctrl.c
@@ -1047,10 +1047,18 @@  static int uvc_query_v4l2_class(struct uvc_video_chain *chain, u32 req_id,
 }

 int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
-			   bool read)
+			   const struct v4l2_ext_controls *ctrls,
+			   unsigned long ioctl)
 {
+	struct uvc_control_mapping *master_map = NULL;
+	struct uvc_control *master_ctrl = NULL;
 	struct uvc_control_mapping *mapping;
 	struct uvc_control *ctrl;
+	bool read = ioctl == VIDIOC_G_EXT_CTRLS;
+	bool try = ioctl == VIDIOC_TRY_EXT_CTRLS;
+	s32 val;
+	int ret;
+	int i;

 	if (__uvc_query_v4l2_class(chain, v4l2_id, 0) >= 0)
 		return -EACCES;
@@ -1065,6 +1073,24 @@  int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
 	if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR) && !read)
 		return -EACCES;

+	if (read || try || !mapping->master_id)
+		return 0;
+
+	for (i = ctrls->count - 1; i >= 0; i--)
+		if (ctrls->controls[i].id == mapping->master_id)
+			return ctrls->controls[i].value ==
+					mapping->master_manual ? 0 : -EACCES;
+
+	__uvc_find_control(ctrl->entity, mapping->master_id, &master_map,
+			   &master_ctrl, 0);
+
+	if (!master_ctrl || !(master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR))
+		return 0;
+
+	ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val);
+	if (ret >= 0 && val != mapping->master_manual)
+		return -EACCES;
+
 	return 0;
 }

diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 12362e0f9870..e40db7ae18b1 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -803,8 +803,8 @@  static int uvc_ctrl_check_access(struct uvc_video_chain *chain,
 	int ret = 0;

 	for (i = 0; i < ctrls->count; ++ctrl, ++i) {
-		ret = uvc_ctrl_is_accessible(chain, ctrl->id,
-					    ioctl == VIDIOC_G_EXT_CTRLS);
+		ret = uvc_ctrl_is_accessible(chain, ctrl->id, ctrls,
+					    ioctl);
 		if (ret)
 			break;
 	}
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index aedb4d3d4db9..8849d7953767 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -869,7 +869,8 @@  static inline int uvc_ctrl_rollback(struct uvc_fh *handle)
 int uvc_ctrl_get(struct uvc_video_chain *chain, struct v4l2_ext_control *xctrl);
 int uvc_ctrl_set(struct uvc_fh *handle, struct v4l2_ext_control *xctrl);
 int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
-			   bool read);
+			   const struct v4l2_ext_controls *ctrls,
+			   unsigned long ioctl);

 int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
 		      struct uvc_xu_control_query *xqry);