From: Greg Kroah-Hartman Date: Sun, 22 Apr 2018 07:18:46 +0000 (+0200) Subject: 4.14-stable patches X-Git-Tag: v3.18.106~42 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=23690e300210dc1528b1a9c377a701786f5ea19d;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-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 --- diff --git a/queue-4.14/alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch b/queue-4.14/alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch new file mode 100644 index 00000000000..e4ff5dbcb24 --- /dev/null +++ b/queue-4.14/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 +@@ -823,8 +823,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; +@@ -838,11 +838,8 @@ static int snd_pcm_oss_change_params(str + const 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); +@@ -1068,6 +1065,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; + } +@@ -1096,11 +1110,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, +@@ -1120,8 +1137,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); +@@ -1129,6 +1144,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; +@@ -1332,13 +1370,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) +@@ -1439,13 +1478,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); +@@ -1501,10 +1541,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; + } +@@ -1590,9 +1632,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"); +@@ -1643,7 +1686,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]; +@@ -1654,8 +1699,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; + } +@@ -1674,10 +1721,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); + } +@@ -1705,10 +1755,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); + } +@@ -1794,10 +1847,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); +@@ -1817,8 +1873,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; +@@ -1842,9 +1896,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; +@@ -1854,8 +1915,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; +@@ -1875,9 +1934,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; +@@ -1961,6 +2027,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; +@@ -1978,13 +2047,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; +@@ -1999,11 +2074,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.14/alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch b/queue-4.14/alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch new file mode 100644 index 00000000000..850f7fe1752 --- /dev/null +++ b/queue-4.14/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 +@@ -1128,13 +1128,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.14/alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch b/queue-4.14/alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch new file mode 100644 index 00000000000..0643e1b0488 --- /dev/null +++ b/queue-4.14/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 +@@ -823,6 +823,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) + { +@@ -1721,6 +1738,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; +@@ -1728,15 +1747,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); + } +@@ -1761,18 +1779,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); + } +@@ -1845,6 +1864,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); +@@ -1858,15 +1878,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); +@@ -1914,12 +1933,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; + } +@@ -1954,12 +1972,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.14/alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch b/queue-4.14/alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch new file mode 100644 index 00000000000..b38fd91c5f6 --- /dev/null +++ b/queue-4.14/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 +@@ -1370,6 +1370,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; +@@ -1433,6 +1434,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; + } + +@@ -1478,6 +1480,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; +@@ -1526,6 +1529,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; + } + +@@ -1632,8 +1636,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) { +@@ -1645,10 +1652,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"); +@@ -1658,10 +1663,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. +@@ -1675,7 +1678,11 @@ static int snd_pcm_oss_sync(struct snd_p + else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) + snd_pcm_lib_writev(substream, NULL, size); + } ++unlock: + mutex_unlock(&runtime->oss.params_lock); ++ atomic_dec(&runtime->oss.rw_ref); ++ if (err < 0) ++ return err; + /* + * finish sync: drain the buffer + */ +@@ -1723,6 +1730,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; +@@ -1757,6 +1766,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; +@@ -1847,6 +1858,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) { +@@ -1901,6 +1914,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); +@@ -1939,6 +1954,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); +@@ -2333,6 +2350,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.14/alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch b/queue-4.14/alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch new file mode 100644 index 00000000000..55e6f75f75f --- /dev/null +++ b/queue-4.14/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 +@@ -842,7 +842,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.14/series b/queue-4.14/series index fda25925202..60ef8cd7464 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -112,3 +112,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