Commit 40cab6e8 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: pcm: Return -EBUSY for OSS ioctls changing busy streams

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: default avatarTakashi Iwai <tiwai@suse.de>
parent 02a5d692
...@@ -57,6 +57,7 @@ struct snd_pcm_oss_runtime { ...@@ -57,6 +57,7 @@ struct snd_pcm_oss_runtime {
char *buffer; /* vmallocated period */ char *buffer; /* vmallocated period */
size_t buffer_used; /* used length from period buffer */ size_t buffer_used; /* used length from period buffer */
struct mutex params_lock; struct mutex params_lock;
atomic_t rw_ref; /* concurrent read/write accesses */
#ifdef CONFIG_SND_PCM_OSS_PLUGINS #ifdef CONFIG_SND_PCM_OSS_PLUGINS
struct snd_pcm_plugin *plugin_first; struct snd_pcm_plugin *plugin_first;
struct snd_pcm_plugin *plugin_last; struct snd_pcm_plugin *plugin_last;
......
...@@ -1370,6 +1370,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha ...@@ -1370,6 +1370,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
if (atomic_read(&substream->mmap_count)) if (atomic_read(&substream->mmap_count))
return -ENXIO; return -ENXIO;
atomic_inc(&runtime->oss.rw_ref);
while (bytes > 0) { while (bytes > 0) {
if (mutex_lock_interruptible(&runtime->oss.params_lock)) { if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
tmp = -ERESTARTSYS; tmp = -ERESTARTSYS;
...@@ -1433,6 +1434,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha ...@@ -1433,6 +1434,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
} }
tmp = 0; tmp = 0;
} }
atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
} }
...@@ -1478,6 +1480,7 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use ...@@ -1478,6 +1480,7 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
if (atomic_read(&substream->mmap_count)) if (atomic_read(&substream->mmap_count))
return -ENXIO; return -ENXIO;
atomic_inc(&runtime->oss.rw_ref);
while (bytes > 0) { while (bytes > 0) {
if (mutex_lock_interruptible(&runtime->oss.params_lock)) { if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
tmp = -ERESTARTSYS; tmp = -ERESTARTSYS;
...@@ -1526,6 +1529,7 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use ...@@ -1526,6 +1529,7 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
} }
tmp = 0; tmp = 0;
} }
atomic_dec(&runtime->oss.rw_ref);
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
} }
...@@ -1632,8 +1636,11 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) ...@@ -1632,8 +1636,11 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
goto __direct; goto __direct;
if ((err = snd_pcm_oss_make_ready(substream)) < 0) if ((err = snd_pcm_oss_make_ready(substream)) < 0)
return err; 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; return -ERESTARTSYS;
}
format = snd_pcm_oss_format_from(runtime->oss.format); format = snd_pcm_oss_format_from(runtime->oss.format);
width = snd_pcm_format_physical_width(format); width = snd_pcm_format_physical_width(format);
if (runtime->oss.buffer_used > 0) { if (runtime->oss.buffer_used > 0) {
...@@ -1645,10 +1652,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) ...@@ -1645,10 +1652,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
runtime->oss.buffer + runtime->oss.buffer_used, runtime->oss.buffer + runtime->oss.buffer_used,
size); size);
err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes); err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
if (err < 0) { if (err < 0)
mutex_unlock(&runtime->oss.params_lock); goto unlock;
return err;
}
} else if (runtime->oss.period_ptr > 0) { } else if (runtime->oss.period_ptr > 0) {
#ifdef OSS_DEBUG #ifdef OSS_DEBUG
pcm_dbg(substream->pcm, "sync: period_ptr\n"); pcm_dbg(substream->pcm, "sync: period_ptr\n");
...@@ -1658,10 +1663,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) ...@@ -1658,10 +1663,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
runtime->oss.buffer, runtime->oss.buffer,
size * 8 / width); size * 8 / width);
err = snd_pcm_oss_sync1(substream, size); err = snd_pcm_oss_sync1(substream, size);
if (err < 0) { if (err < 0)
mutex_unlock(&runtime->oss.params_lock); goto unlock;
return err;
}
} }
/* /*
* The ALSA's period might be a bit large than OSS one. * The ALSA's period might be a bit large than OSS one.
...@@ -1675,7 +1678,11 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file) ...@@ -1675,7 +1678,11 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
snd_pcm_lib_writev(substream, NULL, size); snd_pcm_lib_writev(substream, NULL, size);
} }
unlock:
mutex_unlock(&runtime->oss.params_lock); mutex_unlock(&runtime->oss.params_lock);
atomic_dec(&runtime->oss.rw_ref);
if (err < 0)
return err;
/* /*
* finish sync: drain the buffer * finish sync: drain the buffer
*/ */
...@@ -1723,6 +1730,8 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate) ...@@ -1723,6 +1730,8 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
rate = 192000; rate = 192000;
if (mutex_lock_interruptible(&runtime->oss.params_lock)) if (mutex_lock_interruptible(&runtime->oss.params_lock))
return -ERESTARTSYS; return -ERESTARTSYS;
if (atomic_read(&runtime->oss.rw_ref))
return -EBUSY;
if (runtime->oss.rate != rate) { if (runtime->oss.rate != rate) {
runtime->oss.params = 1; runtime->oss.params = 1;
runtime->oss.rate = rate; runtime->oss.rate = rate;
...@@ -1757,6 +1766,8 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig ...@@ -1757,6 +1766,8 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig
runtime = substream->runtime; runtime = substream->runtime;
if (mutex_lock_interruptible(&runtime->oss.params_lock)) if (mutex_lock_interruptible(&runtime->oss.params_lock))
return -ERESTARTSYS; return -ERESTARTSYS;
if (atomic_read(&runtime->oss.rw_ref))
return -EBUSY;
if (runtime->oss.channels != channels) { if (runtime->oss.channels != channels) {
runtime->oss.params = 1; runtime->oss.params = 1;
runtime->oss.channels = channels; runtime->oss.channels = channels;
...@@ -1847,6 +1858,8 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for ...@@ -1847,6 +1858,8 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for
if (substream == NULL) if (substream == NULL)
continue; continue;
runtime = substream->runtime; runtime = substream->runtime;
if (atomic_read(&runtime->oss.rw_ref))
return -EBUSY;
if (mutex_lock_interruptible(&runtime->oss.params_lock)) if (mutex_lock_interruptible(&runtime->oss.params_lock))
return -ERESTARTSYS; return -ERESTARTSYS;
if (runtime->oss.format != format) { if (runtime->oss.format != format) {
...@@ -1901,6 +1914,8 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int ...@@ -1901,6 +1914,8 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int
if (substream == NULL) if (substream == NULL)
continue; continue;
runtime = substream->runtime; runtime = substream->runtime;
if (atomic_read(&runtime->oss.rw_ref))
return -EBUSY;
if (mutex_lock_interruptible(&runtime->oss.params_lock)) if (mutex_lock_interruptible(&runtime->oss.params_lock))
return -ERESTARTSYS; return -ERESTARTSYS;
err = snd_pcm_oss_set_subdivide1(substream, subdivide); err = snd_pcm_oss_set_subdivide1(substream, subdivide);
...@@ -1939,6 +1954,8 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig ...@@ -1939,6 +1954,8 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
if (substream == NULL) if (substream == NULL)
continue; continue;
runtime = substream->runtime; runtime = substream->runtime;
if (atomic_read(&runtime->oss.rw_ref))
return -EBUSY;
if (mutex_lock_interruptible(&runtime->oss.params_lock)) if (mutex_lock_interruptible(&runtime->oss.params_lock))
return -ERESTARTSYS; return -ERESTARTSYS;
err = snd_pcm_oss_set_fragment1(substream, val); err = snd_pcm_oss_set_fragment1(substream, val);
...@@ -2333,6 +2350,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream, ...@@ -2333,6 +2350,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
runtime->oss.maxfrags = 0; runtime->oss.maxfrags = 0;
runtime->oss.subdivision = 0; runtime->oss.subdivision = 0;
substream->pcm_release = snd_pcm_oss_release_substream; 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) static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment