diff options
author | Clemens Ladisch <clemens@ladisch.de> | 2011-10-15 23:19:25 +0200 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2011-11-06 11:21:42 +0100 |
commit | 862a6244eb9f9f5123fe819454fcfcae0ee1f2f9 (patch) | |
tree | c2b1afb1818dd722c7026ebd0047972a32845d20 /sound | |
parent | 6bcbf64ace40d6833622d85701218a8bb7a4f207 (diff) |
ALSA: ua101: fix crash when unplugging
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>
Cc: 2.6.33+ <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/usb/misc/ua101.c | 28 |
1 files changed, 21 insertions, 7 deletions
diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 67bec7612442..c0609c210303 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -459,7 +459,8 @@ static void kill_stream_urbs(struct ua101_stream *stream) 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 @@ static void disable_iso_interface(struct ua101 *ua, unsigned int intf_index) { 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 @@ static void free_stream_urbs(struct ua101_stream *stream) { 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) |