提交 450bee2d 编写于 作者: T Takashi Iwai 提交者: Yongqiang Liu

ALSA: pcm: Fix races among concurrent hw_params and hw_free calls

stable inclusion
from linux-4.19.243
commit 9cb6c40a6ebe4a0cfc9d6a181958211682cffea9
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I50WAS
CVE: CVE-2022-1048

--------------------------------

commit 92ee3c60 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: NHu Jiahui <kirin.say@gmail.com>
Cc: <stable@vger.kernel.org>
Reviewed-by: NJaroslav Kysela <perex@perex.cz>
Link: https://lore.kernel.org/r/20220322170720.3529-2-tiwai@suse.deSigned-off-by: NTakashi Iwai <tiwai@suse.de>
[OP: backport to 4.19: adjusted context]
Signed-off-by: NOvidiu Panait <ovidiu.panait@windriver.com>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: NRui Xiang <rui.xiang@huawei.com>
Reviewed-by: Nzhangxiaoxu <zhangxiaoxu5@huawei.com>
Reviewed-by: NXiu Jianfeng <xiujianfeng@huawei.com>
Signed-off-by: NYongqiang Liu <liuyongqiang13@huawei.com>
上级 f87d42c7
...@@ -404,6 +404,7 @@ struct snd_pcm_runtime { ...@@ -404,6 +404,7 @@ struct snd_pcm_runtime {
wait_queue_head_t sleep; /* poll sleep */ wait_queue_head_t sleep; /* poll sleep */
wait_queue_head_t tsleep; /* transfer sleep */ wait_queue_head_t tsleep; /* transfer sleep */
struct fasync_struct *fasync; struct fasync_struct *fasync;
struct mutex buffer_mutex; /* protect for buffer changes */
/* -- private section -- */ /* -- private section -- */
void *private_data; void *private_data;
......
...@@ -1027,6 +1027,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, ...@@ -1027,6 +1027,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
init_waitqueue_head(&runtime->tsleep); init_waitqueue_head(&runtime->tsleep);
runtime->status->state = SNDRV_PCM_STATE_OPEN; runtime->status->state = SNDRV_PCM_STATE_OPEN;
mutex_init(&runtime->buffer_mutex);
substream->runtime = runtime; substream->runtime = runtime;
substream->private_data = pcm->private_data; substream->private_data = pcm->private_data;
...@@ -1058,6 +1059,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) ...@@ -1058,6 +1059,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
substream->runtime = NULL; substream->runtime = NULL;
if (substream->timer) if (substream->timer)
spin_unlock_irq(&substream->timer->lock); spin_unlock_irq(&substream->timer->lock);
mutex_destroy(&runtime->buffer_mutex);
kfree(runtime); kfree(runtime);
put_pid(substream->pid); put_pid(substream->pid);
substream->pid = NULL; substream->pid = NULL;
......
...@@ -666,33 +666,40 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm, ...@@ -666,33 +666,40 @@ static int snd_pcm_hw_params_choose(struct snd_pcm_substream *pcm,
return 0; return 0;
} }
#if IS_ENABLED(CONFIG_SND_PCM_OSS)
#define is_oss_stream(substream) ((substream)->oss.oss)
#else
#define is_oss_stream(substream) false
#endif
static int snd_pcm_hw_params(struct snd_pcm_substream *substream, static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
int err, usecs; int err = 0, usecs;
unsigned int bits; unsigned int bits;
snd_pcm_uframes_t frames; snd_pcm_uframes_t frames;
if (PCM_RUNTIME_CHECK(substream)) if (PCM_RUNTIME_CHECK(substream))
return -ENXIO; return -ENXIO;
runtime = substream->runtime; runtime = substream->runtime;
mutex_lock(&runtime->buffer_mutex);
snd_pcm_stream_lock_irq(substream); snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) { switch (runtime->status->state) {
case SNDRV_PCM_STATE_OPEN: case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_SETUP:
case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_PREPARED:
if (!is_oss_stream(substream) &&
atomic_read(&substream->mmap_count))
err = -EBADFD;
break; break;
default: default:
snd_pcm_stream_unlock_irq(substream); err = -EBADFD;
return -EBADFD; break;
} }
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
#if IS_ENABLED(CONFIG_SND_PCM_OSS) if (err)
if (!substream->oss.oss) goto unlock;
#endif
if (atomic_read(&substream->mmap_count))
return -EBADFD;
params->rmask = ~0U; params->rmask = ~0U;
err = snd_pcm_hw_refine(substream, params); err = snd_pcm_hw_refine(substream, params);
...@@ -764,14 +771,19 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -764,14 +771,19 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
if ((usecs = period_to_usecs(runtime)) >= 0) if ((usecs = period_to_usecs(runtime)) >= 0)
pm_qos_add_request(&substream->latency_pm_qos_req, pm_qos_add_request(&substream->latency_pm_qos_req,
PM_QOS_CPU_DMA_LATENCY, usecs); PM_QOS_CPU_DMA_LATENCY, usecs);
return 0; err = 0;
_error: _error:
if (err) {
/* hardware might be unusable from this time, /* hardware might be unusable from this time,
so we force application to retry to set * so we force application to retry to set
the correct hardware parameter settings */ * the correct hardware parameter settings
*/
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
if (substream->ops->hw_free != NULL) if (substream->ops->hw_free != NULL)
substream->ops->hw_free(substream); substream->ops->hw_free(substream);
}
unlock:
mutex_unlock(&runtime->buffer_mutex);
return err; return err;
} }
...@@ -804,22 +816,27 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) ...@@ -804,22 +816,27 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
if (PCM_RUNTIME_CHECK(substream)) if (PCM_RUNTIME_CHECK(substream))
return -ENXIO; return -ENXIO;
runtime = substream->runtime; runtime = substream->runtime;
mutex_lock(&runtime->buffer_mutex);
snd_pcm_stream_lock_irq(substream); snd_pcm_stream_lock_irq(substream);
switch (runtime->status->state) { switch (runtime->status->state) {
case SNDRV_PCM_STATE_SETUP: case SNDRV_PCM_STATE_SETUP:
case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_PREPARED:
if (atomic_read(&substream->mmap_count))
result = -EBADFD;
break; break;
default: default:
snd_pcm_stream_unlock_irq(substream); result = -EBADFD;
return -EBADFD; break;
} }
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
if (atomic_read(&substream->mmap_count)) if (result)
return -EBADFD; goto unlock;
if (substream->ops->hw_free) if (substream->ops->hw_free)
result = substream->ops->hw_free(substream); result = substream->ops->hw_free(substream);
snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN); snd_pcm_set_state(substream, SNDRV_PCM_STATE_OPEN);
pm_qos_remove_request(&substream->latency_pm_qos_req); pm_qos_remove_request(&substream->latency_pm_qos_req);
unlock:
mutex_unlock(&runtime->buffer_mutex);
return result; return result;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册