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
1 changed files with 21 additions and 7 deletions

View File

@ -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)