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>
This commit is contained in:
Clemens Ladisch 2011-10-15 23:19:25 +02:00 committed by Takashi Iwai
parent 6bcbf64ace
commit 862a6244eb

View File

@ -459,7 +459,8 @@ static void kill_stream_urbs(struct ua101_stream *stream)
unsigned int i; unsigned int i;
for (i = 0; i < stream->queue_length; ++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) 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; struct usb_host_interface *alts;
if (!ua->intf[intf_index])
return;
alts = ua->intf[intf_index]->cur_altsetting; alts = ua->intf[intf_index]->cur_altsetting;
if (alts->desc.bAlternateSetting != 0) { if (alts->desc.bAlternateSetting != 0) {
int err = usb_set_interface(ua->dev, int err = usb_set_interface(ua->dev,
@ -1144,27 +1148,37 @@ static void free_stream_urbs(struct ua101_stream *stream)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < stream->queue_length; ++i) for (i = 0; i < stream->queue_length; ++i) {
kfree(stream->urbs[i]); kfree(stream->urbs[i]);
stream->urbs[i] = NULL;
}
} }
static void free_usb_related_resources(struct ua101 *ua, static void free_usb_related_resources(struct ua101 *ua,
struct usb_interface *interface) struct usb_interface *interface)
{ {
unsigned int i; unsigned int i;
struct usb_interface *intf;
mutex_lock(&ua->mutex);
free_stream_urbs(&ua->capture); free_stream_urbs(&ua->capture);
free_stream_urbs(&ua->playback); free_stream_urbs(&ua->playback);
mutex_unlock(&ua->mutex);
free_stream_buffers(ua, &ua->capture); free_stream_buffers(ua, &ua->capture);
free_stream_buffers(ua, &ua->playback); free_stream_buffers(ua, &ua->playback);
for (i = 0; i < ARRAY_SIZE(ua->intf); ++i) for (i = 0; i < ARRAY_SIZE(ua->intf); ++i) {
if (ua->intf[i]) { mutex_lock(&ua->mutex);
usb_set_intfdata(ua->intf[i], NULL); intf = ua->intf[i];
if (ua->intf[i] != interface) ua->intf[i] = NULL;
mutex_unlock(&ua->mutex);
if (intf) {
usb_set_intfdata(intf, NULL);
if (intf != interface)
usb_driver_release_interface(&ua101_driver, usb_driver_release_interface(&ua101_driver,
ua->intf[i]); intf);
} }
}
} }
static void ua101_card_free(struct snd_card *card) static void ua101_card_free(struct snd_card *card)