diff options
| author | Takashi Iwai <tiwai@suse.de> | 2016-01-31 04:32:37 -0500 |
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2016-02-01 06:23:29 -0500 |
| commit | b248371628aad599a48540962f6b85a21a8a0c3f (patch) | |
| tree | b53a2028b511afaa11cacf8eecbb09cd43aaa190 | |
| parent | cc85f7a634cfaf9f0713c6aa06d08817424db37a (diff) | |
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>
| -rw-r--r-- | sound/core/oss/pcm_oss.c | 21 |
1 files changed, 15 insertions, 6 deletions
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 0e73d03b30e3..ebc9fdfe64df 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c | |||
| @@ -835,7 +835,8 @@ static int choose_rate(struct snd_pcm_substream *substream, | |||
| 835 | return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL); | 835 | return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL); |
| 836 | } | 836 | } |
| 837 | 837 | ||
| 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) | ||
| 839 | { | 840 | { |
| 840 | struct snd_pcm_runtime *runtime = substream->runtime; | 841 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 841 | struct snd_pcm_hw_params *params, *sparams; | 842 | struct snd_pcm_hw_params *params, *sparams; |
| @@ -849,7 +850,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) | |||
| 849 | struct snd_mask sformat_mask; | 850 | struct snd_mask sformat_mask; |
| 850 | struct snd_mask mask; | 851 | struct snd_mask mask; |
| 851 | 852 | ||
| 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)) | ||
| 853 | return -EINTR; | 857 | return -EINTR; |
| 854 | sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL); | 858 | sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL); |
| 855 | params = kmalloc(sizeof(*params), GFP_KERNEL); | 859 | 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 | |||
| 1092 | if (asubstream == NULL) | 1096 | if (asubstream == NULL) |
| 1093 | asubstream = substream; | 1097 | asubstream = substream; |
| 1094 | if (substream->runtime->oss.params) { | 1098 | if (substream->runtime->oss.params) { |
| 1095 | err = snd_pcm_oss_change_params(substream); | 1099 | err = snd_pcm_oss_change_params(substream, false); |
| 1096 | if (err < 0) | 1100 | if (err < 0) |
| 1097 | return err; | 1101 | return err; |
| 1098 | } | 1102 | } |
| @@ -1132,7 +1136,7 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream) | |||
| 1132 | return 0; | 1136 | return 0; |
| 1133 | runtime = substream->runtime; | 1137 | runtime = substream->runtime; |
| 1134 | if (runtime->oss.params) { | 1138 | if (runtime->oss.params) { |
| 1135 | err = snd_pcm_oss_change_params(substream); | 1139 | err = snd_pcm_oss_change_params(substream, false); |
| 1136 | if (err < 0) | 1140 | if (err < 0) |
| 1137 | return err; | 1141 | return err; |
| 1138 | } | 1142 | } |
| @@ -2163,7 +2167,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre | |||
| 2163 | runtime = substream->runtime; | 2167 | runtime = substream->runtime; |
| 2164 | 2168 | ||
| 2165 | if (runtime->oss.params && | 2169 | if (runtime->oss.params && |
| 2166 | (err = snd_pcm_oss_change_params(substream)) < 0) | 2170 | (err = snd_pcm_oss_change_params(substream, false)) < 0) |
| 2167 | return err; | 2171 | return err; |
| 2168 | 2172 | ||
| 2169 | info.fragsize = runtime->oss.period_bytes; | 2173 | info.fragsize = runtime->oss.period_bytes; |
| @@ -2804,7 +2808,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area) | |||
| 2804 | return -EIO; | 2808 | return -EIO; |
| 2805 | 2809 | ||
| 2806 | if (runtime->oss.params) { | 2810 | 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) | ||
| 2808 | return err; | 2817 | return err; |
| 2809 | } | 2818 | } |
| 2810 | #ifdef CONFIG_SND_PCM_OSS_PLUGINS | 2819 | #ifdef CONFIG_SND_PCM_OSS_PLUGINS |
