Race in snd-usb-audio driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Takashi and Clemens:

The snd-usb-audio driver has a race between close and disconnect.  A
patch I have been testing reliably triggers this race on my machine and
reveals a use-after-free bug.

This happens when a device is disconnected while being used for audio 
I/O.  Simply unplugging the device doesn't seem to trigger the problem, 
probably because it involves different timing.

In detail: When the device is unplugged, the USB core calls 
usb_audio_disconnect() -> snd_usb_audio_disconnect().

At the same time, the user program gets a poll failure and closes the
device: snd_usb_playback_close() -> snd_usb_pcm_close() ->
stop_endpoints() -> snd_usb_endpoint_sync_pending_stop() ->
wait_clear_urbs().  This routine waits until the endpoint's
outstanding URBs have completed, testing the snd_usb_endpoint structure
in a loop.

Meanwhile, back in the disconnect thread, snd_usb_audio_disconnect()  
calls snd_usb_endpoint_free() for each endpoint on the device.  This
routine unlinks the endpoint's URBs, calls wait_clear_urbs(), and then
deallocates the snd_usb_endpoint structure.

So there are two threads both running wait_clear_urbs() for the same 
endpoint, and one of them will deallocate the endpoint afterward.  If 
that thread happens to finish first, the other thread will dereference 
the deallocated structure.

Obviously there needs to be some sort of mutual exclusion between the
I/O pathways and the disconnect pathway.  I don't know what the right
solution is.  The patch below at least avoids this particular failure
scenario, but probably not correctly.

Can either of you write a proper fix?

Alan Stern



Index: 3.15/sound/usb/pcm.c
===================================================================
--- 3.15.orig/sound/usb/pcm.c
+++ 3.15/sound/usb/pcm.c
@@ -1211,7 +1211,8 @@ static int snd_usb_pcm_close(struct snd_
 	struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
 	struct snd_usb_substream *subs = &as->substream[direction];
 
-	stop_endpoints(subs, true);
+	if (!as->chip->shutdown)
+		stop_endpoints(subs, true);
 
 	if (!as->chip->shutdown && subs->interface >= 0) {
 		usb_set_interface(subs->dev, subs->interface, 0);

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux