History

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.

Takashi Iwai committed 4 years ago

Commit 92ee3c61 parent 646b9073 files · +42 −22Browse files

Unified · Split

3 files
  1. include/sound/pcm.h +1 −0
  2. sound/core/pcm.c +2 −0
  3. sound/core/pcm_native.c +39 −22
include/sound/pcm.h+1−0
@@ -401,6 +401,7 @@ struct snd_pcm_runtime { · 400 unmodified lines
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+2−0
@@ -969,6 +969,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, · 968 unmodified lines
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) · 27 unmodified lines
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+39−22
@@ -685,33 +685,40 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, · 684 unmodified lines
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, · 81 unmodified lines
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) · 33 unmodified lines
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

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t