ALSA: ua101: fix crash when unplugging
commit 862a6244eb9f9f5123fe819454fcfcae0ee1f2f9 upstream.
If the device is unplugged while running, it is possible for a PCM
device to be closed after the disconnect callback has returned. This
means that kill_stream_urb() and disable_iso_interface() would try to
access already-invalid or freed USB data structures.
The function free_usb_related_resources() was intended to prevent this,
but forgot to clear the affected variables.
Reported-and-tested-by: Olivier Courtay <olivier@courtay.org>
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c
index fb5d68f..96c381e 100644
--- a/sound/usb/misc/ua101.c
+++ b/sound/usb/misc/ua101.c
@@ -459,7 +459,8 @@
unsigned int i;
for (i = 0; i < stream->queue_length; ++i)
- usb_kill_urb(&stream->urbs[i]->urb);
+ if (stream->urbs[i])
+ usb_kill_urb(&stream->urbs[i]->urb);
}
static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index)
@@ -484,6 +485,9 @@
{
struct usb_host_interface *alts;
+ if (!ua->intf[intf_index])
+ return;
+
alts = ua->intf[intf_index]->cur_altsetting;
if (alts->desc.bAlternateSetting != 0) {
int err = usb_set_interface(ua->dev,
@@ -1144,27 +1148,37 @@
{
unsigned int i;
- for (i = 0; i < stream->queue_length; ++i)
+ for (i = 0; i < stream->queue_length; ++i) {
kfree(stream->urbs[i]);
+ stream->urbs[i] = NULL;
+ }
}
static void free_usb_related_resources(struct ua101 *ua,
struct usb_interface *interface)
{
unsigned int i;
+ struct usb_interface *intf;
+ mutex_lock(&ua->mutex);
free_stream_urbs(&ua->capture);
free_stream_urbs(&ua->playback);
+ mutex_unlock(&ua->mutex);
free_stream_buffers(ua, &ua->capture);
free_stream_buffers(ua, &ua->playback);
- for (i = 0; i < ARRAY_SIZE(ua->intf); ++i)
- if (ua->intf[i]) {
- usb_set_intfdata(ua->intf[i], NULL);
- if (ua->intf[i] != interface)
+ for (i = 0; i < ARRAY_SIZE(ua->intf); ++i) {
+ mutex_lock(&ua->mutex);
+ intf = ua->intf[i];
+ ua->intf[i] = NULL;
+ mutex_unlock(&ua->mutex);
+ if (intf) {
+ usb_set_intfdata(intf, NULL);
+ if (intf != interface)
usb_driver_release_interface(&ua101_driver,
- ua->intf[i]);
+ intf);
}
+ }
}
static void ua101_card_free(struct snd_card *card)