diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2015-10-20 05:46:31 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2015-11-15 21:58:52 -0500 |
commit | bc0e7345168c0f7483d2d1da86285d89136417cd (patch) | |
tree | 9045e7f4f133da72296fdf13c9270db34d64906a | |
parent | 860dd64c4382709a276eb4b7ef36596579dba04a (diff) |
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>
-rw-r--r-- | sound/core/pcm_dmaengine.c | 9 |
1 files changed, 6 insertions, 3 deletions
diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index fba365a78390..697c166acf05 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c | |||
@@ -202,13 +202,13 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
202 | if (runtime->info & SNDRV_PCM_INFO_PAUSE) | 202 | if (runtime->info & SNDRV_PCM_INFO_PAUSE) |
203 | dmaengine_pause(prtd->dma_chan); | 203 | dmaengine_pause(prtd->dma_chan); |
204 | else | 204 | else |
205 | dmaengine_terminate_all(prtd->dma_chan); | 205 | dmaengine_terminate_async(prtd->dma_chan); |
206 | break; | 206 | break; |
207 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 207 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
208 | dmaengine_pause(prtd->dma_chan); | 208 | dmaengine_pause(prtd->dma_chan); |
209 | break; | 209 | break; |
210 | case SNDRV_PCM_TRIGGER_STOP: | 210 | case SNDRV_PCM_TRIGGER_STOP: |
211 | dmaengine_terminate_all(prtd->dma_chan); | 211 | dmaengine_terminate_async(prtd->dma_chan); |
212 | break; | 212 | break; |
213 | default: | 213 | default: |
214 | return -EINVAL; | 214 | return -EINVAL; |
@@ -346,6 +346,7 @@ int snd_dmaengine_pcm_close(struct snd_pcm_substream *substream) | |||
346 | { | 346 | { |
347 | struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 347 | struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); |
348 | 348 | ||
349 | dmaengine_synchronize(prtd->dma_chan); | ||
349 | kfree(prtd); | 350 | kfree(prtd); |
350 | 351 | ||
351 | return 0; | 352 | return 0; |
@@ -362,9 +363,11 @@ int snd_dmaengine_pcm_close_release_chan(struct snd_pcm_substream *substream) | |||
362 | { | 363 | { |
363 | struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); | 364 | struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream); |
364 | 365 | ||
366 | dmaengine_synchronize(prtd->dma_chan); | ||
365 | dma_release_channel(prtd->dma_chan); | 367 | dma_release_channel(prtd->dma_chan); |
368 | kfree(prtd); | ||
366 | 369 | ||
367 | return snd_dmaengine_pcm_close(substream); | 370 | return 0; |
368 | } | 371 | } |
369 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); | 372 | EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_close_release_chan); |
370 | 373 | ||