Skip to content

Commit 0f6947f

Browse files
tiwaigregkh
authored andcommitted
ALSA: pcm: Fix races among concurrent hw_params and hw_free calls
commit 92ee3c6 upstream. 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 014c81d commit 0f6947f

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
@@ -398,6 +398,7 @@ struct snd_pcm_runtime {
398398
wait_queue_head_t tsleep; /* transfer sleep */
399399
struct fasync_struct *fasync;
400400
bool stop_operating; /* sync_stop will be called */
401+
struct mutex buffer_mutex; /* protect for buffer changes */
401402

402403
/* -- private section -- */
403404
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
@@ -667,33 +667,40 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
667667
return 0;
668668
}
669669

670+
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
671+
#define is_oss_stream(substream) ((substream)->oss.oss)
672+
#else
673+
#define is_oss_stream(substream) false
674+
#endif
675+
670676
static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
671677
struct snd_pcm_hw_params *params)
672678
{
673679
struct snd_pcm_runtime *runtime;
674-
int err, usecs;
680+
int err = 0, usecs;
675681
unsigned int bits;
676682
snd_pcm_uframes_t frames;
677683

678684
if (PCM_RUNTIME_CHECK(substream))
679685
return -ENXIO;
680686
runtime = substream->runtime;
687+
mutex_lock(&runtime->buffer_mutex);
681688
snd_pcm_stream_lock_irq(substream);
682689
switch (runtime->status->state) {
683690
case SNDRV_PCM_STATE_OPEN:
684691
case SNDRV_PCM_STATE_SETUP:
685692
case SNDRV_PCM_STATE_PREPARED:
693+
if (!is_oss_stream(substream) &&
694+
atomic_read(&substream->mmap_count))
695+
err = -EBADFD;
686696
break;
687697
default:
688-
snd_pcm_stream_unlock_irq(substream);
689-
return -EBADFD;
698+
err = -EBADFD;
699+
break;
690700
}
691701
snd_pcm_stream_unlock_irq(substream);
692-
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
693-
if (!substream->oss.oss)
694-
#endif
695-
if (atomic_read(&substream->mmap_count))
696-
return -EBADFD;
702+
if (err)
703+
goto unlock;
697704

698705
snd_pcm_sync_stop(substream, true);
699706

@@ -780,16 +787,21 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
780787
if ((usecs = period_to_usecs(runtime)) >= 0)
781788
cpu_latency_qos_add_request(&substream->latency_pm_qos_req,
782789
usecs);
783-
return 0;
790+
err = 0;
784791
_error:
785-
/* hardware might be unusable from this time,
786-
so we force application to retry to set
787-
the correct hardware parameter settings */
788-
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
789-
if (substream->ops->hw_free != NULL)
790-
substream->ops->hw_free(substream);
791-
if (substream->managed_buffer_alloc)
792-
snd_pcm_lib_free_pages(substream);
792+
if (err) {
793+
/* hardware might be unusable from this time,
794+
* so we force application to retry to set
795+
* the correct hardware parameter settings
796+
*/
797+
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
798+
if (substream->ops->hw_free != NULL)
799+
substream->ops->hw_free(substream);
800+
if (substream->managed_buffer_alloc)
801+
snd_pcm_lib_free_pages(substream);
802+
}
803+
unlock:
804+
mutex_unlock(&runtime->buffer_mutex);
793805
return err;
794806
}
795807

@@ -829,26 +841,31 @@ static int do_hw_free(struct snd_pcm_substream *substream)
829841
static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
830842
{
831843
struct snd_pcm_runtime *runtime;
832-
int result;
844+
int result = 0;
833845

834846
if (PCM_RUNTIME_CHECK(substream))
835847
return -ENXIO;
836848
runtime = substream->runtime;
849+
mutex_lock(&runtime->buffer_mutex);
837850
snd_pcm_stream_lock_irq(substream);
838851
switch (runtime->status->state) {
839852
case SNDRV_PCM_STATE_SETUP:
840853
case SNDRV_PCM_STATE_PREPARED:
854+
if (atomic_read(&substream->mmap_count))
855+
result = -EBADFD;
841856
break;
842857
default:
843-
snd_pcm_stream_unlock_irq(substream);
844-
return -EBADFD;
858+
result = -EBADFD;
859+
break;
845860
}
846861
snd_pcm_stream_unlock_irq(substream);
847-
if (atomic_read(&substream->mmap_count))
848-
return -EBADFD;
862+
if (result)
863+
goto unlock;
849864
result = do_hw_free(substream);
850865
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
851866
cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
867+
unlock:
868+
mutex_unlock(&runtime->buffer_mutex);
852869
return result;
853870
}
854871

0 commit comments

Comments
 (0)