| 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 |
| |
| 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> |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| 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 |
| @@ -833,6 +833,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) |
| { |
| @@ -1774,6 +1791,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; |
| @@ -1781,15 +1800,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); |
| } |
| @@ -1814,18 +1832,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); |
| } |
| @@ -1896,6 +1915,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); |
| @@ -1909,15 +1929,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); |
| @@ -1965,12 +1984,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; |
| } |
| @@ -2005,12 +2023,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; |
| } |