Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 862a6244 authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Takashi Iwai
Browse files

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: default avatarOlivier Courtay <olivier@courtay.org>
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Cc: 2.6.33+ <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 6bcbf64a
Loading
Loading
Loading
Loading
+21 −7
Original line number Diff line number Diff line
@@ -459,6 +459,7 @@ static void kill_stream_urbs(struct ua101_stream *stream)
	unsigned int i;

	for (i = 0; i < stream->queue_length; ++i)
		if (stream->urbs[i])
			usb_kill_urb(&stream->urbs[i]->urb);
}

@@ -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,26 +1148,36 @@ 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);
		}
	}
}