ALSA: pcm_dmaengine: Properly synchronize DMA on shutdown

Use the new dmaengine_synchronize() function to make sure that all complete
callbacks have finished running before the runtime data, which is accessed
in the completed callback, is freed.

This fixes a long standing use-after-free race condition that has been
observed on some systems.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Reviewed-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
This commit is contained in:
Lars-Peter Clausen 2015-10-20 11:46:31 +02:00 committed by Vinod Koul
parent 860dd64c43
commit bc0e734516
1 changed files with 6 additions and 3 deletions

View File

@ -202,13 +202,13 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
if (runtime->info & SNDRV_PCM_INFO_PAUSE) if (runtime->info & SNDRV_PCM_INFO_PAUSE)
dmaengine_pause(prtd->dma_chan); dmaengine_pause(prtd->dma_chan);
else else
dmaengine_terminate_all(prtd->dma_chan); dmaengine_terminate_async(prtd->dma_chan);
break; break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dmaengine_pause(prtd->dma_chan); dmaengine_pause(prtd->dma_chan);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
dmaengine_terminate_all(prtd->dma_chan); dmaengine_terminate_async(prtd->dma_chan);
break; break;
default: default:
return -EINVAL; return -EINVAL;
@ -346,6 +346,7 @@ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream)
{ {
struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
dmaengine_synchronize(prtd->dma_chan);
kfree(prtd); kfree(prtd);
return 0; return 0;
@ -362,9 +363,11 @@ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream)
{ {
struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
dmaengine_synchronize(prtd->dma_chan);
dma_release_channel(prtd->dma_chan); dma_release_channel(prtd->dma_chan);
kfree(prtd);
return snd_dmaengine_pcm_close(substream); return 0;
} }
EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan);