From: Greg Kroah-Hartman Date: Fri, 10 Mar 2023 12:00:47 +0000 (+0100) Subject: 6.1-stable patches X-Git-Tag: v6.1.17~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fe5b38558405e29adac7c3c54c836943be3674d2;p=thirdparty%2Fkernel%2Fstable-queue.git 6.1-stable patches added patches: media-uvcvideo-fix-race-condition-with-usb_kill_urb.patch --- diff --git a/queue-6.1/media-uvcvideo-fix-race-condition-with-usb_kill_urb.patch b/queue-6.1/media-uvcvideo-fix-race-condition-with-usb_kill_urb.patch new file mode 100644 index 00000000000..f9e71d54a51 --- /dev/null +++ b/queue-6.1/media-uvcvideo-fix-race-condition-with-usb_kill_urb.patch @@ -0,0 +1,140 @@ +From 619d9b710cf06f7a00a17120ca92333684ac45a8 Mon Sep 17 00:00:00 2001 +From: Ricardo Ribalda +Date: Thu, 5 Jan 2023 15:31:29 +0100 +Subject: media: uvcvideo: Fix race condition with usb_kill_urb + +From: Ricardo Ribalda + +commit 619d9b710cf06f7a00a17120ca92333684ac45a8 upstream. + +usb_kill_urb warranties that all the handlers are finished when it +returns, but does not protect against threads that might be handling +asynchronously the urb. + +For UVC, the function uvc_ctrl_status_event_async() takes care of +control changes asynchronously. + +If the code is executed in the following order: + +CPU 0 CPU 1 +===== ===== +uvc_status_complete() + uvc_status_stop() +uvc_ctrl_status_event_work() + uvc_status_start() -> FAIL + +Then uvc_status_start will keep failing and this error will be shown: + +<4>[ 5.540139] URB 0000000000000000 submitted while active +drivers/usb/core/urb.c:378 usb_submit_urb+0x4c3/0x528 + +Let's improve the current situation, by not re-submiting the urb if +we are stopping the status event. Also process the queued work +(if any) during stop. + +CPU 0 CPU 1 +===== ===== +uvc_status_complete() + uvc_status_stop() + uvc_status_start() +uvc_ctrl_status_event_work() -> FAIL + +Hopefully, with the usb layer protection this should be enough to cover +all the cases. + +Cc: stable@vger.kernel.org +Fixes: e5225c820c05 ("media: uvcvideo: Send a control event when a Control Change interrupt arrives") +Reviewed-by: Yunke Cao +Signed-off-by: Ricardo Ribalda +Reviewed-by: Laurent Pinchart +Signed-off-by: Laurent Pinchart +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/usb/uvc/uvc_ctrl.c | 5 +++++ + drivers/media/usb/uvc/uvc_status.c | 37 +++++++++++++++++++++++++++++++++++++ + drivers/media/usb/uvc/uvcvideo.h | 1 + + 3 files changed, 43 insertions(+) + +--- a/drivers/media/usb/uvc/uvc_ctrl.c ++++ b/drivers/media/usb/uvc/uvc_ctrl.c +@@ -6,6 +6,7 @@ + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ + ++#include + #include + #include + #include +@@ -1509,6 +1510,10 @@ static void uvc_ctrl_status_event_work(s + + uvc_ctrl_status_event(w->chain, w->ctrl, w->data); + ++ /* The barrier is needed to synchronize with uvc_status_stop(). */ ++ if (smp_load_acquire(&dev->flush_status)) ++ return; ++ + /* Resubmit the URB. */ + w->urb->interval = dev->int_ep->desc.bInterval; + ret = usb_submit_urb(w->urb, GFP_KERNEL); +--- a/drivers/media/usb/uvc/uvc_status.c ++++ b/drivers/media/usb/uvc/uvc_status.c +@@ -6,6 +6,7 @@ + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ + ++#include + #include + #include + #include +@@ -309,5 +310,41 @@ int uvc_status_start(struct uvc_device * + + void uvc_status_stop(struct uvc_device *dev) + { ++ struct uvc_ctrl_work *w = &dev->async_ctrl; ++ ++ /* ++ * Prevent the asynchronous control handler from requeing the URB. The ++ * barrier is needed so the flush_status change is visible to other ++ * CPUs running the asynchronous handler before usb_kill_urb() is ++ * called below. ++ */ ++ smp_store_release(&dev->flush_status, true); ++ ++ /* ++ * Cancel any pending asynchronous work. If any status event was queued, ++ * process it synchronously. ++ */ ++ if (cancel_work_sync(&w->work)) ++ uvc_ctrl_status_event(w->chain, w->ctrl, w->data); ++ ++ /* Kill the urb. */ + usb_kill_urb(dev->int_urb); ++ ++ /* ++ * The URB completion handler may have queued asynchronous work. This ++ * won't resubmit the URB as flush_status is set, but it needs to be ++ * cancelled before returning or it could then race with a future ++ * uvc_status_start() call. ++ */ ++ if (cancel_work_sync(&w->work)) ++ uvc_ctrl_status_event(w->chain, w->ctrl, w->data); ++ ++ /* ++ * From this point, there are no events on the queue and the status URB ++ * is dead. No events will be queued until uvc_status_start() is called. ++ * The barrier is needed to make sure that flush_status is visible to ++ * uvc_ctrl_status_event_work() when uvc_status_start() will be called ++ * again. ++ */ ++ smp_store_release(&dev->flush_status, false); + } +--- a/drivers/media/usb/uvc/uvcvideo.h ++++ b/drivers/media/usb/uvc/uvcvideo.h +@@ -558,6 +558,7 @@ struct uvc_device { + /* Status Interrupt Endpoint */ + struct usb_host_endpoint *int_ep; + struct urb *int_urb; ++ bool flush_status; + u8 *status; + struct input_dev *input; + char input_phys[64]; diff --git a/queue-6.1/series b/queue-6.1/series index 78a84ec43ea..0fa60d40b75 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -191,3 +191,4 @@ drm-display-dp_mst-fix-down-message-handling-after-a-packet-reception-error.patc drm-display-dp_mst-fix-payload-addition-on-a-disconnected-sink.patch drm-i915-dp_mst-add-the-mst-topology-state-for-modesetted-crtcs.patch drm-i915-fix-system-suspend-without-fbdev-being-initialized.patch +media-uvcvideo-fix-race-condition-with-usb_kill_urb.patch