aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2016-01-31 04:32:37 -0500
committerTakashi Iwai <tiwai@suse.de>2016-02-01 06:23:29 -0500
commitb248371628aad599a48540962f6b85a21a8a0c3f (patch)
treeb53a2028b511afaa11cacf8eecbb09cd43aaa190
parentcc85f7a634cfaf9f0713c6aa06d08817424db37a (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.c21
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
838static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) 838static 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