From: sashiko-bot@kernel.org
To: "Cezary Rojewski" <cezary.rojewski@intel.com>,
linux-sound@vger.kernel.org
Subject: Re: [PATCH 7/7] ASoC: Intel: avs: SoundWire PCM BE operations
Date: Mon, 02 Nov 2026 18:26:47 +0000 [thread overview]
Message-ID: <sashiko-outbox-193935@kernel.org> (raw)
In-Reply-To: <20261102181042.1922005-8-cezary.rojewski@intel.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] ASoC: Intel: avs: broken suspend/resume due to avs_sdw_dai_hw_free()
- [High] ASoC: Intel: avs: m_rt leak and UAF on sdw_stream_add_master() error in avs_sdw_dai_hw_params()
- [Medium] ASoC: Intel: avs: dangling codec stream pointer in avs_sdw_dai_shutdown()
- [Medium] ASoC: Intel: avs: missing stream disable rollback in avs_sdw_dai_trigger()
- [Low] ASoC: Intel: avs: link failure when SND_SOC_INTEL_AVS=y and SOUNDWIRE_INTEL=m
--
commit b4101c2b87dd19b6fc2e6be967189ca74f738d4e
Author: Cezary Rojewski <cezary.rojewski@intel.com>
ASoC: Intel: avs: SoundWire PCM BE operations
This commit implements back-end PCM operations for the SoundWire link in
the Intel AVS driver, wiring up DAI startup, shutdown, hw_params, hw_free,
prepare, and trigger callbacks to the SoundWire core and Cadence helpers.
> diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
> index 1d48953c7a31c..07a82a6f5b23e 100644
> --- a/sound/soc/intel/avs/Makefile
> +++ b/sound/soc/intel/avs/Makefile
> @@ -15,7 +15,7 @@ snd-soc-avs-y += probes.o debugfs.o
> endif
>
> ifneq ($(CONFIG_SOUNDWIRE_INTEL),)
> -snd-soc-avs-y += soundwire.o
> +snd-soc-avs-y += soundwire.o pcm_sdw.o
> endif
>
> obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o
[Severity: Low]
Can this fail to link when CONFIG_SND_SOC_INTEL_AVS=y and
CONFIG_SOUNDWIRE_INTEL=m?
In sound/soc/intel/Kconfig, SND_SOC_INTEL_AVS is tristate and does not
depend on SOUNDWIRE_INTEL || !SOUNDWIRE_INTEL. When SND_SOC_INTEL_AVS=y
and SOUNDWIRE_INTEL=m (with SOUNDWIRE=m and SOUNDWIRE_CADENCE=m),
ifneq ($(CONFIG_SOUNDWIRE_INTEL),) is still true and links pcm_sdw.o into
the built-in snd-soc-avs.o, while sdw_alloc_stream(),
sdw_cdns_assign_pdi(), and the other SoundWire helpers live in loadable
modules.
> diff --git a/sound/soc/intel/avs/pcm_sdw.c b/sound/soc/intel/avs/pcm_sdw.c
> new file mode 100644
> index 0000000000000..27cb285eca091
> --- /dev/null
> +++ b/sound/soc/intel/avs/pcm_sdw.c
> @@ -0,0 +1,276 @@
[ ... ]
> +static int avs_sdw_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> + struct sdw_stream_runtime *sdw_stream;
> + struct snd_soc_acpi_mach *mach;
> + struct snd_soc_dai *codec_dai;
> + struct avs_dma_data *data;
> + struct sdw_cdns_pdi *pdi;
> + struct sdw_slave *codec;
> + int dir, ret;
> +
> + mach = dev_get_platdata(dai->component->card->dev);
> + codec = mach->pdata;
> + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
> + dir = SDW_DATA_DIR_RX;
> + else
> + dir = SDW_DATA_DIR_TX;
> +
> + ret = avs_dai_startup(substream, dai);
> + if (ret)
> + return ret;
> +
> + sdw_stream = sdw_alloc_stream(substream->name, SDW_STREAM_PCM);
> + if (!sdw_stream) {
> + ret = -ENOMEM;
> + goto err_alloc_stream;
> + }
> +
> + pdi = sdw_cdns_assign_pdi(codec->bus, dir);
> + if (!pdi) {
> + ret = -EBUSY;
> + goto err_assign_pdi;
> + }
> +
> + /* Fulfill the codec needs by setting the sdw_stream. */
> + codec_dai = snd_soc_rtd_to_codec(rtd, 0);
> + snd_soc_dai_set_stream(codec_dai, sdw_stream, substream->stream);
> +
> + data = snd_soc_dai_get_dma_data(dai, substream);
> + data->sdw_stream = sdw_stream;
> + data->pdi = pdi;
> + return 0;
> +
> +err_assign_pdi:
> + sdw_release_stream(sdw_stream);
> +err_alloc_stream:
> + avs_dai_shutdown(substream, dai);
> + return ret;
> +}
> +
> +static void avs_sdw_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> + struct snd_soc_acpi_mach *mach;
> + struct avs_dma_data *data;
> + struct sdw_slave *codec;
> +
> + data = snd_soc_dai_get_dma_data(dai, substream);
> + mach = dev_get_platdata(dai->component->card->dev);
> + codec = mach->pdata;
> +
> + sdw_cdns_release_pdi(codec->bus, data->pdi);
> + sdw_release_stream(data->sdw_stream);
> + avs_dai_shutdown(substream, dai);
> +}
[Severity: Medium]
Should avs_sdw_dai_startup() check the return value of
snd_soc_dai_set_stream(), and should avs_sdw_dai_shutdown() clear the
stream pointer on codec_dai before freeing data->sdw_stream?
In avs_sdw_dai_shutdown(), sdw_release_stream(data->sdw_stream) frees
data->sdw_stream without calling
snd_soc_dai_set_stream(codec_dai, NULL, substream->stream) (unlike
sdw_shutdown_stream()). If a SoundWire codec driver does not clear its
stream pointer in a .shutdown callback, or if __soc_pcm_open() fails
during codec_dai startup after avs_sdw_dai_startup() has already run (so
soc_pcm_clean() skips codec_dai shutdown during rollback), codec_dai is
left holding a dangling pointer to the freed sdw_stream_runtime.
> +static int avs_sdw_dai_hw_params(struct snd_pcm_substream *substream,
> + struct snd_pcm_hw_params *hw_params,
> + struct snd_soc_dai *dai)
> +{
> + struct sdw_stream_config sconfig = {0};
> + struct sdw_port_config pconfig = {0};
> + struct snd_soc_acpi_mach *mach;
> + struct avs_dma_data *data;
> + struct sdw_cdns_pdi *pdi;
> + struct sdw_slave *codec;
> + int ret;
> +
> + data = snd_soc_dai_get_dma_data(dai, substream);
> + if (data->path)
> + return 0;
> +
> + mach = dev_get_platdata(dai->component->card->dev);
> + codec = mach->pdata;
> + pdi = data->pdi;
> + pdi->ch_count = params_channels(hw_params);
> + pdi->h_ch_num = pdi->ch_count - 1;
> + pdi->l_ch_num = 0;
> + sdw_intel_pdi_config(codec->bus, pdi);
> +
> + sconfig.direction = pdi->dir;
> + sconfig.ch_count = pdi->ch_count;
> + sconfig.frame_rate = params_rate(hw_params);
> + sconfig.type = SDW_STREAM_PCM;
> + sconfig.bps = snd_pcm_format_width(params_format(hw_params));
> + pconfig.num = pdi->num;
> + pconfig.ch_mask = (1 << pdi->ch_count) - 1;
> +
> + ret = sdw_stream_add_master(codec->bus, &sconfig, &pconfig, 1, data->sdw_stream);
> + if (ret)
> + return ret;
> +
> + ret = avs_dai_be_hw_params(substream, hw_params, dai, pdi->intel_alh_id);
> + if (ret)
> + sdw_stream_remove_master(codec->bus, data->sdw_stream);
> + return ret;
> +}
[Severity: High]
If sdw_stream_add_master() fails here in avs_sdw_dai_hw_params(), does
this leak struct sdw_master_runtime and leave a dangling m_rt->stream
pointer on codec->bus->m_rt_list?
During __soc_pcm_hw_params(), the codec DAI hw_params runs before the CPU
DAI hw_params:
__soc_pcm_hw_params()
soc_pcm_hw_params() [codec DAI]
sdw_stream_add_slave()
sdw_master_rt_alloc() -> allocates m_rt, links onto
codec->bus->m_rt_list with
m_rt->stream = data->sdw_stream
soc_pcm_hw_params() [CPU DAI: avs_sdw_dai_hw_params()]
sdw_stream_add_master()
Because m_rt was already allocated by sdw_stream_add_slave(),
sdw_stream_add_master() sets alloc_master_rt = false and does not free
m_rt (or any allocated master ports) if sdw_master_port_alloc(),
sdw_master_rt_config(), sdw_config_stream(), or sdw_master_port_config()
fails.
When avs_sdw_dai_hw_params() returns that error without calling
sdw_stream_remove_master(), data->path remains NULL. Rollback in
soc_pcm_hw_clean() only calls sdw_stream_remove_slave() on the codec DAI
(which frees s_rt, not m_rt) and skips avs_sdw_dai_hw_free(). When the
substream is then closed, avs_sdw_dai_shutdown() frees data->sdw_stream
while m_rt is still linked on codec->bus->m_rt_list with m_rt->stream
pointing to the freed stream.
> +
> +static int avs_sdw_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> + struct snd_soc_acpi_mach *mach;
> + struct avs_dma_data *data;
> + struct sdw_slave *codec;
> + int ret;
> +
> + data = snd_soc_dai_get_dma_data(dai, substream);
> + if (!data->path)
> + return 0;
> +
> + mach = dev_get_platdata(dai->component->card->dev);
> + codec = mach->pdata;
> +
> + ret = avs_path_reset(data->path);
> + if (ret < 0)
> + dev_err(dai->dev, "reset BE path failed: %d\n", ret);
> + avs_path_free(data->path);
> + data->path = NULL;
> + sdw_deprepare_stream(data->sdw_stream);
> + sdw_stream_remove_master(codec->bus, data->sdw_stream);
> +
> + return 0;
> +}
[Severity: High]
Won't calling sdw_stream_remove_master() in avs_sdw_dai_hw_free() break
system suspend and resume when a SoundWire stream is active?
avs_register_sdw_component() registers the component with
avs_component_driver, which uses avs_component_suspend() and
avs_component_resume():
avs_component_suspend()
avs_component_suspend_hw_free()
avs_dai_suspend_be_hw_free()
dai->driver->ops->hw_free() -> avs_sdw_dai_hw_free()
During suspend, avs_dai_suspend_be_hw_free() invokes hw_free only on the
AVS CPU DAI (ASoC does not call hw_free or hw_params on the codec DAI
across suspend/resume). In avs_sdw_dai_hw_free(),
sdw_stream_remove_master() calls sdw_master_rt_free(), which frees both
the master runtime and all attached codec slave runtimes on
m_rt->slave_rt_list, and sets data->sdw_stream->state to
SDW_STREAM_RELEASED.
On resume:
avs_component_resume()
avs_component_resume_hw_params()
avs_dai_resume_hw_params()
avs_sdw_dai_hw_params()
sdw_stream_add_master()
avs_component_resume_prepare()
avs_dai_resume_be_prepare()
avs_sdw_dai_prepare()
sdw_prepare_stream()
Because codec DAI hw_params (sdw_stream_add_slave()) is never called on
resume, m_rt->slave_rt_list remains empty and data->sdw_stream->state
remains SDW_STREAM_RELEASED (only sdw_stream_add_slave() transitions the
stream state to SDW_STREAM_CONFIGURED). Consequently,
sdw_prepare_stream() in avs_sdw_dai_prepare() fails with -EINVAL and
avs_component_pm_op() disconnects the substream.
> +
> +static int avs_sdw_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
> +{
> + struct avs_dma_data *data;
> + int ret;
> +
> + data = snd_soc_dai_get_dma_data(dai, substream);
> +
> + ret = sdw_prepare_stream(data->sdw_stream);
> + if (ret) {
> + dev_err(dai->dev, "sdw stream prepare failed: %d\n", ret);
> + return ret;
> + }
> +
> + return avs_dai_prepare(substream, dai);
> +}
> +
> +static int avs_sdw_dai_trigger(struct snd_pcm_substream *substream, int cmd,
> + struct snd_soc_dai *dai)
> +{
> + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> + struct avs_dma_data *data;
> + int ret = 0;
> +
> + data = snd_soc_dai_get_dma_data(dai, substream);
> +
> + switch (cmd) {
> + case SNDRV_PCM_TRIGGER_RESUME:
> + if (rtd->dai_link->ignore_suspend)
> + break;
> + fallthrough;
> + case SNDRV_PCM_TRIGGER_START:
> + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> + ret = sdw_enable_stream(data->sdw_stream);
> + if (ret)
> + break;
> +
> + ret = avs_path_run(data->path, AVS_TPLG_TRIGGER_AUTO);
> + if (ret < 0)
> + dev_err(dai->dev, "run BE path failed: %d\n", ret);
> + break;
> +
> + case SNDRV_PCM_TRIGGER_SUSPEND:
> + if (rtd->dai_link->ignore_suspend)
> + break;
> + fallthrough;
> + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> + case SNDRV_PCM_TRIGGER_STOP:
> + ret = avs_path_pause(data->path);
> + if (ret < 0)
> + dev_err(dai->dev, "pause BE path failed: %d\n", ret);
> +
> + ret = sdw_disable_stream(data->sdw_stream);
> + if (ret)
> + dev_err(dai->dev, "disable sdw stream failed: %d\n", ret);
> + break;
[Severity: Medium]
If avs_path_run() fails in avs_sdw_dai_trigger(), should
sdw_disable_stream(data->sdw_stream) be called to roll back
sdw_enable_stream()?
When avs_sdw_dai_trigger() returns an error from avs_path_run(), ASoC
does not mark the DAI as triggered or transition the BE to
SND_SOC_DPCM_STATE_START, so STOP will not be called on this DAI. That
leaves data->sdw_stream in SDW_STREAM_ENABLED, causing
sdw_deprepare_stream() in avs_sdw_dai_hw_free() to fail with -EINVAL and
leak the stream's bus bandwidth allocation on codec->bus.
Also, in the SUSPEND/PAUSE_PUSH/STOP case, assigning
ret = sdw_disable_stream(data->sdw_stream) unconditionally overwrites any
negative error returned by avs_path_pause(data->path).
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102181042.1922005-1-cezary.rojewski@intel.com?part=7
next prev parent reply other threads:[~2026-11-02 18:26 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 18:10 [PATCH 0/7] ASoC: Intel: avs: Soundwire support Cezary Rojewski
2026-11-02 18:10 ` [PATCH 1/7] soundwire: intel: Define stubs when library is disabled Cezary Rojewski
2026-11-02 18:19 ` sashiko-bot
2026-11-02 18:10 ` [PATCH 2/7] soundwire: intel: Allow for dynamic assignment of PDIs Cezary Rojewski
2026-11-02 18:22 ` sashiko-bot
2026-11-03 9:04 ` kernel test robot
2026-11-02 18:10 ` [PATCH 3/7] soundwire: intel: Split PDIs and DAIs initialization Cezary Rojewski
2026-11-02 18:14 ` sashiko-bot
2026-11-03 2:35 ` Liao, Bard
2026-11-03 8:59 ` Cezary Rojewski
2026-11-02 18:10 ` [PATCH 4/7] ASoC: Intel: avs: Pass irq to dsp_interrupt callback Cezary Rojewski
2026-11-02 18:10 ` [PATCH 5/7] ASoC: Intel: avs: SoundWire support Cezary Rojewski
2026-11-02 18:29 ` sashiko-bot
2026-11-02 18:10 ` [PATCH 6/7] ASoC: Intel: avs: Expose common PCM functions Cezary Rojewski
2026-11-02 18:11 ` sashiko-bot
2026-11-02 18:18 ` Amadeusz Sławiński
2026-11-02 18:20 ` Cezary Rojewski
2026-11-02 18:10 ` [PATCH 7/7] ASoC: Intel: avs: SoundWire PCM BE operations Cezary Rojewski
2026-11-02 18:26 ` sashiko-bot [this message]
2026-11-03 5:37 ` [PATCH 0/7] ASoC: Intel: avs: Soundwire support Vinod Koul
2026-11-03 9:18 ` Cezary Rojewski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-193935@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=cezary.rojewski@intel.com \
--cc=linux-sound@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.