]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 22 Apr 2018 07:18:46 +0000 (09:18 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 22 Apr 2018 07:18:46 +0000 (09:18 +0200)
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

queue-4.14/alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch [new file with mode: 0644]
queue-4.14/alsa-pcm-fix-endless-loop-for-xrun-recovery-in-oss-emulation.patch [new file with mode: 0644]
queue-4.14/alsa-pcm-fix-mutex-unbalance-in-oss-emulation-ioctls.patch [new file with mode: 0644]
queue-4.14/alsa-pcm-return-ebusy-for-oss-ioctls-changing-busy-streams.patch [new file with mode: 0644]
queue-4.14/alsa-pcm-use-erestartsys-instead-of-eintr-in-oss-emulation.patch [new file with mode: 0644]
queue-4.14/series

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 (file)
index 0000000..e4ff5db
--- /dev/null
@@ -0,0 +1,376 @@
+From 02a5d6925cd34c3b774bdb8eefb057c40a30e870 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Thu, 22 Mar 2018 18:10:14 +0100
+Subject: ALSA: pcm: Avoid potential races between OSS ioctls and read/write
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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 (file)
index 0000000..850f7fe
--- /dev/null
@@ -0,0 +1,53 @@
+From e15dc99dbb9cf99f6432e8e3c0b3a8f7a3403a86 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Sat, 7 Apr 2018 11:48:58 +0200
+Subject: ALSA: pcm: Fix endless loop for XRUN recovery in OSS emulation
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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 (file)
index 0000000..0643e1b
--- /dev/null
@@ -0,0 +1,169 @@
+From f6d297df4dd47ef949540e4a201230d0c5308325 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Tue, 27 Mar 2018 14:32:23 +0200
+Subject: ALSA: pcm: Fix mutex unbalance in OSS emulation ioctls
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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 <dan.carpenter@oracle.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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 (file)
index 0000000..b38fd91
--- /dev/null
@@ -0,0 +1,182 @@
+From 40cab6e88cb0b6c56d3f30b7491a20e803f948f6 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Fri, 23 Mar 2018 08:03:26 +0100
+Subject: ALSA: pcm: Return -EBUSY for OSS ioctls changing busy streams
+
+From: Takashi Iwai <tiwai@suse.de>
+
+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: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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 (file)
index 0000000..55e6f75
--- /dev/null
@@ -0,0 +1,30 @@
+From c64ed5dd9feba193c76eb460b451225ac2a0d87b Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+Date: Tue, 9 Jan 2018 08:51:02 +0100
+Subject: ALSA: pcm: Use ERESTARTSYS instead of EINTR in OSS emulation
+
+From: Takashi Iwai <tiwai@suse.de>
+
+commit c64ed5dd9feba193c76eb460b451225ac2a0d87b upstream.
+
+Fix the last standing EINTR in the whole subsystem.  Use more correct
+ERESTARTSYS for pending signals.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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);
index fda2592520240aa4c605016bc55b9a05d09158de..60ef8cd7464d70daa4d58ddad529a9cfb913e2ca 100644 (file)
@@ -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