Skip to content

Commit 221cd51

Browse files
ribaldamchehab
authored andcommitted
media: uvcvideo: Remove dangling pointers
When an async control is written, we copy a pointer to the file handle that started the operation. That pointer will be used when the device is done. Which could be anytime in the future. If the user closes that file descriptor, its structure will be freed, and there will be one dangling pointer per pending async control, that the driver will try to use. Clean all the dangling pointers during release(). To avoid adding a performance penalty in the most common case (no async operation), a counter has been introduced with some logic to make sure that it is properly handled. Cc: [email protected] Fixes: e5225c8 ("media: uvcvideo: Send a control event when a Control Change interrupt arrives") Reviewed-by: Hans de Goede <[email protected]> Signed-off-by: Ricardo Ribalda <[email protected]> Reviewed-by: Laurent Pinchart <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Laurent Pinchart <[email protected]> Signed-off-by: Mauro Carvalho Chehab <[email protected]>
1 parent 04d3398 commit 221cd51

File tree

3 files changed

+67
-3
lines changed

3 files changed

+67
-3
lines changed

drivers/media/usb/uvc/uvc_ctrl.c

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1579,6 +1579,40 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
15791579
uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
15801580
}
15811581

1582+
static void uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl,
1583+
struct uvc_fh *new_handle)
1584+
{
1585+
lockdep_assert_held(&handle->chain->ctrl_mutex);
1586+
1587+
if (new_handle) {
1588+
if (ctrl->handle)
1589+
dev_warn_ratelimited(&handle->stream->dev->udev->dev,
1590+
"UVC non compliance: Setting an async control with a pending operation.");
1591+
1592+
if (new_handle == ctrl->handle)
1593+
return;
1594+
1595+
if (ctrl->handle) {
1596+
WARN_ON(!ctrl->handle->pending_async_ctrls);
1597+
if (ctrl->handle->pending_async_ctrls)
1598+
ctrl->handle->pending_async_ctrls--;
1599+
}
1600+
1601+
ctrl->handle = new_handle;
1602+
handle->pending_async_ctrls++;
1603+
return;
1604+
}
1605+
1606+
/* Cannot clear the handle for a control not owned by us.*/
1607+
if (WARN_ON(ctrl->handle != handle))
1608+
return;
1609+
1610+
ctrl->handle = NULL;
1611+
if (WARN_ON(!handle->pending_async_ctrls))
1612+
return;
1613+
handle->pending_async_ctrls--;
1614+
}
1615+
15821616
void uvc_ctrl_status_event(struct uvc_video_chain *chain,
15831617
struct uvc_control *ctrl, const u8 *data)
15841618
{
@@ -1589,7 +1623,8 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,
15891623
mutex_lock(&chain->ctrl_mutex);
15901624

15911625
handle = ctrl->handle;
1592-
ctrl->handle = NULL;
1626+
if (handle)
1627+
uvc_ctrl_set_handle(handle, ctrl, NULL);
15931628

15941629
list_for_each_entry(mapping, &ctrl->info.mappings, list) {
15951630
s32 value = __uvc_ctrl_get_value(mapping, data);
@@ -1863,7 +1898,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
18631898

18641899
if (!rollback && handle &&
18651900
ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
1866-
ctrl->handle = handle;
1901+
uvc_ctrl_set_handle(handle, ctrl, handle);
18671902
}
18681903

18691904
return 0;
@@ -2772,6 +2807,26 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
27722807
return 0;
27732808
}
27742809

2810+
void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
2811+
{
2812+
struct uvc_entity *entity;
2813+
2814+
guard(mutex)(&handle->chain->ctrl_mutex);
2815+
2816+
if (!handle->pending_async_ctrls)
2817+
return;
2818+
2819+
list_for_each_entry(entity, &handle->chain->dev->entities, list) {
2820+
for (unsigned int i = 0; i < entity->ncontrols; ++i) {
2821+
if (entity->controls[i].handle != handle)
2822+
continue;
2823+
uvc_ctrl_set_handle(handle, &entity->controls[i], NULL);
2824+
}
2825+
}
2826+
2827+
WARN_ON(handle->pending_async_ctrls);
2828+
}
2829+
27752830
/*
27762831
* Cleanup device controls.
27772832
*/

drivers/media/usb/uvc/uvc_v4l2.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,8 @@ static int uvc_v4l2_release(struct file *file)
671671

672672
uvc_dbg(stream->dev, CALLS, "%s\n", __func__);
673673

674+
uvc_ctrl_cleanup_fh(handle);
675+
674676
/* Only free resources if this is a privileged handle. */
675677
if (uvc_has_privileges(handle))
676678
uvc_queue_release(&stream->queue);

drivers/media/usb/uvc/uvcvideo.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,11 @@ struct uvc_video_chain {
338338
struct uvc_entity *processing; /* Processing unit */
339339
struct uvc_entity *selector; /* Selector unit */
340340

341-
struct mutex ctrl_mutex; /* Protects ctrl.info */
341+
struct mutex ctrl_mutex; /*
342+
* Protects ctrl.info,
343+
* ctrl.handle and
344+
* uvc_fh.pending_async_ctrls
345+
*/
342346

343347
struct v4l2_prio_state prio; /* V4L2 priority state */
344348
u32 caps; /* V4L2 chain-wide caps */
@@ -613,6 +617,7 @@ struct uvc_fh {
613617
struct uvc_video_chain *chain;
614618
struct uvc_streaming *stream;
615619
enum uvc_handle_state state;
620+
unsigned int pending_async_ctrls;
616621
};
617622

618623
struct uvc_driver {
@@ -798,6 +803,8 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
798803
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
799804
struct uvc_xu_control_query *xqry);
800805

806+
void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
807+
801808
/* Utility functions */
802809
struct usb_host_endpoint *uvc_find_endpoint(struct usb_host_interface *alts,
803810
u8 epaddr);

0 commit comments

Comments
 (0)