Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "James Calligeros" <jcalligeros99@gmail.com>
Cc: robh@kernel.org, devicetree@vger.kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v2 09/21] ASoC: apple: Add macaudio machine driver
Date: Sun, 04 Oct 2026 06:24:25 +0000	[thread overview]
Message-ID: <20261004062425.A460A1F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20261004-macaudio-v2-9-7b47ed6335a8@gmail.com>

Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [High] ALSA: macaudio: UAF in workqueues due to devres and remove cleanup ordering
- [High] ALSA: macaudio: OOB memory read in macaudio_vlimit_unlock due to pointer underflow
- [High] ALSA: macaudio: ABBA deadlock between controls_rwsem and volume_lock_mutex
- [High] ALSA: macaudio: sleeping in atomic context in macaudio_be_trigger
- [High] ALSA: macaudio: invalid hardware state from mutating params in macaudio_dpcm_hw_params
- [Medium] ALSA: macaudio: spurious speaker volume lock due to race condition in macaudio_slk_put
- [Medium] ALSA: macaudio: OF node reference leak in probe error paths and driver unbind
- [Medium] ALSA: macaudio: NULL pointer dereference of DAI widget streams on malformed DT
- [Low] ALSA: macaudio: missing NULL check before pointer dereference in macaudio_vlimit_unlock
- [Low] ALSA: macaudio: data race on ma->bes_active read
--

commit 5727989ecaf382b9e8427180fda0eb828623fffb
Author: Martin Povišer <povik+lin@cutebit.org>

ASoC: apple: Add macaudio machine driver

Apple Silicon Macs have a complex audio subsystem consisting
of an I2S peripheral (MCA) and multiple codecs of various
models and capabilities. Some machines have a basic mono
speaker with hardware downmix, while others have a very
intricate stereo system consisting of multiple codecs and
drivers per L/R channel. Some machines report voice coil
voltage and current information back to the SoC, and others
do not. All machines have a headset jack.

