CVE-2026-46143
Description
In the Linux kernel, the following vulnerability has been resolved:
ASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
As prepare can be called mulitple times, this can result in multiple graph opens for playback path.
This will result in a memory leaks, fix this by adding a check before opening.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A memory leak in the Linux kernel's ASoC q6apm-lpass-dai driver occurs when prepare is called multiple times without checking for an existing graph open.
Vulnerability
In the Linux kernel, in the ASoC QCOM q6apm-lpass-dai driver, the prepare callback can be invoked multiple times for the same playback path. Each invocation opens a new graph without first checking whether one is already open, leading to an unintended sequence of multiple graph opens. This affects kernel versions up to and including the commit before the fix referenced in [1].
Exploitation
An attacker would need local access and the ability to trigger multiple prepare calls on a sound card playback stream — possibly via repeated ALSA PCM preparation requests from userspace. No special privileges beyond basic audio subsystem access are required. The sequence is: (1) open the PCM device, (2) call snd_pcm_prepare multiple times, (3) each call triggers a new graph open without closing the previous one.
Impact
Each extra graph open leaks kernel memory associated with the graph allocation. Over repeated or automated calls, this can lead to memory exhaustion and system instability. The impact is limited to denial of service (resource exhaustion); no privilege escalation or information disclosure is described [1].
Mitigation
The fix was committed to the stable kernel tree as commit 3141d8b00cad [1]. It adds a check before opening a new graph, preventing duplicate opens. Users should apply the patch or update to a kernel version that includes this commit. No workaround short of the patch is provided in the available references.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
10c91b7bcc7034ASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index cba8548415ad52..b33864ae529f5b 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
7cab9f2ad51cASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329fc..ba64117b8cfe13 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
3141d8b00cadASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 9fcf8f59ea280a..8f8fb537876f95 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -175,7 +175,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
69acc488aaf3ASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329fc..ba64117b8cfe13 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
b97493f0f42aASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329fc..ba64117b8cfe13 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
b97493f0f42aASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329fc..ba64117b8cfe13 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
7cab9f2ad51cASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329fc..ba64117b8cfe13 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
3141d8b00cadASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 9fcf8f59ea280a..8f8fb537876f95 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -175,7 +175,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
69acc488aaf3ASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 5be37eeea329fc..ba64117b8cfe13 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
c91b7bcc7034ASoC: qcom: q6apm-lpass-dai: Fix multiple graph opens
1 file changed · +1 −2
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c+1 −2 modifieddiff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index cba8548415ad52..b33864ae529f5b 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -181,7 +181,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing idempotency check in `q6apm_lpass_dai_prepare` allows `q6apm_graph_open` to be called multiple times for the same playback DAI, leaking memory."
Attack vector
An attacker who can trigger repeated `prepare` calls on a playback PCM substream (e.g., via ALSA `snd_pcm_prepare` or audio framework operations) will cause `q6apm_graph_open` to be invoked multiple times for the same DAI. Each call allocates a new graph object, but the previous pointer is overwritten without being freed, leading to a memory leak. No special network path or authentication is required; the attacker only needs the ability to interact with the ALSA PCM interface on a Qualcomm ASoC system using the q6apm-lpass-dai driver.
Affected code
The vulnerability is in the `q6apm_lpass_dai_prepare` function in `sound/soc/qcom/qdsp6/q6apm-lpass-dais.c` [patch_id=2898331]. The function unconditionally calls `q6apm_graph_open` for the playback stream without checking whether the graph pointer (`dai_data->graph[dai->id]`) is already non-NULL.
What the fix does
The patch adds a guard condition `dai_data->graph[dai->id] == NULL` to the existing playback-stream check [patch_id=2898331]. Before calling `q6apm_graph_open`, the code now verifies that no graph has already been opened for that DAI. If the pointer is non-NULL, the open is skipped, preventing duplicate allocations and the resulting memory leak. This is a minimal, targeted fix that does not alter any other control flow.
Preconditions
- inputThe attacker must be able to invoke the ALSA `prepare` operation multiple times on a playback PCM substream.
- configThe system must use the Qualcomm ASoC q6apm-lpass-dai driver with a playback path configured.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/3141d8b00cad6d3331953c79060ccc3a0262311bnvd
- git.kernel.org/stable/c/69acc488aaf39d0ddf6c3cf0e47c1873d39919a2nvd
- git.kernel.org/stable/c/7cab9f2ad51c858263da836baebad050a1bc7914nvd
- git.kernel.org/stable/c/b97493f0f42ab9d882a62466782e1900e481a9d6nvd
- git.kernel.org/stable/c/c91b7bcc70346d07f57ef03d1b9a338324e213denvd
News mentions
0No linked articles in our index yet.