From 2aade7fadf391be4bc49589f64de462dfd8daa95 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 22 Apr 2018 09:19:19 +0200 Subject: [PATCH] 4.9-stable patches added patches: alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch --- ...es-between-oss-ioctls-and-read-write.patch | 376 ++++++++++++++++++ ...p-for-xrun-recovery-in-oss-emulation.patch | 53 +++ ...ex-unbalance-in-oss-emulation-ioctls.patch | 169 ++++++++ ...for-oss-ioctls-changing-busy-streams.patch | 182 +++++++++ ...ys-instead-of-eintr-in-oss-emulation.patch | 30 ++ queue-4.9/series | 5 + 6 files changed, 815 insertions(+) create mode 100644 queue-4.9/alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch create mode 100644 queue-4.9/alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch create mode 100644 queue-4.9/alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch create mode 100644 queue-4.9/alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch create mode 100644 queue-4.9/alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch diff --git a/queue-4.9/alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch b/queue-4.9/alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch new file mode 100644 index 00000000000..843b62fff46 --- /dev/null +++ b/queue-4.9/alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch @@ -0,0 +1,376 @@ +From 02a5d6925cd34c3b774bdb8eefb057c40a30e870 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 22 Mar 2018 18:10:14 +0100 +Subject: ALSA: pcm: Avoid potential races between OSS ioctls and read/write + +From: Takashi Iwai + +commit 02a5d6925cd34c3b774bdb8eefb057c40a30e870 upstream. + +Although we apply the params_lock mutex to the whole read and write +operations as well as snd_pcm_oss_change_params(), we may still face +some races. + +First off, the params_lock is taken inside the read and write loop. +This is intentional for avoiding the too long locking, but it allows +the in-between parameter change, which might lead to invalid +pointers. We check the readiness of the stream and set up via +snd_pcm_oss_make_ready() at the beginning of read and write, but it's +called only once, by assuming that it remains ready in the rest. + +Second, many ioctls that may change the actual parameters +(i.e. setting runtime->oss.params=1) aren't protected, hence they can +be processed in a half-baked state. + +This patch is an attempt to plug these holes. The stream readiness +check is moved inside the read/write inner loop, so that the stream is +always set up in a proper state before further processing. Also, each +ioctl that may change the parameter is wrapped with the params_lock +for avoiding the races. + +The issues were triggered by syzkaller in a few different scenarios, +particularly the one below appearing as GPF in loopback_pos_update. + +Reported-by: syzbot+c4227aec125487ec3efa@syzkaller.appspotmail.com +Cc: +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/core/oss/pcm_oss.c | 134 +++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 106 insertions(+), 28 deletions(-) + +--- a/sound/core/oss/pcm_oss.c ++++ b/sound/core/oss/pcm_oss.c +@@ -834,8 +834,8 @@ static int choose_rate(struct snd_pcm_su + 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, +- bool trylock) ++/* call with params_lock held */ ++static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) + { + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_pcm_hw_params *params, *sparams; +@@ -849,11 +849,8 @@ static int snd_pcm_oss_change_params(str + struct snd_mask sformat_mask; + struct snd_mask mask; + +- if (trylock) { +- if (!(mutex_trylock(&runtime->oss.params_lock))) +- return -EAGAIN; +- } else if (mutex_lock_interruptible(&runtime->oss.params_lock)) +- return -ERESTARTSYS; ++ if (!runtime->oss.params) ++ return 0; + sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL); + params = kmalloc(sizeof(*params), GFP_KERNEL); + sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); +@@ -1079,6 +1076,23 @@ failure: + kfree(sw_params); + kfree(params); + kfree(sparams); ++ return err; ++} ++ ++/* this one takes the lock by itself */ ++static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream, ++ bool trylock) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ int err; ++ ++ if (trylock) { ++ if (!(mutex_trylock(&runtime->oss.params_lock))) ++ return -EAGAIN; ++ } else if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; ++ ++ err = snd_pcm_oss_change_params_locked(substream); + mutex_unlock(&runtime->oss.params_lock); + return err; + } +@@ -1107,11 +1121,14 @@ static int snd_pcm_oss_get_active_substr + return 0; + } + ++/* call with params_lock held */ + static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream) + { + int err; + struct snd_pcm_runtime *runtime = substream->runtime; + ++ if (!runtime->oss.prepare) ++ return 0; + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); + if (err < 0) { + pcm_dbg(substream->pcm, +@@ -1131,8 +1148,6 @@ static int snd_pcm_oss_make_ready(struct + struct snd_pcm_runtime *runtime; + int err; + +- if (substream == NULL) +- return 0; + runtime = substream->runtime; + if (runtime->oss.params) { + err = snd_pcm_oss_change_params(substream, false); +@@ -1140,6 +1155,29 @@ static int snd_pcm_oss_make_ready(struct + return err; + } + if (runtime->oss.prepare) { ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; ++ err = snd_pcm_oss_prepare(substream); ++ mutex_unlock(&runtime->oss.params_lock); ++ if (err < 0) ++ return err; ++ } ++ return 0; ++} ++ ++/* call with params_lock held */ ++static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime; ++ int err; ++ ++ runtime = substream->runtime; ++ if (runtime->oss.params) { ++ err = snd_pcm_oss_change_params_locked(substream); ++ if (err < 0) ++ return err; ++ } ++ if (runtime->oss.prepare) { + err = snd_pcm_oss_prepare(substream); + if (err < 0) + return err; +@@ -1367,13 +1405,14 @@ static ssize_t snd_pcm_oss_write1(struct + if (atomic_read(&substream->mmap_count)) + return -ENXIO; + +- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) +- return tmp; + while (bytes > 0) { + if (mutex_lock_interruptible(&runtime->oss.params_lock)) { + tmp = -ERESTARTSYS; + break; + } ++ tmp = snd_pcm_oss_make_ready_locked(substream); ++ if (tmp < 0) ++ goto err; + if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { + tmp = bytes; + if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) +@@ -1474,13 +1513,14 @@ static ssize_t snd_pcm_oss_read1(struct + if (atomic_read(&substream->mmap_count)) + return -ENXIO; + +- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) +- return tmp; + while (bytes > 0) { + if (mutex_lock_interruptible(&runtime->oss.params_lock)) { + tmp = -ERESTARTSYS; + break; + } ++ tmp = snd_pcm_oss_make_ready_locked(substream); ++ if (tmp < 0) ++ goto err; + if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { + if (runtime->oss.buffer_used == 0) { + tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); +@@ -1536,10 +1576,12 @@ static int snd_pcm_oss_reset(struct snd_ + continue; + runtime = substream->runtime; + snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); ++ mutex_lock(&runtime->oss.params_lock); + runtime->oss.prepare = 1; + runtime->oss.buffer_used = 0; + runtime->oss.prev_hw_ptr_period = 0; + runtime->oss.period_ptr = 0; ++ mutex_unlock(&runtime->oss.params_lock); + } + return 0; + } +@@ -1625,9 +1667,10 @@ static int snd_pcm_oss_sync(struct snd_p + goto __direct; + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; + format = snd_pcm_oss_format_from(runtime->oss.format); + width = snd_pcm_format_physical_width(format); +- mutex_lock(&runtime->oss.params_lock); + if (runtime->oss.buffer_used > 0) { + #ifdef OSS_DEBUG + pcm_dbg(substream->pcm, "sync: buffer_used\n"); +@@ -1695,7 +1738,9 @@ static int snd_pcm_oss_sync(struct snd_p + substream->f_flags = saved_f_flags; + if (err < 0) + return err; ++ mutex_lock(&runtime->oss.params_lock); + runtime->oss.prepare = 1; ++ mutex_unlock(&runtime->oss.params_lock); + } + + substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE]; +@@ -1706,8 +1751,10 @@ static int snd_pcm_oss_sync(struct snd_p + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL); + if (err < 0) + return err; ++ mutex_lock(&runtime->oss.params_lock); + runtime->oss.buffer_used = 0; + runtime->oss.prepare = 1; ++ mutex_unlock(&runtime->oss.params_lock); + } + return 0; + } +@@ -1726,10 +1773,13 @@ static int snd_pcm_oss_set_rate(struct s + rate = 1000; + else if (rate > 192000) + rate = 192000; ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; + if (runtime->oss.rate != rate) { + runtime->oss.params = 1; + runtime->oss.rate = rate; + } ++ mutex_unlock(&runtime->oss.params_lock); + } + return snd_pcm_oss_get_rate(pcm_oss_file); + } +@@ -1757,10 +1807,13 @@ static int snd_pcm_oss_set_channels(stru + if (substream == NULL) + continue; + runtime = substream->runtime; ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; + if (runtime->oss.channels != channels) { + runtime->oss.params = 1; + runtime->oss.channels = channels; + } ++ mutex_unlock(&runtime->oss.params_lock); + } + return snd_pcm_oss_get_channels(pcm_oss_file); + } +@@ -1846,10 +1899,13 @@ static int snd_pcm_oss_set_format(struct + if (substream == NULL) + continue; + runtime = substream->runtime; ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; + if (runtime->oss.format != format) { + runtime->oss.params = 1; + runtime->oss.format = format; + } ++ mutex_unlock(&runtime->oss.params_lock); + } + } + return snd_pcm_oss_get_format(pcm_oss_file); +@@ -1869,8 +1925,6 @@ static int snd_pcm_oss_set_subdivide1(st + { + struct snd_pcm_runtime *runtime; + +- if (substream == NULL) +- return 0; + runtime = substream->runtime; + if (subdivide == 0) { + subdivide = runtime->oss.subdivision; +@@ -1894,9 +1948,16 @@ static int snd_pcm_oss_set_subdivide(str + + for (idx = 1; idx >= 0; --idx) { + struct snd_pcm_substream *substream = pcm_oss_file->streams[idx]; ++ struct snd_pcm_runtime *runtime; ++ + if (substream == NULL) + continue; +- if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0) ++ runtime = substream->runtime; ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; ++ err = snd_pcm_oss_set_subdivide1(substream, subdivide); ++ mutex_unlock(&runtime->oss.params_lock); ++ if (err < 0) + return err; + } + return err; +@@ -1906,8 +1967,6 @@ static int snd_pcm_oss_set_fragment1(str + { + struct snd_pcm_runtime *runtime; + +- if (substream == NULL) +- return 0; + runtime = substream->runtime; + if (runtime->oss.subdivision || runtime->oss.fragshift) + return -EINVAL; +@@ -1927,9 +1986,16 @@ static int snd_pcm_oss_set_fragment(stru + + for (idx = 1; idx >= 0; --idx) { + struct snd_pcm_substream *substream = pcm_oss_file->streams[idx]; ++ struct snd_pcm_runtime *runtime; ++ + if (substream == NULL) + continue; +- if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0) ++ runtime = substream->runtime; ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; ++ err = snd_pcm_oss_set_fragment1(substream, val); ++ mutex_unlock(&runtime->oss.params_lock); ++ if (err < 0) + return err; + } + return err; +@@ -2013,6 +2079,9 @@ static int snd_pcm_oss_set_trigger(struc + } + if (psubstream) { + runtime = psubstream->runtime; ++ cmd = 0; ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; + if (trigger & PCM_ENABLE_OUTPUT) { + if (runtime->oss.trigger) + goto _skip1; +@@ -2030,13 +2099,19 @@ static int snd_pcm_oss_set_trigger(struc + cmd = SNDRV_PCM_IOCTL_DROP; + runtime->oss.prepare = 1; + } +- err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL); +- if (err < 0) +- return err; +- } + _skip1: ++ mutex_unlock(&runtime->oss.params_lock); ++ if (cmd) { ++ err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL); ++ if (err < 0) ++ return err; ++ } ++ } + if (csubstream) { + runtime = csubstream->runtime; ++ cmd = 0; ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; + if (trigger & PCM_ENABLE_INPUT) { + if (runtime->oss.trigger) + goto _skip2; +@@ -2051,11 +2126,14 @@ static int snd_pcm_oss_set_trigger(struc + cmd = SNDRV_PCM_IOCTL_DROP; + runtime->oss.prepare = 1; + } +- err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL); +- if (err < 0) +- return err; +- } + _skip2: ++ mutex_unlock(&runtime->oss.params_lock); ++ if (cmd) { ++ err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL); ++ if (err < 0) ++ return err; ++ } ++ } + return 0; + } + diff --git a/queue-4.9/alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch b/queue-4.9/alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch new file mode 100644 index 00000000000..fe869fd6b2b --- /dev/null +++ b/queue-4.9/alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch @@ -0,0 +1,53 @@ +From e15dc99dbb9cf99f6432e8e3c0b3a8f7a3403a86 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Sat, 7 Apr 2018 11:48:58 +0200 +Subject: ALSA: pcm: Fix endless loop for XRUN recovery in OSS emulation + +From: Takashi Iwai + +commit e15dc99dbb9cf99f6432e8e3c0b3a8f7a3403a86 upstream. + +The commit 02a5d6925cd3 ("ALSA: pcm: Avoid potential races between OSS +ioctls and read/write") split the PCM preparation code to a locked +version, and it added a sanity check of runtime->oss.prepare flag +along with the change. This leaded to an endless loop when the stream +gets XRUN: namely, snd_pcm_oss_write3() and co call +snd_pcm_oss_prepare() without setting runtime->oss.prepare flag and +the loop continues until the PCM state reaches to another one. + +As the function is supposed to execute the preparation +unconditionally, drop the invalid state check there. + +The bug was triggered by syzkaller. + +Fixes: 02a5d6925cd3 ("ALSA: pcm: Avoid potential races between OSS ioctls and read/write") +Reported-by: syzbot+150189c103427d31a053@syzkaller.appspotmail.com +Reported-by: syzbot+7e3f31a52646f939c052@syzkaller.appspotmail.com +Reported-by: syzbot+4f2016cf5185da7759dc@syzkaller.appspotmail.com +Cc: +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/core/oss/pcm_oss.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +--- a/sound/core/oss/pcm_oss.c ++++ b/sound/core/oss/pcm_oss.c +@@ -1139,13 +1139,14 @@ static int snd_pcm_oss_get_active_substr + } + + /* call with params_lock held */ ++/* NOTE: this always call PREPARE unconditionally no matter whether ++ * runtime->oss.prepare is set or not ++ */ + static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream) + { + int err; + struct snd_pcm_runtime *runtime = substream->runtime; + +- if (!runtime->oss.prepare) +- return 0; + err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL); + if (err < 0) { + pcm_dbg(substream->pcm, diff --git a/queue-4.9/alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch b/queue-4.9/alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch new file mode 100644 index 00000000000..5da9bea603a --- /dev/null +++ b/queue-4.9/alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch @@ -0,0 +1,169 @@ +From f6d297df4dd47ef949540e4a201230d0c5308325 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 27 Mar 2018 14:32:23 +0200 +Subject: ALSA: pcm: Fix mutex unbalance in OSS emulation ioctls + +From: Takashi Iwai + +commit f6d297df4dd47ef949540e4a201230d0c5308325 upstream. + +The previous fix 40cab6e88cb0 ("ALSA: pcm: Return -EBUSY for OSS +ioctls changing busy streams") introduced some mutex unbalance; the +check of runtime->oss.rw_ref was inserted in a wrong place after the +mutex lock. + +This patch fixes the inconsistency by rewriting with the helper +functions to lock/unlock parameters with the stream check. + +Fixes: 40cab6e88cb0 ("ALSA: pcm: Return -EBUSY for OSS ioctls changing busy streams") +Reported-by: Dan Carpenter +Cc: +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/core/oss/pcm_oss.c | 67 +++++++++++++++++++++++++++++------------------ + 1 file changed, 42 insertions(+), 25 deletions(-) + +--- a/sound/core/oss/pcm_oss.c ++++ b/sound/core/oss/pcm_oss.c +@@ -834,6 +834,23 @@ static int choose_rate(struct snd_pcm_su + return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL); + } + ++/* parameter locking: returns immediately if tried during streaming */ ++static int lock_params(struct snd_pcm_runtime *runtime) ++{ ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ return -ERESTARTSYS; ++ if (atomic_read(&runtime->oss.rw_ref)) { ++ mutex_unlock(&runtime->oss.params_lock); ++ return -EBUSY; ++ } ++ return 0; ++} ++ ++static void unlock_params(struct snd_pcm_runtime *runtime) ++{ ++ mutex_unlock(&runtime->oss.params_lock); ++} ++ + /* call with params_lock held */ + static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) + { +@@ -1773,6 +1790,8 @@ static int snd_pcm_oss_set_rate(struct s + for (idx = 1; idx >= 0; --idx) { + struct snd_pcm_substream *substream = pcm_oss_file->streams[idx]; + struct snd_pcm_runtime *runtime; ++ int err; ++ + if (substream == NULL) + continue; + runtime = substream->runtime; +@@ -1780,15 +1799,14 @@ static int snd_pcm_oss_set_rate(struct s + rate = 1000; + else if (rate > 192000) + rate = 192000; +- if (mutex_lock_interruptible(&runtime->oss.params_lock)) +- return -ERESTARTSYS; +- if (atomic_read(&runtime->oss.rw_ref)) +- return -EBUSY; ++ err = lock_params(runtime); ++ if (err < 0) ++ return err; + if (runtime->oss.rate != rate) { + runtime->oss.params = 1; + runtime->oss.rate = rate; + } +- mutex_unlock(&runtime->oss.params_lock); ++ unlock_params(runtime); + } + return snd_pcm_oss_get_rate(pcm_oss_file); + } +@@ -1813,18 +1831,19 @@ static int snd_pcm_oss_set_channels(stru + for (idx = 1; idx >= 0; --idx) { + struct snd_pcm_substream *substream = pcm_oss_file->streams[idx]; + struct snd_pcm_runtime *runtime; ++ int err; ++ + if (substream == NULL) + continue; + runtime = substream->runtime; +- if (mutex_lock_interruptible(&runtime->oss.params_lock)) +- return -ERESTARTSYS; +- if (atomic_read(&runtime->oss.rw_ref)) +- return -EBUSY; ++ err = lock_params(runtime); ++ if (err < 0) ++ return err; + if (runtime->oss.channels != channels) { + runtime->oss.params = 1; + runtime->oss.channels = channels; + } +- mutex_unlock(&runtime->oss.params_lock); ++ unlock_params(runtime); + } + return snd_pcm_oss_get_channels(pcm_oss_file); + } +@@ -1897,6 +1916,7 @@ static int snd_pcm_oss_get_formats(struc + static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format) + { + int formats, idx; ++ int err; + + if (format != AFMT_QUERY) { + formats = snd_pcm_oss_get_formats(pcm_oss_file); +@@ -1910,15 +1930,14 @@ static int snd_pcm_oss_set_format(struct + if (substream == NULL) + continue; + runtime = substream->runtime; +- if (atomic_read(&runtime->oss.rw_ref)) +- return -EBUSY; +- if (mutex_lock_interruptible(&runtime->oss.params_lock)) +- return -ERESTARTSYS; ++ err = lock_params(runtime); ++ if (err < 0) ++ return err; + if (runtime->oss.format != format) { + runtime->oss.params = 1; + runtime->oss.format = format; + } +- mutex_unlock(&runtime->oss.params_lock); ++ unlock_params(runtime); + } + } + return snd_pcm_oss_get_format(pcm_oss_file); +@@ -1966,12 +1985,11 @@ static int snd_pcm_oss_set_subdivide(str + if (substream == NULL) + continue; + runtime = substream->runtime; +- if (atomic_read(&runtime->oss.rw_ref)) +- return -EBUSY; +- if (mutex_lock_interruptible(&runtime->oss.params_lock)) +- return -ERESTARTSYS; ++ err = lock_params(runtime); ++ if (err < 0) ++ return err; + err = snd_pcm_oss_set_subdivide1(substream, subdivide); +- mutex_unlock(&runtime->oss.params_lock); ++ unlock_params(runtime); + if (err < 0) + return err; + } +@@ -2006,12 +2024,11 @@ static int snd_pcm_oss_set_fragment(stru + if (substream == NULL) + continue; + runtime = substream->runtime; +- if (atomic_read(&runtime->oss.rw_ref)) +- return -EBUSY; +- if (mutex_lock_interruptible(&runtime->oss.params_lock)) +- return -ERESTARTSYS; ++ err = lock_params(runtime); ++ if (err < 0) ++ return err; + err = snd_pcm_oss_set_fragment1(substream, val); +- mutex_unlock(&runtime->oss.params_lock); ++ unlock_params(runtime); + if (err < 0) + return err; + } diff --git a/queue-4.9/alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch b/queue-4.9/alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch new file mode 100644 index 00000000000..1b6a0a1a3a8 --- /dev/null +++ b/queue-4.9/alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch @@ -0,0 +1,182 @@ +From 40cab6e88cb0b6c56d3f30b7491a20e803f948f6 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 23 Mar 2018 08:03:26 +0100 +Subject: ALSA: pcm: Return -EBUSY for OSS ioctls changing busy streams + +From: Takashi Iwai + +commit 40cab6e88cb0b6c56d3f30b7491a20e803f948f6 upstream. + +OSS PCM stream management isn't modal but it allows ioctls issued at +any time for changing the parameters. In the previous hardening +patch ("ALSA: pcm: Avoid potential races between OSS ioctls and +read/write"), we covered these races and prevent the corruption by +protecting the concurrent accesses via params_lock mutex. However, +this means that some ioctls that try to change the stream parameter +(e.g. channels or format) would be blocked until the read/write +finishes, and it may take really long. + +Basically changing the parameter while reading/writing is an invalid +operation, hence it's even more user-friendly from the API POV if it +returns -EBUSY in such a situation. + +This patch adds such checks in the relevant ioctls with the addition +of read/write access refcount. + +Cc: +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + include/sound/pcm_oss.h | 1 + + sound/core/oss/pcm_oss.c | 36 +++++++++++++++++++++++++++--------- + 2 files changed, 28 insertions(+), 9 deletions(-) + +--- a/include/sound/pcm_oss.h ++++ b/include/sound/pcm_oss.h +@@ -57,6 +57,7 @@ struct snd_pcm_oss_runtime { + char *buffer; /* vmallocated period */ + size_t buffer_used; /* used length from period buffer */ + struct mutex params_lock; ++ atomic_t rw_ref; /* concurrent read/write accesses */ + #ifdef CONFIG_SND_PCM_OSS_PLUGINS + struct snd_pcm_plugin *plugin_first; + struct snd_pcm_plugin *plugin_last; +--- a/sound/core/oss/pcm_oss.c ++++ b/sound/core/oss/pcm_oss.c +@@ -1405,6 +1405,7 @@ static ssize_t snd_pcm_oss_write1(struct + if (atomic_read(&substream->mmap_count)) + return -ENXIO; + ++ atomic_inc(&runtime->oss.rw_ref); + while (bytes > 0) { + if (mutex_lock_interruptible(&runtime->oss.params_lock)) { + tmp = -ERESTARTSYS; +@@ -1468,6 +1469,7 @@ static ssize_t snd_pcm_oss_write1(struct + } + tmp = 0; + } ++ atomic_dec(&runtime->oss.rw_ref); + return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; + } + +@@ -1513,6 +1515,7 @@ static ssize_t snd_pcm_oss_read1(struct + if (atomic_read(&substream->mmap_count)) + return -ENXIO; + ++ atomic_inc(&runtime->oss.rw_ref); + while (bytes > 0) { + if (mutex_lock_interruptible(&runtime->oss.params_lock)) { + tmp = -ERESTARTSYS; +@@ -1561,6 +1564,7 @@ static ssize_t snd_pcm_oss_read1(struct + } + tmp = 0; + } ++ atomic_dec(&runtime->oss.rw_ref); + return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; + } + +@@ -1667,8 +1671,11 @@ static int snd_pcm_oss_sync(struct snd_p + goto __direct; + if ((err = snd_pcm_oss_make_ready(substream)) < 0) + return err; +- if (mutex_lock_interruptible(&runtime->oss.params_lock)) ++ atomic_inc(&runtime->oss.rw_ref); ++ if (mutex_lock_interruptible(&runtime->oss.params_lock)) { ++ atomic_dec(&runtime->oss.rw_ref); + return -ERESTARTSYS; ++ } + format = snd_pcm_oss_format_from(runtime->oss.format); + width = snd_pcm_format_physical_width(format); + if (runtime->oss.buffer_used > 0) { +@@ -1680,10 +1687,8 @@ static int snd_pcm_oss_sync(struct snd_p + runtime->oss.buffer + runtime->oss.buffer_used, + size); + err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes); +- if (err < 0) { +- mutex_unlock(&runtime->oss.params_lock); +- return err; +- } ++ if (err < 0) ++ goto unlock; + } else if (runtime->oss.period_ptr > 0) { + #ifdef OSS_DEBUG + pcm_dbg(substream->pcm, "sync: period_ptr\n"); +@@ -1693,10 +1698,8 @@ static int snd_pcm_oss_sync(struct snd_p + runtime->oss.buffer, + size * 8 / width); + err = snd_pcm_oss_sync1(substream, size); +- if (err < 0) { +- mutex_unlock(&runtime->oss.params_lock); +- return err; +- } ++ if (err < 0) ++ goto unlock; + } + /* + * The ALSA's period might be a bit large than OSS one. +@@ -1727,7 +1730,11 @@ static int snd_pcm_oss_sync(struct snd_p + snd_pcm_lib_writev(substream, buffers, size); + } + } ++unlock: + mutex_unlock(&runtime->oss.params_lock); ++ atomic_dec(&runtime->oss.rw_ref); ++ if (err < 0) ++ return err; + /* + * finish sync: drain the buffer + */ +@@ -1775,6 +1782,8 @@ static int snd_pcm_oss_set_rate(struct s + rate = 192000; + if (mutex_lock_interruptible(&runtime->oss.params_lock)) + return -ERESTARTSYS; ++ if (atomic_read(&runtime->oss.rw_ref)) ++ return -EBUSY; + if (runtime->oss.rate != rate) { + runtime->oss.params = 1; + runtime->oss.rate = rate; +@@ -1809,6 +1818,8 @@ static int snd_pcm_oss_set_channels(stru + runtime = substream->runtime; + if (mutex_lock_interruptible(&runtime->oss.params_lock)) + return -ERESTARTSYS; ++ if (atomic_read(&runtime->oss.rw_ref)) ++ return -EBUSY; + if (runtime->oss.channels != channels) { + runtime->oss.params = 1; + runtime->oss.channels = channels; +@@ -1899,6 +1910,8 @@ static int snd_pcm_oss_set_format(struct + if (substream == NULL) + continue; + runtime = substream->runtime; ++ if (atomic_read(&runtime->oss.rw_ref)) ++ return -EBUSY; + if (mutex_lock_interruptible(&runtime->oss.params_lock)) + return -ERESTARTSYS; + if (runtime->oss.format != format) { +@@ -1953,6 +1966,8 @@ static int snd_pcm_oss_set_subdivide(str + if (substream == NULL) + continue; + runtime = substream->runtime; ++ if (atomic_read(&runtime->oss.rw_ref)) ++ return -EBUSY; + if (mutex_lock_interruptible(&runtime->oss.params_lock)) + return -ERESTARTSYS; + err = snd_pcm_oss_set_subdivide1(substream, subdivide); +@@ -1991,6 +2006,8 @@ static int snd_pcm_oss_set_fragment(stru + if (substream == NULL) + continue; + runtime = substream->runtime; ++ if (atomic_read(&runtime->oss.rw_ref)) ++ return -EBUSY; + if (mutex_lock_interruptible(&runtime->oss.params_lock)) + return -ERESTARTSYS; + err = snd_pcm_oss_set_fragment1(substream, val); +@@ -2385,6 +2402,7 @@ static void snd_pcm_oss_init_substream(s + runtime->oss.maxfrags = 0; + runtime->oss.subdivision = 0; + substream->pcm_release = snd_pcm_oss_release_substream; ++ atomic_set(&runtime->oss.rw_ref, 0); + } + + static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file) diff --git a/queue-4.9/alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch b/queue-4.9/alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch new file mode 100644 index 00000000000..f798d2c8106 --- /dev/null +++ b/queue-4.9/alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch @@ -0,0 +1,30 @@ +From c64ed5dd9feba193c76eb460b451225ac2a0d87b Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 9 Jan 2018 08:51:02 +0100 +Subject: ALSA: pcm: Use ERESTARTSYS instead of EINTR in OSS emulation + +From: Takashi Iwai + +commit c64ed5dd9feba193c76eb460b451225ac2a0d87b upstream. + +Fix the last standing EINTR in the whole subsystem. Use more correct +ERESTARTSYS for pending signals. + +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/core/oss/pcm_oss.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/sound/core/oss/pcm_oss.c ++++ b/sound/core/oss/pcm_oss.c +@@ -853,7 +853,7 @@ static int snd_pcm_oss_change_params(str + if (!(mutex_trylock(&runtime->oss.params_lock))) + return -EAGAIN; + } else if (mutex_lock_interruptible(&runtime->oss.params_lock)) +- return -EINTR; ++ return -ERESTARTSYS; + sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL); + params = kmalloc(sizeof(*params), GFP_KERNEL); + sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); diff --git a/queue-4.9/series b/queue-4.9/series index 0dfff59ce32..d9dc5b2fa53 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -57,3 +57,8 @@ thermal-imx-fix-race-condition-in-imx_thermal_probe.patch dt-bindings-clock-mediatek-add-binding-for-fixed-factor-clock-axisel_d4.patch watchdog-f71808e_wdt-fix-wd_en-register-read.patch vfio-pci-virtualize-maximum-read-request-size.patch +alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch +alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch +alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch +alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch +alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch -- 2.47.3