Skip to content

Commit 117f7a2

Browse files
ribaldagregkh
authored andcommitted
media: uvcvideo: Remove dangling pointers
commit 221cd51 upstream. 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]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0fdd7cc commit 117f7a2

File tree

3 files changed

+71
-3
lines changed

3 files changed

+71
-3
lines changed

drivers/media/usb/uvc/uvc_ctrl.c

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,40 @@ static void uvc_ctrl_send_slave_event(struct uvc_video_chain *chain,
14171417
uvc_ctrl_send_event(chain, handle, ctrl, mapping, val, changes);
14181418
}
14191419

1420+
static void uvc_ctrl_set_handle(struct uvc_fh *handle, struct uvc_control *ctrl,
1421+
struct uvc_fh *new_handle)
1422+
{
1423+
lockdep_assert_held(&handle->chain->ctrl_mutex);
1424+
1425+
if (new_handle) {
1426+
if (ctrl->handle)
1427+
dev_warn_ratelimited(&handle->stream->dev->udev->dev,
1428+
"UVC non compliance: Setting an async control with a pending operation.");
1429+
1430+
if (new_handle == ctrl->handle)
1431+
return;
1432+
1433+
if (ctrl->handle) {
1434+
WARN_ON(!ctrl->handle->pending_async_ctrls);
1435+
if (ctrl->handle->pending_async_ctrls)
1436+
ctrl->handle->pending_async_ctrls--;
1437+
}
1438+
1439+
ctrl->handle = new_handle;
1440+
handle->pending_async_ctrls++;
1441+
return;
1442+
}
1443+
1444+
/* Cannot clear the handle for a control not owned by us.*/
1445+
if (WARN_ON(ctrl->handle != handle))
1446+
return;
1447+
1448+
ctrl->handle = NULL;
1449+
if (WARN_ON(!handle->pending_async_ctrls))
1450+
return;
1451+
handle->pending_async_ctrls--;
1452+
}
1453+
14201454
void uvc_ctrl_status_event(struct uvc_video_chain *chain,
14211455
struct uvc_control *ctrl, const u8 *data)
14221456
{
@@ -1427,7 +1461,8 @@ void uvc_ctrl_status_event(struct uvc_video_chain *chain,
14271461
mutex_lock(&chain->ctrl_mutex);
14281462

14291463
handle = ctrl->handle;
1430-
ctrl->handle = NULL;
1464+
if (handle)
1465+
uvc_ctrl_set_handle(handle, ctrl, NULL);
14311466

14321467
list_for_each_entry(mapping, &ctrl->info.mappings, list) {
14331468
s32 value = __uvc_ctrl_get_value(mapping, data);
@@ -1698,7 +1733,7 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev,
16981733

16991734
if (!rollback && handle &&
17001735
ctrl->info.flags & UVC_CTRL_FLAG_ASYNCHRONOUS)
1701-
ctrl->handle = handle;
1736+
uvc_ctrl_set_handle(handle, ctrl, handle);
17021737
}
17031738

17041739
return 0;
@@ -2552,6 +2587,30 @@ int uvc_ctrl_init_device(struct uvc_device *dev)
25522587
return 0;
25532588
}
25542589

2590+
void uvc_ctrl_cleanup_fh(struct uvc_fh *handle)
2591+
{
2592+
struct uvc_entity *entity;
2593+
2594+
mutex_lock(&handle->chain->ctrl_mutex);
2595+
2596+
if (!handle->pending_async_ctrls) {
2597+
mutex_unlock(&handle->chain->ctrl_mutex);
2598+
return;
2599+
}
2600+
2601+
list_for_each_entry(entity, &handle->chain->dev->entities, list) {
2602+
unsigned int i;
2603+
for (i = 0; i < entity->ncontrols; ++i) {
2604+
if (entity->controls[i].handle != handle)
2605+
continue;
2606+
uvc_ctrl_set_handle(handle, &entity->controls[i], NULL);
2607+
}
2608+
}
2609+
2610+
WARN_ON(handle->pending_async_ctrls);
2611+
mutex_unlock(&handle->chain->ctrl_mutex);
2612+
}
2613+
25552614
/*
25562615
* Cleanup device controls.
25572616
*/

drivers/media/usb/uvc/uvc_v4l2.c

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

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

605+
uvc_ctrl_cleanup_fh(handle);
606+
605607
/* Only free resources if this is a privileged handle. */
606608
if (uvc_has_privileges(handle))
607609
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
@@ -472,7 +472,11 @@ struct uvc_video_chain {
472472
struct uvc_entity *processing; /* Processing unit */
473473
struct uvc_entity *selector; /* Selector unit */
474474

475-
struct mutex ctrl_mutex; /* Protects ctrl.info */
475+
struct mutex ctrl_mutex; /*
476+
* Protects ctrl.info,
477+
* ctrl.handle and
478+
* uvc_fh.pending_async_ctrls
479+
*/
476480

477481
struct v4l2_prio_state prio; /* V4L2 priority state */
478482
u32 caps; /* V4L2 chain-wide caps */
@@ -724,6 +728,7 @@ struct uvc_fh {
724728
struct uvc_video_chain *chain;
725729
struct uvc_streaming *stream;
726730
enum uvc_handle_state state;
731+
unsigned int pending_async_ctrls;
727732
};
728733

729734
struct uvc_driver {
@@ -907,6 +912,8 @@ int uvc_ctrl_is_accessible(struct uvc_video_chain *chain, u32 v4l2_id,
907912
int uvc_xu_ctrl_query(struct uvc_video_chain *chain,
908913
struct uvc_xu_control_query *xqry);
909914

915+
void uvc_ctrl_cleanup_fh(struct uvc_fh *handle);
916+
910917
/* Utility functions */
911918
void uvc_simplify_fraction(u32 *numerator, u32 *denominator,
912919
unsigned int n_terms, unsigned int threshold);

0 commit comments

Comments
 (0)