Skip to content

Commit 92ee3c6

Browse files
committed
ALSA: pcm: Fix races among concurrent hw_params and hw_free calls
Currently we have neither proper check nor protection against the concurrent calls of PCM hw_params and hw_free ioctls, which may result in a UAF. Since the existing PCM stream lock can't be used for protecting the whole ioctl operations, we need a new mutex to protect those racy calls. This patch introduced a new mutex, runtime->buffer_mutex, and applies it to both hw_params and hw_free ioctl code paths. Along with it, the both functions are slightly modified (the mmap_count check is moved into the state-check block) for code simplicity. Reported-by: Hu Jiahui <kirin.say@gmail.com> Cc: <stable@vger.kernel.org> Reviewed-by: Jaroslav Kysela <perex@perex.cz> Link: https://lore.kernel.org/r/20220322170720.3529-2-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
1 parent 646b907 commit 92ee3c6

3 files changed

Lines changed: 42 additions & 22 deletions

File tree

include/sound/pcm.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ struct snd_pcm_runtime {
401401
wait_queue_head_t tsleep; /* transfer sleep */
402402
struct fasync_struct *fasync;
403403
bool stop_operating; /* sync_stop will be called */
404+
struct mutex buffer_mutex; /* protect for buffer changes */
404405

405406
/* -- private section -- */
406407
void *private_data;

sound/core/pcm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
969969
init_waitqueue_head(&runtime->tsleep);
970970

971971
runtime->status->state = SNDRV_PCM_STATE_OPEN;
972+
mutex_init(&runtime->buffer_mutex);
972973

973974
substream->runtime = runtime;
974975
substream->private_data = pcm->private_data;
@@ -1002,6 +1003,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
10021003
} else {
10031004
substream->runtime = NULL;
10041005
}
1006+
mutex_destroy(&runtime->buffer_mutex);
10051007
kfree(runtime);
10061008
put_pid(substream->pid);
10071009
substream->pid = NULL;

sound/core/pcm_native.c

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -685,33 +685,40 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
685685
return 0;
686686
}
687687

688+
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
689+
#define is_oss_stream(substream) ((substream)->oss.oss)
690+
#else
691+
#define is_oss_stream(substream) false
692+
#endif
693+
688694
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
689695
struct snd_pcm_hw_params *params)
690696
{
691697
struct snd_pcm_runtime *runtime;
692-
int err, usecs;
698+
int err = 0, usecs;
693699
unsigned int bits;
694700
snd_pcm_uframes_t frames;
695701

696702
if (PCM_RUNTIME_CHECK(substream))
697703
return -ENXIO;
698704
runtime = substream->runtime;
705+
mutex_lock(&runtime->buffer_mutex);
699706
snd_pcm_stream_lock_irq(substream);
700707
switch (runtime->status->state) {
701708
case SNDRV_PCM_STATE_OPEN:
702709
case SNDRV_PCM_STATE_SETUP:
703710
case SNDRV_PCM_STATE_PREPARED:
711+
if (!is_oss_stream(substream) &&
712+
atomic_read(&substream->mmap_count))
713+
err = -EBADFD;
704714
break;
705715
default:
706-
snd_pcm_stream_unlock_irq(substream);
707-
return -EBADFD;
716+
err = -EBADFD;
717+
break;
708718
}
709719
snd_pcm_stream_unlock_irq(substream);
710-
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
711-
if (!substream->oss.oss)
712-
#endif
713-
if (atomic_read(&substream->mmap_count))
714-
return -EBADFD;
720+
if (err)
721+
goto unlock;
715722

716723
snd_pcm_sync_stop(substream, true);
717724

@@ -799,16 +806,21 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
799806
if (usecs >= 0)
800807
cpu_latency_qos_add_request(&substream->latency_pm_qos_req,
801808
usecs);
802-
return 0;
809+
err = 0;
803810
_error:
804-
/* hardware might be unusable from this time,
805-
so we force application to retry to set
806-
the correct hardware parameter settings */
807-
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
808-
if (substream->ops->hw_free != NULL)
809-
substream->ops->hw_free(substream);
810-
if (substream->managed_buffer_alloc)
811-
snd_pcm_lib_free_pages(substream);
811+
if (err) {
812+
/* hardware might be unusable from this time,
813+
* so we force application to retry to set
814+
* the correct hardware parameter settings
815+
*/
816+
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
817+
if (substream->ops->hw_free != NULL)
818+
substream->ops->hw_free(substream);
819+
if (substream->managed_buffer_alloc)
820+
snd_pcm_lib_free_pages(substream);
821+
}
822+
unlock:
823+
mutex_unlock(&runtime->buffer_mutex);
812824
return err;
813825
}
814826

@@ -848,26 +860,31 @@ static int do_hw_free(struct snd_pcm_substream *substream)
848860
static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
849861
{
850862
struct snd_pcm_runtime *runtime;
851-
int result;
863+
int result = 0;
852864

853865
if (PCM_RUNTIME_CHECK(substream))
854866
return -ENXIO;
855867
runtime = substream->runtime;
868+
mutex_lock(&runtime->buffer_mutex);
856869
snd_pcm_stream_lock_irq(substream);
857870
switch (runtime->status->state) {
858871
case SNDRV_PCM_STATE_SETUP:
859872
case SNDRV_PCM_STATE_PREPARED:
873+
if (atomic_read(&substream->mmap_count))
874+
result = -EBADFD;
860875
break;
861876
default:
862-
snd_pcm_stream_unlock_irq(substream);
863-
return -EBADFD;
877+
result = -EBADFD;
878+
break;
864879
}
865880
snd_pcm_stream_unlock_irq(substream);
866-
if (atomic_read(&substream->mmap_count))
867-
return -EBADFD;
881+
if (result)
882+
goto unlock;
868883
result = do_hw_free(substream);
869884
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
870885
cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
886+
unlock:
887+
mutex_unlock(&runtime->buffer_mutex);
871888
return result;
872889
}
873890

0 commit comments

Comments
 (0)