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
401 wait_queue_head_t tsleep; /* transfer sleep */401 wait_queue_head_t tsleep; /* transfer sleep */
402 struct fasync_struct *fasync;402 struct fasync_struct *fasync;
403 bool stop_operating; /* sync_stop will be called */403 bool stop_operating; /* sync_stop will be called */
404 struct mutex buffer_mutex; /* protect for buffer changes */
404 405
405 /* -- private section -- */406 /* -- private section -- */
406 void *private_data;407 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
969 init_waitqueue_head(&runtime->tsleep);969 init_waitqueue_head(&runtime->tsleep);
970 970
971 runtime->status->state = SNDRV_PCM_STATE_OPEN;971 runtime->status->state = SNDRV_PCM_STATE_OPEN;
972 mutex_init(&runtime->buffer_mutex);
972 973
973 substream->runtime = runtime;974 substream->runtime = runtime;
974 substream->private_data = pcm->private_data;975 substream->private_data = pcm->private_data;
@@ -1002,6 +1003,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) · 27 unmodified lines
1002 } else {1003 } else {
1003 substream->runtime = NULL;1004 substream->runtime = NULL;
1004 }1005 }
1006 mutex_destroy(&runtime->buffer_mutex);
1005 kfree(runtime);1007 kfree(runtime);
1006 put_pid(substream->pid);1008 put_pid(substream->pid);
1007 substream->pid = NULL;1009 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
685 return 0;685 return 0;
686}686}
687 687
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
688static int snd_pcm_hw_params(struct snd_pcm_substream *substream,694static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
689 struct snd_pcm_hw_params *params)695 struct snd_pcm_hw_params *params)
690{696{
691 struct snd_pcm_runtime *runtime;697 struct snd_pcm_runtime *runtime;
692 int err, usecs;698 int err = 0, usecs;
693 unsigned int bits;699 unsigned int bits;
694 snd_pcm_uframes_t frames;700 snd_pcm_uframes_t frames;
695 701
696 if (PCM_RUNTIME_CHECK(substream))702 if (PCM_RUNTIME_CHECK(substream))
697 return -ENXIO;703 return -ENXIO;
698 runtime = substream->runtime;704 runtime = substream->runtime;
705 mutex_lock(&runtime->buffer_mutex);
699 snd_pcm_stream_lock_irq(substream);706 snd_pcm_stream_lock_irq(substream);
700 switch (runtime->status->state) {707 switch (runtime->status->state) {
701 case SNDRV_PCM_STATE_OPEN:708 case SNDRV_PCM_STATE_OPEN:
702 case SNDRV_PCM_STATE_SETUP:709 case SNDRV_PCM_STATE_SETUP:
703 case SNDRV_PCM_STATE_PREPARED:710 case SNDRV_PCM_STATE_PREPARED:
711 if (!is_oss_stream(substream) &&
712 atomic_read(&substream->mmap_count))
713 err = -EBADFD;
704 break;714 break;
705 default:715 default:
706 snd_pcm_stream_unlock_irq(substream);716 err = -EBADFD;
707 return -EBADFD;717 break;
708 }718 }
709 snd_pcm_stream_unlock_irq(substream);719 snd_pcm_stream_unlock_irq(substream);
710#if IS_ENABLED(CONFIG_SND_PCM_OSS)720 if (err)
711 if (!substream->oss.oss)721 goto unlock;
712#endif
713 if (atomic_read(&substream->mmap_count))
714 return -EBADFD;
715 722
716 snd_pcm_sync_stop(substream, true);723 snd_pcm_sync_stop(substream, true);
717 724
@@ -799,16 +806,21 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, · 81 unmodified lines
799 if (usecs >= 0)806 if (usecs >= 0)
800 cpu_latency_qos_add_request(&substream->latency_pm_qos_req,807 cpu_latency_qos_add_request(&substream->latency_pm_qos_req,
801 usecs);808 usecs);
802 return 0;809 err = 0;
803 _error:810 _error:
804 /* hardware might be unusable from this time,811 if (err) {
805 so we force application to retry to set812 /* hardware might be unusable from this time,
806 the correct hardware parameter settings */813 * so we force application to retry to set
807 snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);814 * the correct hardware parameter settings
808 if (substream->ops->hw_free != NULL)815 */
809 substream->ops->hw_free(substream);816 snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
810 if (substream->managed_buffer_alloc)817 if (substream->ops->hw_free != NULL)
811 snd_pcm_lib_free_pages(substream);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);
812 return err;824 return err;
813}825}
814 826
@@ -848,26 +860,31 @@ static int do_hw_free(struct snd_pcm_substream *substream) · 33 unmodified lines
848static int snd_pcm_hw_free(struct snd_pcm_substream *substream)860static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
849{861{
850 struct snd_pcm_runtime *runtime;862 struct snd_pcm_runtime *runtime;
851 int result;863 int result = 0;
852 864
853 if (PCM_RUNTIME_CHECK(substream))865 if (PCM_RUNTIME_CHECK(substream))
854 return -ENXIO;866 return -ENXIO;
855 runtime = substream->runtime;867 runtime = substream->runtime;
868 mutex_lock(&runtime->buffer_mutex);
856 snd_pcm_stream_lock_irq(substream);869 snd_pcm_stream_lock_irq(substream);
857 switch (runtime->status->state) {870 switch (runtime->status->state) {
858 case SNDRV_PCM_STATE_SETUP:871 case SNDRV_PCM_STATE_SETUP:
859 case SNDRV_PCM_STATE_PREPARED:872 case SNDRV_PCM_STATE_PREPARED:
873 if (atomic_read(&substream->mmap_count))
874 result = -EBADFD;
860 break;875 break;
861 default:876 default:
862 snd_pcm_stream_unlock_irq(substream);877 result = -EBADFD;
863 return -EBADFD;878 break;
864 }879 }
865 snd_pcm_stream_unlock_irq(substream);880 snd_pcm_stream_unlock_irq(substream);
866 if (atomic_read(&substream->mmap_count))881 if (result)
867 return -EBADFD;882 goto unlock;
868 result = do_hw_free(substream);883 result = do_hw_free(substream);
869 snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);884 snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
870 cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);885 cpu_latency_qos_remove_request(&substream->latency_pm_qos_req);
886 unlock:
887 mutex_unlock(&runtime->buffer_mutex);
871 return result;888 return result;
872}889}
873 890

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