[PATCH] ASoC: soc-compress: fix use-after-free in soc_compr_trigger_fe() during BE list traversal

"Shinhyung Kang" <[email protected]>
Newsgroups org.alsa-project.alsa-devel
Message-ID <[email protected]>
The DPCM compress trigger path traverses the FE's BE client list in
dpcm_be_dai_trigger() without holding card->pcm_mutex, while
dpcm_be_disconnect() can concurrently remove and free entries from
that same list under pcm_mutex protection.

This causes a use-after-free when for_each_dpcm_be() advances to the
next list node after releasing a BE's stream lock between iterations,
and the snd_soc_dpcm entry has already been kfree()'d by a concurrent
dpcm_be_disconnect() call.

Crash signature observed:
 Unable to handle kernel paging request at virtual address dead0000000000e8
 Call trace:
  dpcm_be_dai_trigger+0x90/0x3f0
  soc_compr_trigger_fe+0xa8/0x144
  snd_compr_ioctl+0xc98/0x2010
  __arm64_sys_ioctl+0x164/0x784

Race condition timeline:
 Thread A (soc_compr_trigger_fe):
   snd_soc_card_mutex_lock()        <- holds card->mutex only
   dpcm_be_dai_trigger()
    for_each_dpcm_be(fe, stream, dpcm) {
	  snd_pcm_stream_lock_irqsave_nested(be_substream);
	  ...
	  snd_pcm_stream_unlock_irqrestore(be_substream);
	  /* WINDOW: next iteration reads dpcm->list_be.next */
	}

 Thread B (snd_soc_dpcm_runtime_update via DAPM):
   snd_soc_dpcm_mutex_lock()        <- holds card->pcm_mutex
   dpcm_be_disconnect()
	snd_pcm_stream_lock_irq(fe_substream);
	list_del(&dpcm->list_be);      <- removes from list
	snd_pcm_stream_unlock_irq();
	kfree(dpcm);                   <- frees the struct

The PCM trigger path (dpcm_fe_dai_trigger) is protected against this
race by checking runtime_update and deferring to trigger_pending when
a concurrent update is in progress. The compress trigger path
(soc_compr_trigger_fe) lacks this deferred-trigger mechanism, so the
only correct fix is to hold pcm_mutex for the duration of the BE list
traversal, as is done in all other compress FE operations such as
soc_compr_open_fe() and soc_compr_set_params().

Signed-off-by: Shinhyung Kang <[email protected]>
---
 sound/soc/soc-compress.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index b8402802ae78..615ce7a0e8d9 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -285,6 +285,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream
*cstream, int cmd)
 		return snd_soc_component_compr_trigger(cstream, cmd);
 
 	snd_soc_card_mutex_lock(fe->card);
+	snd_soc_dpcm_mutex_lock(fe);
 
 	ret = snd_soc_dai_compr_trigger(cpu_dai, cstream, cmd);
 	if (ret < 0)
@@ -315,6 +316,7 @@ static int soc_compr_trigger_fe(struct snd_compr_stream
*cstream, int cmd)
 
 out:
 	fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+	snd_soc_dpcm_mutex_unlock(fe);
 	snd_soc_card_mutex_unlock(fe->card);
 	return ret;
 }
-- 
2.21.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.