Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7e1d1456 authored by Takashi Iwai's avatar Takashi Iwai Committed by Greg Kroah-Hartman
Browse files

ALSA: pcm: Fix potential data race at PCM memory allocation helpers



[ Upstream commit bd55842ed998a622ba6611fe59b3358c9f76773d ]

The PCM memory allocation helpers have a sanity check against too many
buffer allocations.  However, the check is performed without a proper
lock and the allocation isn't serialized; this allows user to allocate
more memories than predefined max size.

Practically seen, this isn't really a big problem, as it's more or
less some "soft limit" as a sanity check, and it's not possible to
allocate unlimitedly.  But it's still better to address this for more
consistent behavior.

The patch covers the size check in do_alloc_pages() with the
card->memory_mutex, and increases the allocated size there for
preventing the further overflow.  When the actual allocation fails,
the size is decreased accordingly.

Reported-by: default avatarBassCheck <bass@buaa.edu.cn>
Reported-by: default avatarTuo Li <islituo@gmail.com>
Link: https://lore.kernel.org/r/CADm8Tek6t0WedK+3Y6rbE5YEt19tML8BUL45N2ji4ZAz1KcN_A@mail.gmail.com


Reviewed-by: default avatarJaroslav Kysela <perex@perex.cz>
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20230703112430.30634-1-tiwai@suse.de


Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 140797d0
Loading
Loading
Loading
Loading
+36 −8
Original line number Diff line number Diff line
@@ -31,14 +31,40 @@ static unsigned long max_alloc_per_card = 32UL * 1024UL * 1024UL;
module_param(max_alloc_per_card, ulong, 0644);
MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");

static void __update_allocated_size(struct snd_card *card, ssize_t bytes)
{
	card->total_pcm_alloc_bytes += bytes;
}

static void update_allocated_size(struct snd_card *card, ssize_t bytes)
{
	mutex_lock(&card->memory_mutex);
	__update_allocated_size(card, bytes);
	mutex_unlock(&card->memory_mutex);
}

static void decrease_allocated_size(struct snd_card *card, size_t bytes)
{
	mutex_lock(&card->memory_mutex);
	WARN_ON(card->total_pcm_alloc_bytes < bytes);
	__update_allocated_size(card, -(ssize_t)bytes);
	mutex_unlock(&card->memory_mutex);
}

static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
			  size_t size, struct snd_dma_buffer *dmab)
{
	int err;

	/* check and reserve the requested size */
	mutex_lock(&card->memory_mutex);
	if (max_alloc_per_card &&
	    card->total_pcm_alloc_bytes + size > max_alloc_per_card)
	    card->total_pcm_alloc_bytes + size > max_alloc_per_card) {
		mutex_unlock(&card->memory_mutex);
		return -ENOMEM;
	}
	__update_allocated_size(card, size);
	mutex_unlock(&card->memory_mutex);

	if (IS_ENABLED(CONFIG_SND_DMA_SGBUF) &&
	    (type == SNDRV_DMA_TYPE_DEV_SG || type == SNDRV_DMA_TYPE_DEV_UC_SG) &&
@@ -53,9 +79,14 @@ static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,

	err = snd_dma_alloc_pages(type, dev, size, dmab);
	if (!err) {
		mutex_lock(&card->memory_mutex);
		card->total_pcm_alloc_bytes += dmab->bytes;
		mutex_unlock(&card->memory_mutex);
		/* the actual allocation size might be bigger than requested,
		 * and we need to correct the account
		 */
		if (dmab->bytes != size)
			update_allocated_size(card, dmab->bytes - size);
	} else {
		/* take back on allocation failure */
		decrease_allocated_size(card, size);
	}
	return err;
}
@@ -64,10 +95,7 @@ static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab)
{
	if (!dmab->area)
		return;
	mutex_lock(&card->memory_mutex);
	WARN_ON(card->total_pcm_alloc_bytes < dmab->bytes);
	card->total_pcm_alloc_bytes -= dmab->bytes;
	mutex_unlock(&card->memory_mutex);
	decrease_allocated_size(card, dmab->bytes);
	snd_dma_free_pages(dmab);
	dmab->area = NULL;
}