ALSA: usb-audio: fix oops due to cleanup race when disconnecting
When a USB audio device is disconnected, snd_usb_audio_disconnect()
kills all audio URBs. At the same time, the application, after being
notified of the disconnection, might close the device, in which case
ALSA calls the .hw_free callback, which should free the URBs too.
Commit de1b8b93a0ba "[ALSA] Fix hang-up at disconnection of usb-audio"
prevented snd_usb_hw_free() from freeing the URBs to avoid a hang that
resulted from this race, but this introduced another race because the
URB callbacks could now be executed after snd_usb_hw_free() has
returned, and try to access already freed data.
Fix the first race by introducing a mutex to serialize the disconnect
callback and all PCM callbacks that manage URBs (hw_free and hw_params).
Reported-and-tested-by: Pierre-Louis Bossart <pierre-louis.bossart@intel.com>
Cc: <stable@kernel.org>
[CL: also serialize hw_params callback]
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 4132522..e3f6805 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -361,6 +361,7 @@
}
if (changed) {
+ mutex_lock(&subs->stream->chip->shutdown_mutex);
/* format changed */
snd_usb_release_substream_urbs(subs, 0);
/* influenced: period_bytes, channels, rate, format, */
@@ -368,6 +369,7 @@
params_rate(hw_params),
snd_pcm_format_physical_width(params_format(hw_params)) *
params_channels(hw_params));
+ mutex_unlock(&subs->stream->chip->shutdown_mutex);
}
return ret;
@@ -385,8 +387,9 @@
subs->cur_audiofmt = NULL;
subs->cur_rate = 0;
subs->period_bytes = 0;
- if (!subs->stream->chip->shutdown)
- snd_usb_release_substream_urbs(subs, 0);
+ mutex_lock(&subs->stream->chip->shutdown_mutex);
+ snd_usb_release_substream_urbs(subs, 0);
+ mutex_unlock(&subs->stream->chip->shutdown_mutex);
return snd_pcm_lib_free_vmalloc_buffer(substream);
}