> diff --git a/sound/soc/apple/macaudio.c b/sound/soc/apple/macaudio.c
> index 0000000000000..36a62d88bb18b
> --- /dev/null
> +++ b/sound/soc/apple/macaudio.c
> @@ -0,0 +1,1699 @@
[ ... ]
> +static void macaudio_vlimit_unlock(struct macaudio_snd_data *ma, bool unlock)
> +{
> +	int ret, max;
> +	struct snd_kcontrol *kctl;
> +	const char *name = volume_control_names[ma->cfg->amp];
> +	int namelen = strlen(name);
> +
> +	if (!name) {
> +		WARN_ON_ONCE(1);
> +		return;
> +	}

[Severity: Low]
Does this code dereference name before checking if it is NULL? The call
to strlen(name) happens just before if (!name).

[ ... ]
> +	/* The caller has already taken the rw semaphore as necessary */
> +	list_for_each_entry(kctl, &ma->card.snd_card->controls, list) {
> +		const char *suffix = kctl->id.name + strlen(kctl->id.name) - namelen;
> +
> +		if (strncmp(suffix, name, namelen))
> +			continue;

[Severity: High]
Can this arithmetic underflow if the kcontrol name is shorter than
namelen? Since strlen() returns an unsigned size_t, subtracting a
larger namelen would result in a very large positive offset, leading
to an out-of-bounds memory read in strncmp().

[ ... ]
> +static void macaudio_vlimit_update(struct macaudio_snd_data *ma, bool take_rwsem)
> +{
[ ... ]
> +	/*
> +	 * If this has been called from either of the safety interlock
> +	 * workqueues, the core API has not taken controls_rwsem for us.
> +	 * Take the write semaphore since macaudio_vlimit_unlock writes
> +	 * to the kcontrols.
> +	 */
> +	if (take_rwsem)
> +		down_write(&ma->card.snd_card->controls_rwsem);

[Severity: High]
Could this lead to an ABBA deadlock? When a user interacts with the
"Speaker Volume Unlock" kcontrol, the ALSA core takes controls_rwsem
before calling macaudio_slk_put(), which then acquires
volume_lock_mutex.

Here, the worker thread macaudio_vlimit_timeout_work acquires
volume_lock_mutex first and then attempts to acquire controls_rwsem
via macaudio_vlimit_update().

[ ... ]
> +static void macaudio_vlimit_timeout_work(struct work_struct *wrk)
> +{
> +	struct macaudio_snd_data *ma = container_of(to_delayed_work(wrk),
> +						    struct macaudio_snd_data, lock_timeout_work);
> +
> +	guard(mutex)(&ma->volume_lock_mutex);
> +	ma->speaker_lock_remain = 0;
> +	macaudio_vlimit_update(ma, true);
> +}

[Severity: Medium]
Will this blindly zeroize the timeout if it executes concurrently with a
userspace ping? If macaudio_slk_put() is actively updating the timeout
but the worker has already fired and is waiting on volume_lock_mutex,
the non-synchronous cancel_delayed_work() in macaudio_slk_put()
won't stop it. The worker will then wake up, set
ma->speaker_lock_remain = 0, and lock the volume.

[ ... ]
> +static void macaudio_vlimit_update_work(struct work_struct *wrk)
> +{
> +	struct macaudio_snd_data *ma = container_of(wrk,
> +						    struct macaudio_snd_data, lock_update_work);
> +
> +	if (ma->bes_active)

[Severity: Low]
Is this a data race? ma->bes_active is read here without any locking or
READ_ONCE(), while macaudio_be_trigger() concurrently modifies it.

[ ... ]
> +static int macaudio_parse_of(struct macaudio_snd_data *ma)
> +{
[ ... ]
> +		for (be_index = 0; be_index < num_bes; be_index++) {
> +			/*
> +			 * Set initial link name to be overwritten by a BE-specific
> +			 * name later so that we can use at least use the provisional
> +			 * name in error messages.
> +			 */
> +			link->name = link_name;
> +
> +			ret = macaudio_parse_of_be_dai_link(ma, link, be_index,
> +							    ncodecs_per_cpu, cpu, codec);
> +			if (ret)
> +				goto err_free;
> +
> +			link_props->is_speakers = speakers;
> +			link_props->is_headphones = !speakers;
> +
> +			link_props->codecs = devm_kcalloc(dev, ncodecs_per_cpu,
> +							  sizeof(struct ma_codec_idle), GFP_KERNEL);
> +			if (!link_props->codecs)
> +				return -ENOMEM;

[Severity: Medium]
Does this directly return without cleaning up the OF node references?
Bypassing the err_free label here skips the cleanup logic, leaving the
references acquired in macaudio_parse_of_component() leaked.

Additionally, when taking the err_free path from within this loop,
num_links hasn't been incremented yet for the current iteration, so the
nodes for the currently failing link might not be released.

[ ... ]
> +static int macaudio_dpcm_hw_params(struct snd_pcm_substream *substream,
> +				   struct snd_pcm_hw_params *params)
> +{
[ ... ]
> +	if (props->is_sense) {
> +		/*
> +		 * The sense FE is opened exclusively by speakersafetyd in blocking
> +		 * mode. At the time of opening, this sample rate may be 0 if the
> +		 * speaker BE is not running. This does not matter, as speakersafetyd
> +		 * only cares about the active sample rate exported to the virtual
> +		 * sample rate kcontrol below.
> +		 */
> +		rate->min = rate->max = cpu_dai->symmetric_rate;
> +		return 0;
> +	}
> +
> +	/* Speakers BE */
> +	if (props->is_speakers) {
> +		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
> +			/* Sense PCM: keep the existing BE rate (0 if not already running) */
> +			rate->min = rate->max = cpu_dai->symmetric_rate;
> +
> +			return 0;
> +		}

[Severity: High]
Does setting rate->min = rate->max = cpu_dai->symmetric_rate mutate
the hardware parameters structure directly during the hw_params phase?
If the speaker BE is not running and symmetric_rate is 0, this forces
the rate to 0 Hz, which may cause downstream hardware drivers to reject
it with -EINVAL and break the stream initialization. Should constraints
like this be applied in the startup callback instead?

[ ... ]
> +static int macaudio_be_trigger(struct snd_pcm_substream *substream, int cmd)
> +{
> +	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
> +	struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(rtd->card);
> +	struct macaudio_link_props *props = &ma->link_props[rtd->dai_link->id];
> +
> +	guard(mutex)(&ma->be_link_mutex);

[Severity: High]
Can this attempt to acquire a sleeping lock while in an atomic context?
Because these DAI links are not marked with nonatomic = 1, the ASoC and
ALSA cores invoke the .trigger callback with the PCM substream spinlock
held and IRQs disabled.

[ ... ]
> +static int macaudio_add_backend_dai_route(struct snd_soc_card *card, struct snd_soc_dai *dai,
> +					  bool is_speakers)
> +{
> +	struct snd_soc_dapm_route routes[2];
> +	struct snd_soc_dapm_route *r;
> +	int nroutes = 0;
> +	int ret;
> +
> +	memset(routes, 0, sizeof(routes));
> +
> +	dev_dbg(card->dev, "adding routes for '%s'\n", dai->name);
> +
> +	r = &routes[nroutes++];
> +	if (is_speakers)
> +		r->source = "Speaker Playback";
> +	else
> +		r->source = "Headphone Playback";
> +	r->sink = dai->stream[SNDRV_PCM_STREAM_PLAYBACK].widget->name;

[Severity: Medium]
What happens if the configured device tree specifies a capture-only DAI?
Without verifying that the playback stream widget is non-NULL before
dereferencing its name, a malformed device tree could trigger a kernel
panic during device probe.

[ ... ]
> +static int macaudio_slk_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *uvalue)
> +{
> +	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
> +	struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
> +
> +	guard(mutex)(&ma->volume_lock_mutex);

[Severity: High]
Could this result in an ABBA deadlock? As mentioned earlier, this function
is called with controls_rwsem held by the core, and takes
volume_lock_mutex. The background worker acquires volume_lock_mutex
first and then attempts to acquire controls_rwsem.

[ ... ]
> +	cancel_delayed_work(&ma->lock_timeout_work);
> +
> +	ma->speaker_lock_remain = ms_to_ktime(SPEAKER_LOCK_TIMEOUT);
> +	ma->speaker_lock_timeout = ktime_add(ktime_get(), ma->speaker_lock_remain);
> +	macaudio_vlimit_update(ma, false);

[Severity: Medium]
Will this non-synchronous cancel_delayed_work() fail to stop the worker
if it is already running and waiting on volume_lock_mutex? This ties into
the race condition discussed in macaudio_vlimit_timeout_work().

[ ... ]
> +static int macaudio_snd_platform_probe(struct platform_device *pdev)
> +{
[ ... ]
> +	INIT_WORK(&data->lock_update_work, macaudio_vlimit_update_work);
> +	INIT_DELAYED_WORK(&data->lock_timeout_work, macaudio_vlimit_timeout_work);
> +
> +	return devm_snd_soc_register_card(dev, card);
> +}

[Severity: High]
Does registering the card via devres create a use-after-free window
during unbind? The devres cleanup (including unregistering the card)
runs after macaudio_snd_platform_remove() completes. This leaves a
window where ALSA callbacks can still fire and re-queue workitems on
the workqueues that were cancelled during remove.

[ ... ]
> +static void macaudio_snd_platform_remove(struct platform_device *pdev)
> +{
> +	struct macaudio_snd_data *ma = dev_get_drvdata(&pdev->dev);
> +
> +	cancel_work_sync(&ma->lock_update_work);
> +	cancel_delayed_work_sync(&ma->lock_timeout_work);
> +}

[Severity: High]
Since the sound card is still fully active here (due to devres), can the
work items get re-queued by ongoing ALSA callbacks right after these sync
cancels? When devres later frees macaudio_snd_data, any re-queued work
would execute on freed memory.

[Severity: Medium]
Does this remove handler also leak the OF node references acquired during
probe? It appears to omit the necessary calls to
snd_soc_of_put_dai_link_codecs() and snd_soc_of_put_dai_link_cpus().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261004-macaudio-v2-0-7b47ed6335a8@gmail.com?part=9

  reply	other threads:[~2026-10-04  6:24 UTC|newest]

Thread overview: 49+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-04  6:03 [PATCH v2 00/21] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
2026-10-04  6:03 ` [PATCH v2 01/21] ASoC: dt-bindings: cs42l84: Document sound-name-prefix property James Calligeros
2026-10-09  7:09   ` Krzysztof Kozlowski
2026-10-04  6:03 ` [PATCH v2 02/21] ASoC: dt-bindings: cs42l42: Do not require chip power supplies James Calligeros
2026-10-09  7:10   ` Krzysztof Kozlowski
2026-10-04  6:03 ` [PATCH v2 03/21] ASoC: dt-bindings: cs42l42: Document #sound-dai-cells property James Calligeros
2026-10-09  7:10   ` Krzysztof Kozlowski
2026-10-04  6:03 ` [PATCH v2 04/21] ASoC: dt-bindings: cs42l42: Document sound-name-prefix property James Calligeros
2026-10-09  7:11   ` Krzysztof Kozlowski
2026-10-04  6:03 ` [PATCH v2 05/21] ASoC: dt-bindings: Add binding for Apple Silicon Mac audio James Calligeros
2026-10-04  6:15   ` sashiko-bot
2026-10-09  7:17   ` Krzysztof Kozlowski
2026-10-09  7:21     ` James Calligeros
2026-10-09  7:29       ` Krzysztof Kozlowski
2026-10-04  6:03 ` [PATCH v2 06/21] ASoC: ops: Introduce 'snd_soc_deactivate_kctl' James Calligeros
2026-10-09  8:16   ` Cezary Rojewski
2026-10-04  6:03 ` [PATCH v2 07/21] ASoC: ops: Introduce 'soc_set_enum_kctl' James Calligeros
2026-10-04  6:22   ` sashiko-bot
2026-10-07 17:36   ` Ajay Kumar Nandam
2026-10-09  8:27   ` Cezary Rojewski
2026-10-04  6:03 ` [PATCH v2 08/21] ASoC: card: Let 'fixup_controls' return errors James Calligeros
2026-10-07 14:23   ` Charles Keepax
2026-10-04  6:03 ` [PATCH v2 09/21] ASoC: apple: Add macaudio machine driver James Calligeros
2026-10-04  6:24   ` sashiko-bot [this message]
2026-10-07 18:04   ` Ajay Kumar Nandam
2026-10-04  6:03 ` [PATCH v2 10/21] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes James Calligeros
2026-10-04  6:17   ` sashiko-bot
2026-10-07 18:17   ` Ajay Kumar Nandam
2026-10-04  6:03 ` [PATCH v2 11/21] arm64: dts: apple: t8103-j313: " James Calligeros
2026-10-04  6:19   ` sashiko-bot
2026-10-07 18:32   ` Ajay Kumar Nandam
2026-10-08  8:46     ` James Calligeros
2026-10-08  9:34       ` Ajay Kumar Nandam
2026-10-04  6:03 ` [PATCH v2 12/21] arm64: dts: apple: t8103-j293: " James Calligeros
2026-10-04  6:25   ` sashiko-bot
2026-10-04  6:03 ` [PATCH v2 13/21] arm64: dts: apple: t8103-j45x: Add headset " James Calligeros
2026-10-04  6:21   ` sashiko-bot
2026-10-08  6:18   ` Ajay Kumar Nandam
2026-10-04  6:03 ` [PATCH v2 14/21] arm64: dts: apple: t8112-j413: Add speaker/headset " James Calligeros
2026-10-04  6:03 ` [PATCH v2 15/21] arm64: dts: apple: t8112-j415: " James Calligeros
2026-10-04  6:21   ` sashiko-bot
2026-10-04  6:03 ` [PATCH v2 16/21] arm64: dts: apple: t8112-j473: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 17/21] arm64: dts: apple: t8112-j493: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 18/21] arm64: dts: apple: t600x-j31x: " James Calligeros
2026-10-04  6:24   ` sashiko-bot
2026-10-04  6:03 ` [PATCH v2 19/21] arm64: dts: apple: t600x-j375: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 20/21] arm64: dts: apple: t602x-j41x: " James Calligeros
2026-10-04  6:03 ` [PATCH v2 21/21] arm64: dts: apple: t602x-j47x: " James Calligeros
2026-10-08  9:29   ` Ajay Kumar Nandam

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=20261004062425.A460A1F000FF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jcalligeros99@gmail.com \
    --cc=robh@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox