Commit 66f2d19f authored by Takashi Iwai's avatar Takashi Iwai

ALSA: pcm: Fix memory leak at closing a stream without hw_free

ALSA PCM core recently introduced a new managed PCM buffer allocation
mode that does allocate / free automatically at hw_params and
hw_free.  However, it overlooked the code path directly calling
hw_free PCM ops at releasing the PCM substream, and it may result in a
memory leak as spotted by syzkaller when no buffer preallocation is
used (e.g. vmalloc buffer).

This patch papers over it with a slight refactoring.  The hw_free ops
call and relevant tasks are unified in a new helper function, and call
it from both places.

Fixes: 0dba808e ("ALSA: pcm: Introduce managed buffer allocation mode")
Reported-by: syzbot+30edd0f34bfcdc548ac4@syzkaller.appspotmail.com
Cc: <stable@vger.kernel.org>
Link: https://lore.kernel.org/r/20200129195907.12197-1-tiwai@suse.deSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 46b770f7
...@@ -786,10 +786,22 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream, ...@@ -786,10 +786,22 @@ static int snd_pcm_hw_params_user(struct snd_pcm_substream *substream,
return err; return err;
} }
static int do_hw_free(struct snd_pcm_substream *substream)
{
int result = 0;
snd_pcm_sync_stop(substream);
if (substream->ops->hw_free)
result = substream->ops->hw_free(substream);
if (substream->managed_buffer_alloc)
snd_pcm_lib_free_pages(substream);
return result;
}
static int snd_pcm_hw_free(struct snd_pcm_substream *substream) static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
int result = 0; int result;
if (PCM_RUNTIME_CHECK(substream)) if (PCM_RUNTIME_CHECK(substream))
return -ENXIO; return -ENXIO;
...@@ -806,11 +818,7 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream) ...@@ -806,11 +818,7 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
snd_pcm_stream_unlock_irq(substream); snd_pcm_stream_unlock_irq(substream);
if (atomic_read(&substream->mmap_count)) if (atomic_read(&substream->mmap_count))
return -EBADFD; return -EBADFD;
snd_pcm_sync_stop(substream); result = do_hw_free(substream);
if (substream->ops->hw_free)
result = substream->ops->hw_free(substream);
if (substream->managed_buffer_alloc)
snd_pcm_lib_free_pages(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);
return result; return result;
...@@ -2529,9 +2537,7 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream) ...@@ -2529,9 +2537,7 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
snd_pcm_drop(substream); snd_pcm_drop(substream);
if (substream->hw_opened) { if (substream->hw_opened) {
if (substream->ops->hw_free && do_hw_free(substream);
substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
substream->ops->hw_free(substream);
substream->ops->close(substream); substream->ops->close(substream);
substream->hw_opened = 0; substream->hw_opened = 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment