| From: Takashi Iwai <tiwai@suse.de> |
| Date: Mon, 2 Apr 2018 22:41:43 +0200 |
| Subject: ALSA: pcm: Fix UAF at PCM release via PCM timer access |
| |
| commit a820ccbe21e8ce8e86c39cd1d3bc8c7d1cbb949b upstream. |
| |
| The PCM runtime object is created and freed dynamically at PCM stream |
| open / close time. This is tracked via substream->runtime, and it's |
| cleared at snd_pcm_detach_substream(). |
| |
| The runtime object assignment is protected by PCM open_mutex, so for |
| all PCM operations, it's safely handled. However, each PCM substream |
| provides also an ALSA timer interface, and user-space can access to |
| this while closing a PCM substream. This may eventually lead to a |
| UAF, as snd_pcm_timer_resolution() tries to access the runtime while |
| clearing it in other side. |
| |
| Fortunately, it's the only concurrent access from the PCM timer, and |
| it merely reads runtime->timer_resolution field. So, we can avoid the |
| race by reordering kfree() and wrapping the substream->runtime |
| clearance with the corresponding timer lock. |
| |
| Reported-by: syzbot+8e62ff4e07aa2ce87826@syzkaller.appspotmail.com |
| Signed-off-by: Takashi Iwai <tiwai@suse.de> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| sound/core/pcm.c | 8 +++++++- |
| 1 file changed, 7 insertions(+), 1 deletion(-) |
| |
| --- a/sound/core/pcm.c |
| +++ b/sound/core/pcm.c |
| @@ -28,6 +28,7 @@ |
| #include <sound/core.h> |
| #include <sound/minors.h> |
| #include <sound/pcm.h> |
| +#include <sound/timer.h> |
| #include <sound/control.h> |
| #include <sound/info.h> |
| |
| @@ -1002,8 +1003,13 @@ void snd_pcm_detach_substream(struct snd |
| #ifdef CONFIG_SND_PCM_XRUN_DEBUG |
| kfree(runtime->hwptr_log); |
| #endif |
| - kfree(runtime); |
| + /* Avoid concurrent access to runtime via PCM timer interface */ |
| + if (substream->timer) |
| + spin_lock_irq(&substream->timer->lock); |
| substream->runtime = NULL; |
| + if (substream->timer) |
| + spin_unlock_irq(&substream->timer->lock); |
| + kfree(runtime); |
| put_pid(substream->pid); |
| substream->pid = NULL; |
| substream->pstr->substream_opened--; |