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

media: gspca: Kill all URBs before releasing any of them

Some subdrivers access the gspca_dev->urb array in the completion handler.
To prevent use-after-free (actually, NULL dereferences) we need to
synchronously kill all the URBs before we release them.

In particular, this is currently the case for drivers such
as sn9c20x and sonixj, which access the gspca_dev->urb[0]
in the context of completion handler for *any* of the URBs.

This commit changes the destroy_urb implementation, so it kills
all URBs first, and then proceed to set the URBs to NULL in the
array and release them.

Signed-off-by: Ezequiel Garcia <ezequiel@collabora.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>

authored by

Ezequiel Garcia and committed by
Mauro Carvalho Chehab
6992effe df95e82f

+11 -4
+11 -4
drivers/media/usb/gspca/gspca.c
··· 472 472 unsigned int i; 473 473 474 474 gspca_dbg(gspca_dev, D_STREAM, "kill transfer\n"); 475 + 476 + /* Killing all URBs guarantee that no URB completion 477 + * handler is running. Therefore, there shouldn't 478 + * be anyone trying to access gspca_dev->urb[i] 479 + */ 480 + for (i = 0; i < MAX_NURBS; i++) 481 + usb_kill_urb(gspca_dev->urb[i]); 482 + 483 + gspca_dbg(gspca_dev, D_STREAM, "releasing urbs\n"); 475 484 for (i = 0; i < MAX_NURBS; i++) { 476 485 urb = gspca_dev->urb[i]; 477 - if (urb == NULL) 478 - break; 479 - 486 + if (!urb) 487 + continue; 480 488 gspca_dev->urb[i] = NULL; 481 - usb_kill_urb(urb); 482 489 usb_free_coherent(gspca_dev->dev, 483 490 urb->transfer_buffer_length, 484 491 urb->transfer_buffer,