[alsa-devel] usb audio race at disconnect time

Takashi Iwai tiwai at suse.de
Fri Oct 12 16:31:26 CEST 2012


At Thu, 11 Oct 2012 18:41:52 +0200,
Takashi Iwai wrote:
> 
> [Added Daniel and Clemens in the loop]
> 
> At Thu, 11 Oct 2012 17:17:59 +0200,
> Matthieu CASTET wrote:
> > 
> > Hi,
> > 
> > while doing some monkey tests on a product we found races in usb audio code when
> > the device in unplugged from usb (on linus master tree).
> > 
> > This can be reproduced with usb_audio_show_race.diff and CONFIG_DEBUG_SLAB.
> > With this patch, start a stream :
> > # arecord -D hw:0 -r 44100 -c 2  -f S16_LE > /dev/null
> > you will see the kernel log : "in snd_usb_hw_params sleeping"
> > Unplug the device before "in snd_usb_hw_params sleeping exit", and you will see
> > an oops in snd_pcm_hw_params
> > 
> > 
> > Instead of using CONFIG_DEBUG_SLAB, usb_audio_show_use_after_free.diff can show
> > use after free by setting usb_device pointer to NULL in
> > snd_usb_audio_disconnect. [1]
> > 
> > 
> > In order to protect from all the races, before using any usb_device we need to
> > check if it is not freed.
> > 
> > What's the best way to do that ?
> > 
> > A trival fix would be to take a mutex in all snd_pcm_ops callback that access usb :
> > 
> > {
> > mutex_lock(&chip->shutdown_mutex);
> > if (!chip->dev) {
> > 	mutex_unlock(&chip->shutdown_mutex);
> > 	return -ENODEV;
> > }
> > [...]
> > mutex_unlock(&chip->shutdown_mutex);
> > }
> > 
> > 
> > But that will serialize all snd_pcm_ops callbacks.
> 
> We had already some protections but they don't cover the recent code
> rewrites at all, so this bad result for now.
> 
> Yes, the easiest and maybe sane way would be to put the mutex in each
> ops except for trigger.  open, close, hw_params, hw_free and prepare
> are ops that don't need much parallelism, so this can be protected
> well.  Or, we may introduce a new mutex per stream if we really want
> parallel operations, but I don't think it's worth.
> 
> The trigger callback is an atomic ops, so it won't need the check.
> Maybe the spinlock is needed to avoid the race in
> snd_usb_stream_disconnect().
> 
> > Another solution could be to use refcounting or rwlock patern :
> > - snd_pcm_ops callbacks call rdlock_trylock/rdlock_unlock
> > - usb_driver disconnect callback take rwlock_lock and never release it
> 
> I don't think this is needed.
> 
> So... the below is a quick hack I did without testing at all.
> Hopefully this can give some advance.

I checked this issue again actually with a real machine, and tried a
bit better version.  I'm going to send a series of fix patches.


Takashi


More information about the Alsa-devel mailing list