Commit c8ea3d9e authored by Takashi Iwai's avatar Takashi Iwai Committed by Stefan Bader

ALSA: pcm: Fix mutex unbalance in OSS emulation ioctls

BugLink: http://bugs.launchpad.net/bugs/1768429

commit f6d297df upstream.

The previous fix 40cab6e8 ("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: 40cab6e8 ("ALSA: pcm: Return -EBUSY for OSS ioctls changing busy streams")
Reported-by: default avatarDan Carpenter <dan.carpenter@oracle.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarJuerg Haefliger <juergh@canonical.com>
Signed-off-by: default avatarKleber Sacilotto de Souza <kleber.souza@canonical.com>
parent ec93802a
...@@ -833,6 +833,23 @@ static int choose_rate(struct snd_pcm_substream *substream, ...@@ -833,6 +833,23 @@ static int choose_rate(struct snd_pcm_substream *substream,
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL); 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 */ /* call with params_lock held */
static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream) static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
{ {
...@@ -1772,6 +1789,8 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate) ...@@ -1772,6 +1789,8 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
for (idx = 1; idx >= 0; --idx) { for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx]; struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
int err;
if (substream == NULL) if (substream == NULL)
continue; continue;
runtime = substream->runtime; runtime = substream->runtime;
...@@ -1779,15 +1798,14 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate) ...@@ -1779,15 +1798,14 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
rate = 1000; rate = 1000;
else if (rate > 192000) else if (rate > 192000)
rate = 192000; rate = 192000;
if (mutex_lock_interruptible(&runtime->oss.params_lock)) err = lock_params(runtime);
return -ERESTARTSYS; if (err < 0)
if (atomic_read(&runtime->oss.rw_ref)) return err;
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;
} }
mutex_unlock(&runtime->oss.params_lock); unlock_params(runtime);
} }
return snd_pcm_oss_get_rate(pcm_oss_file); return snd_pcm_oss_get_rate(pcm_oss_file);
} }
...@@ -1812,18 +1830,19 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig ...@@ -1812,18 +1830,19 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig
for (idx = 1; idx >= 0; --idx) { for (idx = 1; idx >= 0; --idx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx]; struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
int err;
if (substream == NULL) if (substream == NULL)
continue; continue;
runtime = substream->runtime; runtime = substream->runtime;
if (mutex_lock_interruptible(&runtime->oss.params_lock)) err = lock_params(runtime);
return -ERESTARTSYS; if (err < 0)
if (atomic_read(&runtime->oss.rw_ref)) return err;
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;
} }
mutex_unlock(&runtime->oss.params_lock); unlock_params(runtime);
} }
return snd_pcm_oss_get_channels(pcm_oss_file); return snd_pcm_oss_get_channels(pcm_oss_file);
} }
...@@ -1896,6 +1915,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file) ...@@ -1896,6 +1915,7 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format) static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int format)
{ {
int formats, idx; int formats, idx;
int err;
if (format != AFMT_QUERY) { if (format != AFMT_QUERY) {
formats = snd_pcm_oss_get_formats(pcm_oss_file); formats = snd_pcm_oss_get_formats(pcm_oss_file);
...@@ -1909,15 +1929,14 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for ...@@ -1909,15 +1929,14 @@ 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)) err = lock_params(runtime);
return -EBUSY; if (err < 0)
if (mutex_lock_interruptible(&runtime->oss.params_lock)) return err;
return -ERESTARTSYS;
if (runtime->oss.format != format) { if (runtime->oss.format != format) {
runtime->oss.params = 1; runtime->oss.params = 1;
runtime->oss.format = format; runtime->oss.format = format;
} }
mutex_unlock(&runtime->oss.params_lock); unlock_params(runtime);
} }
} }
return snd_pcm_oss_get_format(pcm_oss_file); return snd_pcm_oss_get_format(pcm_oss_file);
...@@ -1965,12 +1984,11 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int ...@@ -1965,12 +1984,11 @@ 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)) err = lock_params(runtime);
return -EBUSY; if (err < 0)
if (mutex_lock_interruptible(&runtime->oss.params_lock)) return err;
return -ERESTARTSYS;
err = snd_pcm_oss_set_subdivide1(substream, subdivide); err = snd_pcm_oss_set_subdivide1(substream, subdivide);
mutex_unlock(&runtime->oss.params_lock); unlock_params(runtime);
if (err < 0) if (err < 0)
return err; return err;
} }
...@@ -2005,12 +2023,11 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig ...@@ -2005,12 +2023,11 @@ 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)) err = lock_params(runtime);
return -EBUSY; if (err < 0)
if (mutex_lock_interruptible(&runtime->oss.params_lock)) return err;
return -ERESTARTSYS;
err = snd_pcm_oss_set_fragment1(substream, val); err = snd_pcm_oss_set_fragment1(substream, val);
mutex_unlock(&runtime->oss.params_lock); unlock_params(runtime);
if (err < 0) if (err < 0)
return err; return err;
} }
......
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