Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

media: uvcvideo: Fix race condition with usb_kill_urb

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 <yunkec@chromium.org>
Signed-off-by: Ricardo Ribalda <ribalda@chromium.org>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

authored by

Ricardo Ribalda and committed by
Laurent Pinchart
619d9b71 716c3304

+43
+5
drivers/media/usb/uvc/uvc_ctrl.c
··· 6 6 * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 7 */ 8 8 9 + #include <asm/barrier.h> 9 10 #include <linux/bitops.h> 10 11 #include <linux/kernel.h> 11 12 #include <linux/list.h> ··· 1571 1570 int ret; 1572 1571 1573 1572 uvc_ctrl_status_event(w->chain, w->ctrl, w->data); 1573 + 1574 + /* The barrier is needed to synchronize with uvc_status_stop(). */ 1575 + if (smp_load_acquire(&dev->flush_status)) 1576 + return; 1574 1577 1575 1578 /* Resubmit the URB. */ 1576 1579 w->urb->interval = dev->int_ep->desc.bInterval;
+37
drivers/media/usb/uvc/uvc_status.c
··· 6 6 * Laurent Pinchart (laurent.pinchart@ideasonboard.com) 7 7 */ 8 8 9 + #include <asm/barrier.h> 9 10 #include <linux/kernel.h> 10 11 #include <linux/input.h> 11 12 #include <linux/slab.h> ··· 312 311 313 312 void uvc_status_stop(struct uvc_device *dev) 314 313 { 314 + struct uvc_ctrl_work *w = &dev->async_ctrl; 315 + 316 + /* 317 + * Prevent the asynchronous control handler from requeing the URB. The 318 + * barrier is needed so the flush_status change is visible to other 319 + * CPUs running the asynchronous handler before usb_kill_urb() is 320 + * called below. 321 + */ 322 + smp_store_release(&dev->flush_status, true); 323 + 324 + /* 325 + * Cancel any pending asynchronous work. If any status event was queued, 326 + * process it synchronously. 327 + */ 328 + if (cancel_work_sync(&w->work)) 329 + uvc_ctrl_status_event(w->chain, w->ctrl, w->data); 330 + 331 + /* Kill the urb. */ 315 332 usb_kill_urb(dev->int_urb); 333 + 334 + /* 335 + * The URB completion handler may have queued asynchronous work. This 336 + * won't resubmit the URB as flush_status is set, but it needs to be 337 + * cancelled before returning or it could then race with a future 338 + * uvc_status_start() call. 339 + */ 340 + if (cancel_work_sync(&w->work)) 341 + uvc_ctrl_status_event(w->chain, w->ctrl, w->data); 342 + 343 + /* 344 + * From this point, there are no events on the queue and the status URB 345 + * is dead. No events will be queued until uvc_status_start() is called. 346 + * The barrier is needed to make sure that flush_status is visible to 347 + * uvc_ctrl_status_event_work() when uvc_status_start() will be called 348 + * again. 349 + */ 350 + smp_store_release(&dev->flush_status, false); 316 351 }
+1
drivers/media/usb/uvc/uvcvideo.h
··· 577 577 struct usb_host_endpoint *int_ep; 578 578 struct urb *int_urb; 579 579 struct uvc_status *status; 580 + bool flush_status; 580 581 581 582 struct input_dev *input; 582 583 char input_phys[64];