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, conor+dt@kernel.org, devicetree@vger.kernel.org
Subject: Re: [PATCH 16/28] ASoC: apple: Add macaudio machine driver
Date: Sun, 20 Sep 2026 05:14:30 +0000	[thread overview]
Message-ID: <20260920051431.214F01F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20260920-macaudio-v1-16-741cc20a74e5@gmail.com>

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [High] macaudio: UAF due to missing lock_update_work cancellation on driver unbind
- [High] macaudio: list corruption traversing snd_card->controls without controls_rwsem
- [Medium] macaudio: spurious volume lock due to timeout workqueue race
- [Medium] macaudio: missing mutex lock in macaudio_slk_unlock causes data race
- [Medium] macaudio: non-atomic bitwise operations on bes_active causes lost triggers
- [Medium] macaudio: OF node reference leaks on error paths and driver unbind
- [Medium] macaudio: TOCTOU race on speaker_volume_was_locked outside mutex
- [Low] macaudio: bitwise truncation of u64 bes_active on 32-bit COMPILE_TEST
--

commit c31fe439d1e39b1d15e314167ccc57d6863e8d4b
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
> new file mode 100644
> --- /dev/null
> +++ b/sound/soc/apple/macaudio.c
[ ... ]
> +static void macaudio_vlimit_update(struct macaudio_snd_data *ma)
> +{
> +	int i;
> +	bool unlock = true;
> +	struct snd_kcontrol *kctl;
> +	const char *reason;
[ ... ]
> +	/* Check that *every* limited control is locked by the same owner */
> +	list_for_each_entry(kctl, &ma->card.snd_card->controls, list) {

[Severity: High]
Can this list traversal be corrupted by concurrent modifications?

The ALSA sound card controls list (&ma->card.snd_card->controls) is iterated
here, but neither this driver nor snd_soc_limit_volume() acquire the ALSA
controls_rwsem lock.

Since macaudio_vlimit_update() is called asynchronously from background
workqueues, unprivileged userspace could concurrently add or remove controls
(e.g., via SNDRV_CTL_IOCTL_ELEM_ADD) under a write lock, mutating the list
while it is being traversed here.

> +		if (!snd_soc_control_matches(kctl, volume_control_names[ma->cfg->amp]))
> +			continue;
[ ... ]
> +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);
> +
> +	mutex_lock(&ma->volume_lock_mutex);
> +
> +	ma->speaker_lock_remain = 0;
> +	macaudio_vlimit_update(ma);

[Severity: Medium]
Could this worker blindly override a valid ping from userspace?

If macaudio_slk_put() executes concurrently, it calls cancel_delayed_work()
while holding ma->volume_lock_mutex. If the timeout worker is already waiting
for the mutex, cancel_delayed_work() returns false.

Once macaudio_slk_put() extends the timeout and releases the mutex, the blocked
worker thread will immediately acquire it and blindly zero the timeout here,
overriding the extension and locking the volume prematurely.

> +
> +	mutex_unlock(&ma->volume_lock_mutex);
> +}
[ ... ]
> +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;

[Severity: Medium]
Does this cleanup path skip partially populated links?

If macaudio_parse_of_be_dai_link() fails, the code jumps to err_free.
However, num_links is only incremented after the be_index loop finishes.
This means the cleanup loop below will fail to drop references for the
codecs and cpus that were already parsed in the current, incomplete batch.

Additionally, macaudio_snd_platform_remove() doesn't seem to release any
of these OF node references when the driver is unbound.

