summaryrefslogtreecommitdiffstats
path: root/sound/core
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2018-03-23 08:03:26 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-04-24 09:29:27 +0200
commita653caa0ca1bb87bb60efb371eaec0fa2a5b3676 (patch)
treec557a82f993664e9dafa58728b04a3c92e4b540a /sound/core
parenta20304a59636dde34670b17c01ded4465f4620f9 (diff)
downloadlinux-stable-a653caa0ca1bb87bb60efb371eaec0fa2a5b3676.tar.gz
linux-stable-a653caa0ca1bb87bb60efb371eaec0fa2a5b3676.tar.bz2
linux-stable-a653caa0ca1bb87bb60efb371eaec0fa2a5b3676.zip
ALSA: pcm: Return -EBUSY for OSS ioctls changing busy streams
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>
Diffstat (limited to 'sound/core')
-rw-r--r--sound/core/oss/pcm_oss.c36
1 files changed, 27 insertions, 9 deletions
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index 35789a20628c..139a8e5abf49 100644
--- 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 snd_pcm_substream *substream, const cha
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 snd_pcm_substream *substream, const cha
}
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 snd_pcm_substream *substream, char __use
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 snd_pcm_substream *substream, char __use
}
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_pcm_oss_file *pcm_oss_file)
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_pcm_oss_file *pcm_oss_file)
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_pcm_oss_file *pcm_oss_file)
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_pcm_oss_file *pcm_oss_file)
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 snd_pcm_oss_file *pcm_oss_file, int rate)
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(struct snd_pcm_oss_file *pcm_oss_file, unsig
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 snd_pcm_oss_file *pcm_oss_file, int for
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(struct snd_pcm_oss_file *pcm_oss_file, int
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(struct snd_pcm_oss_file *pcm_oss_file, unsig
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(struct snd_pcm_substream *substream,
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)