| From 862a6244eb9f9f5123fe819454fcfcae0ee1f2f9 Mon Sep 17 00:00:00 2001 |
| From: Clemens Ladisch <clemens@ladisch.de> |
| Date: Sat, 15 Oct 2011 23:19:25 +0200 |
| Subject: ALSA: ua101: fix crash when unplugging |
| |
| From: Clemens Ladisch <clemens@ladisch.de> |
| |
| 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> |
| |
| --- |
| sound/usb/misc/ua101.c | 28 +++++++++++++++++++++------- |
| 1 file changed, 21 insertions(+), 7 deletions(-) |
| |
| --- a/sound/usb/misc/ua101.c |
| +++ b/sound/usb/misc/ua101.c |
| @@ -459,7 +459,8 @@ static void kill_stream_urbs(struct ua10 |
| 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 |
| { |
| 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 ua10 |
| { |
| 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) |