* [PATCH 0/2] ALSA: aloop: Fix notify mode, add a selftest
@ 2026-10-03 13:26 Henrik Enquist via B4 Relay
2026-10-03 13:26 ` [PATCH 1/2] ALSA: aloop: Don't constrain playback params in notify mode Henrik Enquist via B4 Relay
2026-10-04 18:37 ` [PATCH 0/2] ALSA: aloop: Fix notify mode, add a selftest Takashi Iwai
0 siblings, 2 replies; 3+ messages in thread
From: Henrik Enquist via B4 Relay @ 2026-10-03 13:26 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, Mark Brown, Shuah Khan
Cc: linux-sound, linux-kernel, linux-kselftest, Pavel Hofman, stable,
Henrik Enquist
Hi,
snd-aloop has a notify mode (the pcm_notify module parameter, or the
"PCM Notify" control per cable) where the playback side may switch
format, rate and channels while a capture is running. The driver then
stops the capture and updates the "PCM Slave" controls, so the capture
application can reopen with the new parameters. alsaloop enables it in
its slave mode, and I'd like to use it in CamillaDSP, which captures
from a loopback and needs to follow whatever the player outputs.
This hasn't worked since 2018. Commit 898dfe4687f4 ("ALSA: aloop: Fix
racy hw constraints adjustment") moved the hw rules over to the shared
cable->hw. That fixed a real race, but it also pins the playback side
to the capture's parameters in notify mode. Pavel reported it on
alsa-devel in 2020 [1], and Jaroslav reproduced it and pointed at the
same commit:
It seems that 898dfe4687f4 from Takashi broke this functionality
(tied the cable parameters more strictly, so the playback cannot set
freely own parameters for the pcm_notify=1 case). We need to find
another way to detach capture stream in this case.
The thread ended there, without a patch.
The breakage is quiet, which probably explains why it lasted. A player
that probes the playback device sees only the capture's rate and
format, and resamples to them. aplay prints a warning, most players
don't. There's no rate change, no stopped capture and no event.
Patch 1 skips the hw rules for the playback side when notify is on,
which gives it back the freedom loopback_open() still intends it to
have. That makes the capture stop in loopback_check_format() reachable
again. That path was hardened this year by 826af7fa62e3 ("ALSA: aloop:
Fix racy access at PCM trigger") and e5c33cdc6f40 ("ALSA: aloop: Fix
peer runtime UAF during format-change stop"), and with those in place
I'm comfortable turning it back on. They need to go first in any
stable backport.
The period bytes rule is skipped as well. It came later, for the sound
timer source, but without skipping it a playback that switches rate
keeps the old capture's period size. The stream then runs with a period
that doesn't match the timer, and every switch fills dmesg with "Period
size ... not corresponding to timer resolution".
Patch 2 adds a selftest, so this doesn't break silently again. It
doesn't need to go to stable, so it can go via for-next if that's
easier.
Testing: for-linus at b4e7fc36e31f, arm64 VM, kernel with KASAN,
lockdep, DEBUG_ATOMIC_SLEEP and DEBUG_LIST, comparing the patched
driver with the unpatched one built from the same tree. With the
jiffies, hrtimer and sound timer (via snd-dummy) sources:
- With notify on, rate, format and channel changes on the playback
side now go through, stop the capture and update the controls. With
notify off they're refused as before.
- A capture that reopens at "PCM Slave Rate" after each stop follows a
player alternating between 44.1k and 96k files, 20 of 20 switches,
with both RW and mmap access. Unpatched: 10 of 20, the playback stays
pinned to the capture's rate.
- A capture opened second is still pinned to the playback.
- 2 minutes of random open/start/close on both ends per source gave no
KASAN or lockdep reports. The sound timer source logs an occasional
"snd_timer_stop ... failed with -16", at the same rate unpatched.
- The new selftest fails 2 of 4 tests unpatched, and passes 4 of 4
patched, 50 runs in a row.
Not tested: real hardware, other architectures, pause/resume, more
than two substreams.
One question, following up on Takashi's point in that thread about
telling user space that a stream got invalidated. After the stop, the
capture drains and then reads return -EBADFD. alsa-lib now documents
-ENODATA as meaning the PCM must be completely restarted, which looks
like the right signal here. Would you want aloop to report that
instead? I left it out since it changes what user space sees, but I'm
happy to do it as a follow-up.
AI disclosure: I used Claude Code (Claude Opus 5.5) for this. Starting
from my description of the problem and the 2020 thread, it did the
root cause analysis from the driver source and git history, set up the
test kernels and VM, wrote the test scripts, ran the measurements, and
wrote both patches and their changelogs. I reviewed all of it, and both
patches carry an Assisted-by tag.
[1] https://lore.kernel.org/all/b4af9071-f8d7-5b47-4d7a-c5743bd67394@ivitera.com/
Henrik
---
Henrik Enquist (2):
ALSA: aloop: Don't constrain playback params in notify mode
selftests/alsa: Add a test for snd-aloop notify mode
sound/drivers/aloop.c | 20 ++
tools/testing/selftests/alsa/.gitignore | 1 +
tools/testing/selftests/alsa/Makefile | 2 +-
tools/testing/selftests/alsa/aloop-test.c | 345 ++++++++++++++++++++++++++++++
4 files changed, 367 insertions(+), 1 deletion(-)
---
base-commit: 13dffca9043500ed6033d770b491a043fb952bc5
change-id: 20261003-aloop-notify-fix-4f7beb408ab8
Best regards,
--
Henrik Enquist <henrik.enquist@gmail.com>
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH 1/2] ALSA: aloop: Don't constrain playback params in notify mode
2026-10-03 13:26 [PATCH 0/2] ALSA: aloop: Fix notify mode, add a selftest Henrik Enquist via B4 Relay
@ 2026-10-03 13:26 ` Henrik Enquist via B4 Relay
2026-10-04 18:37 ` [PATCH 0/2] ALSA: aloop: Fix notify mode, add a selftest Takashi Iwai
1 sibling, 0 replies; 3+ messages in thread
From: Henrik Enquist via B4 Relay @ 2026-10-03 13:26 UTC (permalink / raw)
To: Jaroslav Kysela, Takashi Iwai, Mark Brown, Shuah Khan
Cc: linux-sound, linux-kernel, linux-kselftest, Pavel Hofman, stable,
Henrik Enquist
From: Henrik Enquist <henrik.enquist@gmail.com>
With notify enabled (the pcm_notify module parameter or the "PCM
Notify" control), the playback side of a cable may change format, rate
and channels while a capture is running. loopback_check_format() then
stops the capture and notifies the "PCM Slave" controls, so that the
capture application can reopen with the new parameters. For this
reason loopback_open() still gives a notify mode playback the
unconstrained loopback_pcm_hardware.
Since commit 898dfe4687f4 ("ALSA: aloop: Fix racy hw constraints
adjustment") the hw rules refine against the shared cable->hw instead
of the substream's own runtime->hw. This pins the playback side to the
parameters of an open capture whatever the notify setting is. A player
probing the device sees only the capture's rate, format and channels,
and the format change path in loopback_check_format() can't be reached
by a new playback.
Skip the format, rate and channels rules for the playback substream
when notify is enabled. Skip the period bytes rule as well: with a
sound timer source, params_change() already takes the period size from
a notify mode playback, and keeping the old capture's period bytes
leaves a playback at another rate with a period that doesn't match the
timer. The capture side stays constrained to the cable parameters.
The capture stop this makes reachable again was hardened by commit
826af7fa62e3 ("ALSA: aloop: Fix racy access at PCM trigger") and
commit e5c33cdc6f40 ("ALSA: aloop: Fix peer runtime UAF during
format-change stop"), which need to be applied first when backporting.
Reproducer, with notify enabled on the cable:
arecord -D hw:Loopback,1,0 -r 44100 -f S32_LE -c 2 /dev/null &
aplay -D hw:Loopback,0,0 96k.wav
Before: aplay is forced to 44100 Hz and the capture keeps running.
After: aplay runs at 96000 Hz, the capture is stopped and "PCM Slave
Rate" reports 96000.
Fixes: 898dfe4687f4 ("ALSA: aloop: Fix racy hw constraints adjustment")
Reported-by: Pavel Hofman <pavel.hofman@ivitera.com>
Closes: https://lore.kernel.org/all/b4af9071-f8d7-5b47-4d7a-c5743bd67394@ivitera.com/
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Henrik Enquist <henrik.enquist@gmail.com>
---
sound/drivers/aloop.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 81cb1f59f7..5f0e2dc967 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1073,6 +1073,18 @@ static unsigned int get_cable_index(struct snd_pcm_substream *substream)
return !substream->stream;
}
+/*
+ * In notify mode the playback side may change format, rate, channels and,
+ * with a sound timer, the period size. A running capture that no longer
+ * matches is stopped by loopback_check_format().
+ * call in loopback->cable_lock
+ */
+static bool loopback_playback_unconstrained(struct loopback_pcm *dpcm)
+{
+ return dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ get_notify(dpcm);
+}
+
static int rule_format(struct snd_pcm_hw_params *params,
struct snd_pcm_hw_rule *rule)
{
@@ -1082,6 +1094,8 @@ static int rule_format(struct snd_pcm_hw_params *params,
snd_mask_none(&m);
scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ if (loopback_playback_unconstrained(dpcm))
+ return 0;
m.bits[0] = (u_int32_t)cable->hw.formats;
m.bits[1] = (u_int32_t)(cable->hw.formats >> 32);
}
@@ -1096,6 +1110,8 @@ static int rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval t;
scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ if (loopback_playback_unconstrained(dpcm))
+ return 0;
t.min = cable->hw.rate_min;
t.max = cable->hw.rate_max;
}
@@ -1112,6 +1128,8 @@ static int rule_channels(struct snd_pcm_hw_params *params,
struct snd_interval t;
scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ if (loopback_playback_unconstrained(dpcm))
+ return 0;
t.min = cable->hw.channels_min;
t.max = cable->hw.channels_max;
}
@@ -1128,6 +1146,8 @@ static int rule_period_bytes(struct snd_pcm_hw_params *params,
struct snd_interval t;
scoped_guard(mutex, &dpcm->loopback->cable_lock) {
+ if (loopback_playback_unconstrained(dpcm))
+ return 0;
t.min = cable->hw.period_bytes_min;
t.max = cable->hw.period_bytes_max;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 0/2] ALSA: aloop: Fix notify mode, add a selftest
2026-10-03 13:26 [PATCH 0/2] ALSA: aloop: Fix notify mode, add a selftest Henrik Enquist via B4 Relay
2026-10-03 13:26 ` [PATCH 1/2] ALSA: aloop: Don't constrain playback params in notify mode Henrik Enquist via B4 Relay
@ 2026-10-04 18:37 ` Takashi Iwai
1 sibling, 0 replies; 3+ messages in thread
From: Takashi Iwai @ 2026-10-04 18:37 UTC (permalink / raw)
To: henrik.enquist
Cc: Jaroslav Kysela, Takashi Iwai, Mark Brown, Shuah Khan,
linux-sound, linux-kernel, linux-kselftest, Pavel Hofman, stable
On Sat, 03 Oct 2026 15:26:10 +0200,
Henrik Enquist via B4 Relay wrote:
>
> Hi,
>
> snd-aloop has a notify mode (the pcm_notify module parameter, or the
> "PCM Notify" control per cable) where the playback side may switch
> format, rate and channels while a capture is running. The driver then
> stops the capture and updates the "PCM Slave" controls, so the capture
> application can reopen with the new parameters. alsaloop enables it in
> its slave mode, and I'd like to use it in CamillaDSP, which captures
> from a loopback and needs to follow whatever the player outputs.
>
> This hasn't worked since 2018. Commit 898dfe4687f4 ("ALSA: aloop: Fix
> racy hw constraints adjustment") moved the hw rules over to the shared
> cable->hw. That fixed a real race, but it also pins the playback side
> to the capture's parameters in notify mode. Pavel reported it on
> alsa-devel in 2020 [1], and Jaroslav reproduced it and pointed at the
> same commit:
>
> It seems that 898dfe4687f4 from Takashi broke this functionality
> (tied the cable parameters more strictly, so the playback cannot set
> freely own parameters for the pcm_notify=1 case). We need to find
> another way to detach capture stream in this case.
>
> The thread ended there, without a patch.
>
> The breakage is quiet, which probably explains why it lasted. A player
> that probes the playback device sees only the capture's rate and
> format, and resamples to them. aplay prints a warning, most players
> don't. There's no rate change, no stopped capture and no event.
>
> Patch 1 skips the hw rules for the playback side when notify is on,
> which gives it back the freedom loopback_open() still intends it to
> have. That makes the capture stop in loopback_check_format() reachable
> again. That path was hardened this year by 826af7fa62e3 ("ALSA: aloop:
> Fix racy access at PCM trigger") and e5c33cdc6f40 ("ALSA: aloop: Fix
> peer runtime UAF during format-change stop"), and with those in place
> I'm comfortable turning it back on. They need to go first in any
> stable backport.
>
> The period bytes rule is skipped as well. It came later, for the sound
> timer source, but without skipping it a playback that switches rate
> keeps the old capture's period size. The stream then runs with a period
> that doesn't match the timer, and every switch fills dmesg with "Period
> size ... not corresponding to timer resolution".
>
> Patch 2 adds a selftest, so this doesn't break silently again. It
> doesn't need to go to stable, so it can go via for-next if that's
> easier.
>
> Testing: for-linus at b4e7fc36e31f, arm64 VM, kernel with KASAN,
> lockdep, DEBUG_ATOMIC_SLEEP and DEBUG_LIST, comparing the patched
> driver with the unpatched one built from the same tree. With the
> jiffies, hrtimer and sound timer (via snd-dummy) sources:
> - With notify on, rate, format and channel changes on the playback
> side now go through, stop the capture and update the controls. With
> notify off they're refused as before.
> - A capture that reopens at "PCM Slave Rate" after each stop follows a
> player alternating between 44.1k and 96k files, 20 of 20 switches,
> with both RW and mmap access. Unpatched: 10 of 20, the playback stays
> pinned to the capture's rate.
> - A capture opened second is still pinned to the playback.
> - 2 minutes of random open/start/close on both ends per source gave no
> KASAN or lockdep reports. The sound timer source logs an occasional
> "snd_timer_stop ... failed with -16", at the same rate unpatched.
> - The new selftest fails 2 of 4 tests unpatched, and passes 4 of 4
> patched, 50 runs in a row.
> Not tested: real hardware, other architectures, pause/resume, more
> than two substreams.
>
> One question, following up on Takashi's point in that thread about
> telling user space that a stream got invalidated. After the stop, the
> capture drains and then reads return -EBADFD. alsa-lib now documents
> -ENODATA as meaning the PCM must be completely restarted, which looks
> like the right signal here. Would you want aloop to report that
> instead? I left it out since it changes what user space sees, but I'm
> happy to do it as a follow-up.
>
> AI disclosure: I used Claude Code (Claude Opus 5.5) for this. Starting
> from my description of the problem and the 2020 thread, it did the
> root cause analysis from the driver source and git history, set up the
> test kernels and VM, wrote the test scripts, ran the measurements, and
> wrote both patches and their changelogs. I reviewed all of it, and both
> patches carry an Assisted-by tag.
>
> [1] https://lore.kernel.org/all/b4af9071-f8d7-5b47-4d7a-c5743bd67394@ivitera.com/
>
> Henrik
>
> ---
> Henrik Enquist (2):
> ALSA: aloop: Don't constrain playback params in notify mode
> selftests/alsa: Add a test for snd-aloop notify mode
Applied both patches to for-next branch now. Thanks.
Takashi
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-04 18:38 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-03 13:26 [PATCH 0/2] ALSA: aloop: Fix notify mode, add a selftest Henrik Enquist via B4 Relay
2026-10-03 13:26 ` [PATCH 1/2] ALSA: aloop: Don't constrain playback params in notify mode Henrik Enquist via B4 Relay
2026-10-04 18:37 ` [PATCH 0/2] ALSA: aloop: Fix notify mode, add a selftest Takashi Iwai
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox