forked from Minki/linux
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:
parent
6bcbf64ace
commit
862a6244eb
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user