Skip to content

Commit bd55842

Browse files
committed
ALSA: pcm: Fix potential data race at PCM memory allocation helpers
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: BassCheck <[email protected]> Reported-by: Tuo Li <[email protected]> Link: https://lore.kernel.org/r/CADm8Tek6t0WedK+3Y6rbE5YEt19tML8BUL45N2ji4ZAz1KcN_A@mail.gmail.com Reviewed-by: Jaroslav Kysela <[email protected]> Cc: <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent 1f4a08f commit bd55842

File tree

1 file changed

+36
-8
lines changed

1 file changed

+36
-8
lines changed

sound/core/pcm_memory.c

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,56 @@ static unsigned long max_alloc_per_card = 32UL * 1024UL * 1024UL;
3131
module_param(max_alloc_per_card, ulong, 0644);
3232
MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");
3333

34+
static void __update_allocated_size(struct snd_card *card, ssize_t bytes)
35+
{
36+
card->total_pcm_alloc_bytes += bytes;
37+
}
38+
39+
static void update_allocated_size(struct snd_card *card, ssize_t bytes)
40+
{
41+
mutex_lock(&card->memory_mutex);
42+
__update_allocated_size(card, bytes);
43+
mutex_unlock(&card->memory_mutex);
44+
}
45+
46+
static void decrease_allocated_size(struct snd_card *card, size_t bytes)
47+
{
48+
mutex_lock(&card->memory_mutex);
49+
WARN_ON(card->total_pcm_alloc_bytes < bytes);
50+
__update_allocated_size(card, -(ssize_t)bytes);
51+
mutex_unlock(&card->memory_mutex);
52+
}
53+
3454
static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
3555
int str, size_t size, struct snd_dma_buffer *dmab)
3656
{
3757
enum dma_data_direction dir;
3858
int err;
3959

60+
/* check and reserve the requested size */
61+
mutex_lock(&card->memory_mutex);
4062
if (max_alloc_per_card &&
41-
card->total_pcm_alloc_bytes + size > max_alloc_per_card)
63+
card->total_pcm_alloc_bytes + size > max_alloc_per_card) {
64+
mutex_unlock(&card->memory_mutex);
4265
return -ENOMEM;
66+
}
67+
__update_allocated_size(card, size);
68+
mutex_unlock(&card->memory_mutex);
4369

4470
if (str == SNDRV_PCM_STREAM_PLAYBACK)
4571
dir = DMA_TO_DEVICE;
4672
else
4773
dir = DMA_FROM_DEVICE;
4874
err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
4975
if (!err) {
50-
mutex_lock(&card->memory_mutex);
51-
card->total_pcm_alloc_bytes += dmab->bytes;
52-
mutex_unlock(&card->memory_mutex);
76+
/* the actual allocation size might be bigger than requested,
77+
* and we need to correct the account
78+
*/
79+
if (dmab->bytes != size)
80+
update_allocated_size(card, dmab->bytes - size);
81+
} else {
82+
/* take back on allocation failure */
83+
decrease_allocated_size(card, size);
5384
}
5485
return err;
5586
}
@@ -58,10 +89,7 @@ static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab)
5889
{
5990
if (!dmab->area)
6091
return;
61-
mutex_lock(&card->memory_mutex);
62-
WARN_ON(card->total_pcm_alloc_bytes < dmab->bytes);
63-
card->total_pcm_alloc_bytes -= dmab->bytes;
64-
mutex_unlock(&card->memory_mutex);
92+
decrease_allocated_size(card, dmab->bytes);
6593
snd_dma_free_pages(dmab);
6694
dmab->area = NULL;
6795
}

0 commit comments

Comments
 (0)