> +
> +			link_props->is_speakers = speakers;
[ ... ]
> +		num_links += num_bes;
> +	}
[ ... ]
> +err_free:
> +	of_node_put(codec);
> +	of_node_put(cpu);
> +	of_node_put(np);
> +
> +	if (!card->dai_link)
> +		return ret;
> +
> +	for (i = 0; i < num_links; i++) {
> +		/*
> +		 * TODO: If we don't go through this path are the references
> +		 * freed inside ASoC?
> +		 */
> +		snd_soc_of_put_dai_link_codecs(&card->dai_link[i]);
> +		snd_soc_of_put_dai_link_cpus(&card->dai_link[i]);
> +	}
> +
> +	return ret;
> +}
[ ... ]
> +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];
> +
> +	if (props->is_speakers && substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
> +		switch (cmd) {
> +		case SNDRV_PCM_TRIGGER_START:
> +		case SNDRV_PCM_TRIGGER_RESUME:
> +		case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
> +			ma->bes_active |= BIT(rtd->dai_link->id);

[Severity: Medium]
Will these non-atomic bitwise operators drop updates to ma->bes_active?

ALSA DPCM allows different frontend PCMs to concurrently trigger their
respective backend DAI links on different CPUs. Using standard bitwise 
assignment (|= and &= ~) creates a read-modify-write data race between 
concurrent triggers, which could cause lost updates.

> +			break;
> +		case SNDRV_PCM_TRIGGER_SUSPEND:
> +		case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
> +		case SNDRV_PCM_TRIGGER_STOP:
> +			ma->bes_active &= ~BIT(rtd->dai_link->id);

[Severity: Low]
Does this bitwise NOT clear the upper 32 bits of ma->bes_active?

On 32-bit architectures, BIT() evaluates to a 32-bit unsigned long, making
the inverted mask also 32-bit. When implicitly zero-extended to u64 for the
assignment, it would effectively clear bits 32-63 of ma->bes_active.

While this platform is 64-bit, this truncation could still happen when the
driver is built under COMPILE_TEST. Using BIT_ULL() would prevent this.

> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +
> +		schedule_work(&ma->lock_update_work);

[Severity: High]
Does this code leave ma->lock_update_work unmanaged before devres teardown?

During card unregistration, any running PCM streams are stopped, which invokes
macaudio_be_trigger() with SNDRV_PCM_TRIGGER_STOP. That trigger forcefully
queues the lock_update_work.

If we don't call cancel_work_sync(&ma->lock_update_work) in the device remove
hook, the queued work might execute after the devres-managed ma structure has
already been freed, which would lead to a use-after-free.

> +	}
> +
> +	return 0;
> +}
[ ... ]
> +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);
> +
> +	if (!ma->speaker_lock_owner)
> +		return -EPERM;
> +
> +	if (uvalue->value.integer.value[0] != SPEAKER_MAGIC_VALUE)
> +		return -EINVAL;
> +
> +	/* Serves as a notification that the lock was lost at some point */
> +	if (ma->speaker_volume_was_locked) {

[Severity: Medium]
Can this check race with the timeout worker?

Since ma->speaker_volume_was_locked is checked and reset before acquiring
the volume_lock_mutex, a background worker could lock the volume and set
this flag to true just after this lockless check finishes.

If that happens, this function will bypass returning -ETIMEDOUT and proceed
to blindly reset the timeout, hiding the timeout event from userspace.

> +		ma->speaker_volume_was_locked = false;
> +		return -ETIMEDOUT;
> +	}
> +
> +	mutex_lock(&ma->volume_lock_mutex);
> +
> +	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);
[ ... ]
> +static void macaudio_slk_unlock(struct snd_kcontrol *kcontrol)
> +{
> +	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
> +	struct macaudio_snd_data *ma = snd_soc_card_get_drvdata(card);
> +
> +	ma->speaker_lock_owner = NULL;
> +	ma->speaker_lock_timeout = 0;
> +	macaudio_vlimit_update(ma);

[Severity: Medium]
Should this block be protected by ma->volume_lock_mutex?

This function modifies shared state (ma->speaker_lock_owner and
ma->speaker_lock_timeout) and calls macaudio_vlimit_update() without
acquiring the mutex. All other functions interacting with this state
(such as macaudio_slk_lock or macaudio_vlimit_timeout_work) rely on this
mutex for synchronization.

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

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260920-macaudio-v1-0-741cc20a74e5@gmail.com?part=16

  reply	other threads:[~2026-09-20  5:14 UTC|newest]

Thread overview: 73+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-20  4:53 [PATCH 00/28] Add speaker and headset jack support for Apple Silicon Macs James Calligeros
2026-09-20  4:53 ` [PATCH 01/28] dt-bindings: sound: Add Apple Macs sound peripherals James Calligeros
2026-09-20  5:06   ` sashiko-bot
2026-09-20 18:45   ` Sven Peter
2026-09-21 15:02   ` Mark Brown
2026-09-21 15:22   ` Mark Brown
2026-09-23 11:35   ` Krzysztof Kozlowski
2026-09-20  4:53 ` [PATCH 02/28] ASoC: cs42l42: Fix typo James Calligeros
2026-09-21 15:25   ` Mark Brown
2026-09-20  4:53 ` [PATCH 03/28] ASoC: cs42l42: Set a faster digital ramp-up rate James Calligeros
2026-09-20  4:53 ` [PATCH 04/28] ASoC: apple: mca: Fix PD link double-frees James Calligeros
2026-09-20  4:53 ` [PATCH 05/28] alsa: pcm: Remove the qos request only if active James Calligeros
2026-09-21 15:03   ` Mark Brown
2026-09-22  8:09   ` Mark Brown
2026-09-20  4:53 ` [PATCH 06/28] ALSA: dmaengine: Always terminate DMA when a PCM is closed James Calligeros
2026-09-20  5:05   ` sashiko-bot
2026-09-22 10:12   ` Mark Brown
2026-09-20  4:53 ` [PATCH 07/28] ALSA: Support nonatomic dmaengine PCMs James Calligeros
2026-09-20  5:09   ` sashiko-bot
2026-09-21 15:32   ` Mark Brown
2026-09-20  4:53 ` [PATCH 08/28] ALSA: control: Add kcontrol callbacks for lock/unlock James Calligeros
2026-09-20  5:11   ` sashiko-bot
2026-09-29  9:52   ` Takashi Iwai
2026-10-03  1:34     ` James Calligeros
2026-10-03  6:39       ` Takashi Iwai
2026-10-03  9:22         ` Takashi Iwai
2026-09-20  4:53 ` [PATCH 09/28] ASoC: ops: Move guts out of snd_soc_limit_volume James Calligeros
2026-09-20  4:53 ` [PATCH 10/28] ASoC: ops: Accept patterns in snd_soc_limit_volume James Calligeros
2026-09-20  5:12   ` sashiko-bot
2026-09-21 15:27   ` Mark Brown
2026-09-21 16:27     ` Charles Keepax
2026-09-23 11:12       ` James Calligeros
2026-09-20  4:53 ` [PATCH 11/28] ASoC: ops: Introduce 'snd_soc_deactivate_kctl' James Calligeros
2026-09-20  5:09   ` sashiko-bot
2026-09-22  9:53   ` Mark Brown
2026-09-20  4:53 ` [PATCH 12/28] ASoC: ops: Introduce 'soc_set_enum_kctl' James Calligeros
2026-09-20  5:10   ` sashiko-bot
2026-09-22  9:50   ` Mark Brown
2026-09-20  4:53 ` [PATCH 13/28] ASoC: card: Let 'fixup_controls' return errors James Calligeros
2026-09-20  4:53 ` [PATCH 14/28] ASoC: ops: Export snd_soc_control_matches() James Calligeros
2026-09-22  9:43   ` Mark Brown
2026-09-20  4:53 ` [PATCH 15/28] ASoC: tas2764: Set up V/ISENSE on codec probe James Calligeros
2026-09-20  5:10   ` sashiko-bot
2026-09-22  9:40   ` Mark Brown
2026-09-20  4:53 ` [PATCH 16/28] ASoC: apple: Add macaudio machine driver James Calligeros
2026-09-20  5:14   ` sashiko-bot [this message]
2026-09-22  9:38   ` Mark Brown
2026-09-26  1:06     ` James Calligeros
2026-09-28 11:03       ` Mark Brown
2026-09-30  7:36         ` James Calligeros
2026-09-30 11:17           ` Mark Brown
2026-09-20  4:53 ` [PATCH 17/28] arm64: dts: apple: t8103-j274: Add speaker/headset jack nodes James Calligeros
2026-09-20  5:14   ` sashiko-bot
2026-09-20  4:53 ` [PATCH 18/28] arm64: dts: apple: t8103-j313: " James Calligeros
2026-09-20  4:53 ` [PATCH 19/28] arm64: dts: apple: t8103-j293: " James Calligeros
2026-09-20  5:19   ` sashiko-bot
2026-09-20  4:53 ` [PATCH 20/28] arm64: dts: apple: t8103-j45x: Add headset " James Calligeros
2026-09-20  5:16   ` sashiko-bot
2026-09-20  4:54 ` [PATCH 21/28] arm64: dts: apple: t8112-j413: Add speaker/headset " James Calligeros
2026-09-20  5:14   ` sashiko-bot
2026-09-20  4:54 ` [PATCH 22/28] arm64: dts: apple: t8112-j415: " James Calligeros
2026-09-20  4:54 ` [PATCH 23/28] arm64: dts: apple: t8112-j473: " James Calligeros
2026-09-20  5:19   ` sashiko-bot
2026-09-20  4:54 ` [PATCH 24/28] arm64: dts: apple: t8112-j493: " James Calligeros
2026-09-20  5:17   ` sashiko-bot
2026-09-20  4:54 ` [PATCH 25/28] arm64: dts: apple: t600x-j31x: " James Calligeros
2026-09-20  5:17   ` sashiko-bot
2026-09-20  4:54 ` [PATCH 26/28] arm64: dts: apple: t600x-j375: " James Calligeros
2026-09-20  5:16   ` sashiko-bot
2026-09-20  4:54 ` [PATCH 27/28] arm64: dts: apple: t602x-j41x: " James Calligeros
2026-09-20  4:54 ` [PATCH 28/28] arm64: dts: apple: t602x-j47x: " James Calligeros
2026-09-20  5:15   ` sashiko-bot
2026-09-21 15:14 ` [PATCH 00/28] Add speaker and headset jack support for Apple Silicon Macs Mark Brown

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=20260920051431.214F01F000FF@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.