ALSA: pcm: Fix potential deadlock in OSS emulation
There are potential deadlocks in PCM OSS emulation code while accessing read/write and mmap concurrently. This comes from the infamous mmap_sem usage in copy_from/to_user(). Namely, snd_pcm_oss_write() -> &runtime->oss.params_lock -> copy_to_user() -> &mm->mmap_sem mmap() -> &mm->mmap_sem -> snd_pcm_oss_mmap() -> &runtime->oss.params_lock Since we can't avoid taking params_lock from mmap code path, use trylock variant and aborts with -EAGAIN as a workaround of this AB/BA deadlock. BugLink: http://lkml.kernel.org/r/CACT4Y+bVrBKDG0G2_AcUgUQa+X91VKTeS4v+wN7BSHwHtqn3kQ@mail.gmail.com Reported-by: Dmitry Vyukov <dvyukov@google.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
cc85f7a634
commit
b248371628
|
@ -835,7 +835,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
|
|||
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
|
||||
}
|
||||
|
||||
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
|
||||
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
|
||||
bool trylock)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_pcm_hw_params *params, *sparams;
|
||||
|
@ -849,7 +850,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
|
|||
struct snd_mask sformat_mask;
|
||||
struct snd_mask mask;
|
||||
|
||||
if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||
if (trylock) {
|
||||
if (!(mutex_trylock(&runtime->oss.params_lock)))
|
||||
return -EAGAIN;
|
||||
} else if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||
return -EINTR;
|
||||
sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
|
||||
params = kmalloc(sizeof(*params), GFP_KERNEL);
|
||||
|
@ -1092,7 +1096,7 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
|
|||
if (asubstream == NULL)
|
||||
asubstream = substream;
|
||||
if (substream->runtime->oss.params) {
|
||||
err = snd_pcm_oss_change_params(substream);
|
||||
err = snd_pcm_oss_change_params(substream, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
@ -1132,7 +1136,7 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
runtime = substream->runtime;
|
||||
if (runtime->oss.params) {
|
||||
err = snd_pcm_oss_change_params(substream);
|
||||
err = snd_pcm_oss_change_params(substream, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
@ -2163,7 +2167,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
|
|||
runtime = substream->runtime;
|
||||
|
||||
if (runtime->oss.params &&
|
||||
(err = snd_pcm_oss_change_params(substream)) < 0)
|
||||
(err = snd_pcm_oss_change_params(substream, false)) < 0)
|
||||
return err;
|
||||
|
||||
info.fragsize = runtime->oss.period_bytes;
|
||||
|
@ -2804,7 +2808,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
|
|||
return -EIO;
|
||||
|
||||
if (runtime->oss.params) {
|
||||
if ((err = snd_pcm_oss_change_params(substream)) < 0)
|
||||
/* use mutex_trylock() for params_lock for avoiding a deadlock
|
||||
* between mmap_sem and params_lock taken by
|
||||
* copy_from/to_user() in snd_pcm_oss_write/read()
|
||||
*/
|
||||
err = snd_pcm_oss_change_params(substream, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
|
||||
|
|
Loading…
Reference in New Issue