All of lore.kernel.org
 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: 57+ 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-10  2:20     ` James Calligeros
2026-10-13 17:46       ` Mark Brown
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-10  2:48     ` James Calligeros
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-13 17:50   ` Mark Brown
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-09 11:52   ` Cezary Rojewski
2026-10-10  4:40     ` James Calligeros
2026-10-12 10:03       ` Cezary Rojewski
2026-10-13  7:14         ` James Calligeros
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 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.