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 <bass@buaa.edu.cn> Reported-by: Tuo Li <islituo@gmail.com> Link: https://lore.kernel.org/r/CADm8Tek6t0WedK+3Y6rbE5YEt19tML8BUL45N2ji4ZAz1KcN_A@mail.gmail.com Reviewed-by: Jaroslav Kysela <perex@perex.cz> Cc: <stable@vger.kernel.org> Link: https://lore.kernel.org/r/20230703112430.30634-1-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Родитель
1f4a08fed4
Коммит
bd55842ed9
|
@ -31,15 +31,41 @@ static unsigned long max_alloc_per_card = 32UL * 1024UL * 1024UL;
|
||||||
module_param(max_alloc_per_card, ulong, 0644);
|
module_param(max_alloc_per_card, ulong, 0644);
|
||||||
MODULE_PARM_DESC(max_alloc_per_card, "Max total allocation bytes per card.");
|
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,
|
static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
|
||||||
int str, size_t size, struct snd_dma_buffer *dmab)
|
int str, size_t size, struct snd_dma_buffer *dmab)
|
||||||
{
|
{
|
||||||
enum dma_data_direction dir;
|
enum dma_data_direction dir;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
/* check and reserve the requested size */
|
||||||
|
mutex_lock(&card->memory_mutex);
|
||||||
if (max_alloc_per_card &&
|
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;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
__update_allocated_size(card, size);
|
||||||
|
mutex_unlock(&card->memory_mutex);
|
||||||
|
|
||||||
if (str == SNDRV_PCM_STREAM_PLAYBACK)
|
if (str == SNDRV_PCM_STREAM_PLAYBACK)
|
||||||
dir = DMA_TO_DEVICE;
|
dir = DMA_TO_DEVICE;
|
||||||
|
@ -47,9 +73,14 @@ static int do_alloc_pages(struct snd_card *card, int type, struct device *dev,
|
||||||
dir = DMA_FROM_DEVICE;
|
dir = DMA_FROM_DEVICE;
|
||||||
err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
|
err = snd_dma_alloc_dir_pages(type, dev, dir, size, dmab);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
mutex_lock(&card->memory_mutex);
|
/* the actual allocation size might be bigger than requested,
|
||||||
card->total_pcm_alloc_bytes += dmab->bytes;
|
* and we need to correct the account
|
||||||
mutex_unlock(&card->memory_mutex);
|
*/
|
||||||
|
if (dmab->bytes != size)
|
||||||
|
update_allocated_size(card, dmab->bytes - size);
|
||||||
|
} else {
|
||||||
|
/* take back on allocation failure */
|
||||||
|
decrease_allocated_size(card, size);
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
@ -58,10 +89,7 @@ static void do_free_pages(struct snd_card *card, struct snd_dma_buffer *dmab)
|
||||||
{
|
{
|
||||||
if (!dmab->area)
|
if (!dmab->area)
|
||||||
return;
|
return;
|
||||||
mutex_lock(&card->memory_mutex);
|
decrease_allocated_size(card, dmab->bytes);
|
||||||
WARN_ON(card->total_pcm_alloc_bytes < dmab->bytes);
|
|
||||||
card->total_pcm_alloc_bytes -= dmab->bytes;
|
|
||||||
mutex_unlock(&card->memory_mutex);
|
|
||||||
snd_dma_free_pages(dmab);
|
snd_dma_free_pages(dmab);
|
||||||
dmab->area = NULL;
|
dmab->area = NULL;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче