Skip to content

Commit b248371

Browse files
committed
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 <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent cc85f7a commit b248371

File tree

1 file changed

+15
-6
lines changed

1 file changed

+15
-6
lines changed

sound/core/oss/pcm_oss.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
835835
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
836836
}
837837

838-
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
838+
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
839+
bool trylock)
839840
{
840841
struct snd_pcm_runtime *runtime = substream->runtime;
841842
struct snd_pcm_hw_params *params, *sparams;
@@ -849,7 +850,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
849850
struct snd_mask sformat_mask;
850851
struct snd_mask mask;
851852

852-
if (mutex_lock_interruptible(&runtime->oss.params_lock))
853+
if (trylock) {
854+
if (!(mutex_trylock(&runtime->oss.params_lock)))
855+
return -EAGAIN;
856+
} else if (mutex_lock_interruptible(&runtime->oss.params_lock))
853857
return -EINTR;
854858
sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
855859
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
10921096
if (asubstream == NULL)
10931097
asubstream = substream;
10941098
if (substream->runtime->oss.params) {
1095-
err = snd_pcm_oss_change_params(substream);
1099+
err = snd_pcm_oss_change_params(substream, false);
10961100
if (err < 0)
10971101
return err;
10981102
}
@@ -1132,7 +1136,7 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
11321136
return 0;
11331137
runtime = substream->runtime;
11341138
if (runtime->oss.params) {
1135-
err = snd_pcm_oss_change_params(substream);
1139+
err = snd_pcm_oss_change_params(substream, false);
11361140
if (err < 0)
11371141
return err;
11381142
}
@@ -2163,7 +2167,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
21632167
runtime = substream->runtime;
21642168

21652169
if (runtime->oss.params &&
2166-
(err = snd_pcm_oss_change_params(substream)) < 0)
2170+
(err = snd_pcm_oss_change_params(substream, false)) < 0)
21672171
return err;
21682172

21692173
info.fragsize = runtime->oss.period_bytes;
@@ -2804,7 +2808,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
28042808
return -EIO;
28052809

28062810
if (runtime->oss.params) {
2807-
if ((err = snd_pcm_oss_change_params(substream)) < 0)
2811+
/* use mutex_trylock() for params_lock for avoiding a deadlock
2812+
* between mmap_sem and params_lock taken by
2813+
* copy_from/to_user() in snd_pcm_oss_write/read()
2814+
*/
2815+
err = snd_pcm_oss_change_params(substream, true);
2816+
if (err < 0)
28082817
return err;
28092818
}
28102819
#ifdef CONFIG_SND_PCM_OSS_PLUGINS

0 commit comments

Comments
 (0)