seq_ump_process_event() borrows client->out_rfile.output without
synchronizing with the first-open and last-close transition in
seq_ump_client_open() and seq_ump_client_close().
The last output unuse can therefore drop opened[STR_OUT] to zero and
release the rawmidi file while an in-flight event_input callback is still
inside snd_rawmidi_kernel_write(). That leaves the rawmidi substream
runtime exposed to teardown before the write path has taken its own
buffer reference.
Add a per-client rwlock for the event_input-visible output file. Publish
a newly opened output file under the write side, and hold the read side
from the output lookup through snd_rawmidi_kernel_write(). The last
output close copies and clears the visible output file under the write
side, then drops the lock and releases the saved rawmidi file. Use
IRQ-safe rwlock guards because event_input can also be reached from
atomic sequencer delivery.
The buggy scenario involves two paths, with each column showing the
order within that path:
path A label: event_input path path B label: last unuse path
1. seq_ump_process_event() reads 1. seq_ump_client_close()
client->out_rfile.output. drops opened[STR_OUT] to zero.
2. snd_rawmidi_kernel_write1() 2. snd_rawmidi_kernel_release()
has not yet pinned runtime. closes the output file.
3. The writer continues using 3. close_substream() frees
the borrowed substream. substream->runtime.
This keeps the output substream and runtime alive for the full
event_input write while keeping rawmidi release outside the rwlock.
KASAN reproduced this as a slab-use-after-free in
snd_rawmidi_kernel_write1(), with allocation through
seq_ump_use()/snd_seq_port_connect() and free through
seq_ump_unuse()/snd_seq_port_disconnect().
Suggested-by: Takashi Iwai <tiwai@suse.de>
Validation reproduced this kernel report:
KASAN slab-use-after-free in snd_rawmidi_kernel_write1+0x9d/0x400
RIP: 0033:0x7f5528af837f
Read of size 8
Call trace:
dump_stack_lvl+0x73/0xb0 (?:?)
print_report+0xd1/0x650 (?:?)
srso_alias_return_thunk+0x5/0xfbef5 (?:?)
__virt_addr_valid+0x1a7/0x340 (?:?)
kasan_complete_mode_report_info+0x64/0x200 (?:?)
kasan_report+0xf7/0x130 (?:?)
snd_rawmidi_kernel_write1+0x9d/0x400 (?:?)
__asan_load8+0x82/0xb0 (?:?)
update_stack_state+0x1ef/0x2d0 (?:?)
snd_rawmidi_kernel_write+0x1a/0x20 (?:?)
seq_ump_process_event+0xd4/0x120 (sound/core/seq/seq_ump_client.c:82)
__snd_seq_deliver_single_event+0x8a/0xe0 (?:?)
snd_seq_deliver_from_ump+0x2b2/0xd60 (?:?)
lock_acquire+0x14e/0x2e0 (?:?)
find_held_lock+0x31/0x90 (?:?)
snd_seq_port_use_ptr+0xa6/0xe0 (?:?)
__kasan_check_write+0x18/0x20 (?:?)
do_raw_read_unlock+0x32/0xa0 (?:?)
_raw_read_unlock+0x26/0x50 (?:?)
snd_seq_deliver_single_event+0x45c/0x4b0 (?:?)
snd_seq_deliver_event+0x10d/0x1b0 (?:?)
snd_seq_client_enqueue_event+0x192/0x240 (?:?)
snd_seq_write+0x2cd/0x450 (?:?)
apparmor_file_permission+0x20/0x30 (?:?)
security_file_permission+0x51/0x60 (?:?)
vfs_write+0x1ce/0x850 (?:?)
__fget_files+0x12b/0x220 (?:?)
lock_release+0xc8/0x2a0 (?:?)
__rcu_read_unlock+0x74/0x2d0 (?:?)
__fget_files+0x135/0x220 (?:?)
ksys_write+0x15a/0x180 (?:?)
rcu_is_watching+0x24/0x60 (?:?)
__x64_sys_write+0x46/0x60 (?:?)
x64_sys_call+0x7d/0x20d0 (?:?)
do_syscall_64+0xc1/0x360 (arch/x86/entry/syscall_64.c:87)
entry_SYSCALL_64_after_hwframe+0x77/0x7f (?:?)
Fixes: 81fd444aa3 ("ALSA: seq: Bind UMP device")
Signed-off-by: Zhang Cen <rollkingzzc@gmail.com>
Link: https://patch.msgid.link/20260520103249.3048345-1-rollkingzzc@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
snd_seq_create_port() walks client->ports_list_head looking for
the ordered insertion point and on loop fall-through passes
&p->list to list_add_tail():
list_for_each_entry(p, &client->ports_list_head, list) {
if (p->addr.port == port) {
kfree(new_port);
return -EBUSY;
}
if (p->addr.port > num)
break;
...
}
list_add_tail(&new_port->list, &p->list);
When the loop walks all entries without break (e.g., the new
port sorts last), p is past-the-end. &p->list aliases
&client->ports_list_head (the list head) via container_of offset
cancellation, so the insert lands at the list tail. That is the
intended behaviour, but the access is undefined per C11 even
though it works in practice.
Track an explicit insert_before pointer initialised to the list
head and overwritten to &p->list only when the loop breaks
early. The observable behaviour is unchanged.
Fixes: 9244b2c307 ("[ALSA] alsa core: convert to list_for_each_entry*")
Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
Link: https://patch.msgid.link/20260518194023.1667857-3-maoyixie.tju@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
snd_timer_dev_register() walks snd_timer_list looking for the
ordered insertion point and on loop fall-through passes
&timer1->device_list to list_add_tail():
list_for_each_entry(timer1, &snd_timer_list, device_list) {
...
break; /* on found-position */
...
}
list_add_tail(&timer->device_list, &timer1->device_list);
When the loop walks all entries without break, timer1 is
past-the-end. &timer1->device_list aliases &snd_timer_list (the
list head) via container_of offset cancellation, so the insert
lands at the list tail. That is the intended behaviour, but the
access is undefined per C11 even though it works in practice.
Track an explicit insert_before pointer initialised to the list
head and overwritten to &timer1->device_list only when the loop
breaks early. The observable behaviour is unchanged.
Fixes: 9244b2c307 ("[ALSA] alsa core: convert to list_for_each_entry*")
Signed-off-by: Maoyi Xie <maoyixie.tju@gmail.com>
Link: https://patch.msgid.link/20260518194023.1667857-2-maoyixie.tju@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Mirror of Mark Brown's ASoC: hdac_hdmi rate-limit patch (commit
[lkml.kernel.org/lkml/2025/6/13/1380]) for the generic snd_parse_eld()
helper used by ASoC hdmi-codec.
When a HDMI sink is disconnected (e.g. a board with two HDMI outputs and
only one cable), userspace audio servers like PipeWire keep probing the
disconnected card and trigger:
HDMI: Unknown ELD version 0
at every probe — easily 30+ messages per burst on rk3588. The same
applies to malformed ELD (MNL out of range). Both conditions are
expected when no sink is attached; rate-limit the dev_info() so the
kernel ring buffer does not fill up.
Signed-off-by: Francesco Saverio Pavone <pavone.lawyer@gmail.com>
Link: https://patch.msgid.link/20260516141244.21801-1-pavone.lawyer@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
snd_fasync_helper() updates fasync->on under snd_fasync_lock, and
snd_fasync_work_fn() now also evaluates fasync->on under the same
lock. snd_kill_fasync() still tests the flag before taking the lock,
leaving an unsynchronized read against FASYNC enable/disable updates.
Move the enabled-state check into the locked section.
Also clear fasync->on under snd_fasync_lock in snd_fasync_free()
before unlinking the pending entry. Together with the locked sender-side
check, this publishes teardown before flushing the deferred work and
prevents a racing sender from requeueing the entry after free has
started.
Fixes: ef34a0ae7a ("ALSA: core: Add async signal helpers")
Fixes: 8146cd333d ("ALSA: core: Fix potential data race at fasync handling")
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260506-alsa-core-fasync-on-lock-v1-1-ea48c77d6ca4@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
The sequencer UAPI defines group_filter as an unsigned int bitmap.
Bit 0 filters groupless messages and bits 1-16 filter UMP groups 1-16.
The internal snd_seq_client storage is only unsigned short, so bit 16
is truncated when userspace sets the filter. The same truncation affects
the automatic UMP client filter used to avoid delivery to inactive
groups, so events for group 16 cannot be filtered.
Store the internal bitmap as unsigned int and keep both userspace-provided
and automatically generated values limited to the defined UAPI bits.
Fixes: d2b7060777 ("ALSA: seq: Add UMP group filter")
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260506-alsa-seq-ump-group16-filter-v1-1-b75160bf6993@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Currently the runtime.oss.trigger field may be accessed concurrently
without protection, which may lead to the data race. And, in this
case, it may lead to more severe problem because it's a bit field; as
writing the data, it may overwrite other bit fields as well, which
confuses the operation completely, as spotted by fuzzing.
Fix it by covering runtime.oss.trigger bit fled also with the existing
params_lock mutex in both snd_pcm_oss_get_trigger() and
snd_pcm_oss_poll().
Reported-and-tested-by: Jaeyoung Chung <jjy600901@snu.ac.kr>
Closes: https://lore.kernel.org/20260423145330.210035-1-jjy600901@snu.ac.kr
Cc: <stable@vger.kernel.org>
Link: https://patch.msgid.link/20260424112205.123703-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
In snd_fasync_work_fn(), which is the offload work for traversing and
processing the pending fasync list, the call of kill_fasync() is done
outside the snd_fasync_lock for avoiding deadlocks. The problem is
that its the references of fasync->on, fasync->signal and fasync->poll
are done there also outside the lock. Since these may be modified by
snd_kill_fasync() call concurrently from other process, inconsistent
values might be passed to kill_fasync(). Although there shouldn't be
critical UAF, it's still better to be addressed.
This patch moves the kill_fasync() argument evaluations inside the
snd_fasync_lock for avoiding the data races above. The handling in
fasync->on flag is optimized in the loop to skip directly.
Also, for more clarity, snd_fasync_free() takes the lock and unlink
the pending entry more directly instead of clearing fasync->on flag.
Reported-by: Jake Lamberson <lamberson.jake@gmail.com>
Fixes: ef34a0ae7a ("ALSA: core: Add async signal helpers")
Cc: <stable@vger.kernel.org>
Link: https://patch.msgid.link/20260420061721.3253644-1-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
snd_ctl_elem_init_enum_names() advances pointer p through the names
buffer while decrementing buf_len. If buf_len reaches zero but items
remain, the next iteration calls strnlen(p, 0).
While strnlen(p, 0) returns 0 and would hit the existing name_len == 0
error path, CONFIG_FORTIFY_SOURCE's fortified strnlen() first checks
maxlen against __builtin_dynamic_object_size(). When Clang loses track
of p's object size inside the loop, this triggers a BRK exception panic
before the return value is examined.
Add a buf_len == 0 guard at the loop entry to prevent calling fortified
strnlen() on an exhausted buffer.
Found by kernel fuzz testing through Xiaomi Smartphone.
Fixes: 8d448162bd ("ALSA: control: add support for ENUMERATED user space controls")
Cc: stable@vger.kernel.org
Signed-off-by: Ziqing Chen <chenziqing@xiaomi.com>
Link: https://patch.msgid.link/20260414132437.261304-1-chenziqing@xiaomi.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
There are a number of mechanisms, including the userspace accessible
timestamp and buffer availability ioctl()s, which allow us to trigger
a timestamp update on a stream before it has been configured. Since
drivers might rely on stream configuration for reporting of pcm_io_frames,
including potentially doing a division by the number of channels, and
these operations are not meaningful for an unconfigured stream reject
attempts to read timestamps before any configuration is done.
Signed-off-by: Mark Brown <broonie@kernel.org>
Acked-by: Vinod Koul <vkoul@kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20260401-alsa-unconfigured-tstamp-v1-1-694c2cb5f71d@kernel.org
Without CONFIG_SND_DYNAMIC_MINORS, ALSA reserves only two fixed minors
for compress devices on each card: comprD0 and comprD1.
snd_find_free_minor() currently computes the compress minor as
type + dev without validating dev first, so device numbers greater than
1 spill into the HWDEP minor range instead of failing registration.
ASoC passes rtd->id to snd_compress_new(), so this can happen on real
non-dynamic-minor builds.
Add a dedicated fixed-minor check for SNDRV_DEVICE_TYPE_COMPRESS in
snd_find_free_minor() and reject out-of-range device numbers with
-EINVAL before constructing the minor.
Also remove the stale TODO in compress_offload.c that still claims
multiple compress nodes are missing.
Fixes: 3eafc959b3 ("ALSA: core: add support for compressed devices")
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260325-alsa-compress-static-minors-v1-1-0628573bee1c@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
snd_pcm_suspend_all() walks all PCM substreams and uses a lockless
runtime check to skip closed streams. It then calls snd_pcm_suspend()
for each remaining substream and finally runs snd_pcm_sync_stop() in a
second pass.
The runtime lifetime is still controlled by pcm->open_mutex in the
open/release path. That means a concurrent close can clear or free
substream->runtime after the initial check in snd_pcm_suspend_all(),
leaving the later suspend or sync-stop path to dereference a stale or
NULL runtime pointer.
Serialize snd_pcm_suspend_all() with pcm->open_mutex so the runtime
pointer stays stable across both loops. This matches the existing PCM
runtime lifetime rule already used by other core paths that access
substream->runtime outside the stream lock.
Suggested-by: Takashi Iwai <tiwai@suse.com>
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260327-alsa-pcm-suspend-open-close-lock-v2-1-cc4baca4dcd6@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
snd_seq_oss_write() currently returns the raw load_patch() callback
result for SEQ_FULLSIZE events.
That callback is documented as returning 0 on success and -errno on
failure, but snd_seq_oss_write() is the file write path and should
report the number of user bytes consumed on success. Some in-tree
backends also return backend-specific positive values, which can still
be shorter than the original write size.
Return the full byte count for successful SEQ_FULLSIZE writes.
Preserve negative errors and convert any nonnegative completion to the
original count.
Cc: stable@vger.kernel.org
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260324-alsa-seq-oss-fullsize-write-return-v1-1-66d448510538@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
snd_pcm_ioctl_sync_ptr_x32() still handles incoming appl_ptr updates
differently from the other SYNC_PTR paths. The native handler and the
32-bit compat handler both pass appl_ptr through pcm_lib_apply_appl_ptr(),
but the x32 handler still writes control->appl_ptr directly.
That direct assignment skips the common appl_ptr validation against
runtime->boundary and also bypasses the substream ack() callback.
This makes the x32 ioctl path behave differently from the native and
compat32 cases, and it can miss the driver notification that explicit
appl_ptr synchronization relies on.
Use pcm_lib_apply_appl_ptr() for x32 too, so appl_ptr updates are
validated consistently and drivers relying on ack() notifications
see the same behavior.
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260321-alsa-pcm-x32-sync-ptr-v1-1-02ce655657c6@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
snd_timer_check_slave() still walks all registered timers and all open
timer instances to find a matching master for a newly opened slave.
Maintain a global list of open master instances that can accept slave
links and use it for the slave lookup path instead. This keeps the
existing matching semantics while avoiding the nested walk over
snd_timer_list and each timer open_list_head.
The reverse path in snd_timer_check_master() already scans only the
pending slave list, so this makes both lookup paths closer in shape
without changing the master/slave linking logic.
Signed-off-by: Cássio Gabriel <cassiogabrielcontato@gmail.com>
Link: https://patch.msgid.link/20260316-alsa-timer-master-list-v1-1-fb95e547110a@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
__snd_pcm_set_state() writes runtime->state under the PCM stream lock.
However, the OSS I/O functions snd_pcm_oss_write3(), snd_pcm_oss_read3(),
snd_pcm_oss_writev3() and snd_pcm_oss_readv3() read runtime->state
without holding the stream lock, only holding oss.params_lock (a
different mutex that does not synchronize with the stream lock).
Since __snd_pcm_set_state() is called from IRQ context (e.g.,
snd_pcm_period_elapsed -> snd_pcm_update_state -> __snd_pcm_xrun ->
snd_pcm_stop -> snd_pcm_post_stop) while the OSS read/write paths
run in process context, these are concurrent accesses that constitute
a data race.
Rather than using READ_ONCE()/WRITE_ONCE() barriers, introduce a
snd_pcm_get_state() helper that reads runtime->state under the stream
lock, matching the locking discipline used elsewhere in the PCM layer.
Also export snd_pcm_set_state() for completeness.
Use snd_pcm_get_state() in all four OSS I/O functions, caching the
result in a local variable where the same snapshot is used for
multiple comparisons to avoid taking the lock repeatedly.
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
Link: https://patch.msgid.link/20260316085047.2876451-1-zzzccc427@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
In the drain loop, the local variable 'runtime' is reassigned to a
linked stream's runtime (runtime = s->runtime at line 2157). After
releasing the stream lock at line 2169, the code accesses
runtime->no_period_wakeup, runtime->rate, and runtime->buffer_size
(lines 2170-2178) — all referencing the linked stream's runtime without
any lock or refcount protecting its lifetime.
A concurrent close() on the linked stream's fd triggers
snd_pcm_release_substream() → snd_pcm_drop() → pcm_release_private()
→ snd_pcm_unlink() → snd_pcm_detach_substream() → kfree(runtime).
No synchronization prevents kfree(runtime) from completing while the
drain path dereferences the stale pointer.
Fix by caching the needed runtime fields (no_period_wakeup, rate,
buffer_size) into local variables while still holding the stream lock,
and using the cached values after the lock is released.
Fixes: f2b3614cef ("ALSA: PCM - Don't check DMA time-out too shortly")
Cc: stable@vger.kernel.org
Signed-off-by: Mehul Rao <mehulrao@gmail.com>
Link: https://patch.msgid.link/20260305193508.311096-1-mehulrao@gmail.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>
The put() operation is expected to return:
1) 0 on success if no changes were made
2) 1 on success if changes were made
3) error code otherwise
Currently 2) is usually ignored when writing control-operations. While
forcing compliance is not an option right now, make it easier for
developers to adhere to the expectations and notice problems by logging
them when CONFIG_SND_CTL_DEBUG is enabled.
Due to large size of struct snd_ctl_elem_value, 'value_buf' is provided
as a reusable buffer for kctl->put() verification. This prevents
exhausting the stack when verifying the operation.
>From user perspective, patch introduces a new trace/events category
'snd_ctl' containing a single 'snd_ctl_put' event type. Log sample:
amixer-1086 [003] ..... 8.035939: snd_ctl_put: success: expected=0, actual=0 for ctl numid=1, iface=MIXER, name='Master Playback Volume', index=0, device=0, subdevice=0, card=0
amixer-1087 [003] ..... 8.938721: snd_ctl_put: success: expected=1, actual=1 for ctl numid=1, iface=MIXER, name='Master Playback Volume', index=0, device=0, subdevice=0, card=0
amixer-1088 [003] ..... 9.631470: snd_ctl_put: success: expected=1, actual=1 for ctl numid=1, iface=MIXER, name='Master Playback Volume', index=0, device=0, subdevice=0, card=0
amixer-1089 [000] ..... 9.636786: snd_ctl_put: fail: expected=1, actual=0 for ctl numid=5, iface=MIXER, name='Loopback Mute', index=0, device=0, subdevice=0, card=0
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
Reviewed-by: Mark Brown <broonie@kernel.org>
Reviewed-by: Jaroslav Kysela <perex@perex.cz>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20260224205619.584795-1-cezary.rojewski@intel.com
This converts some of the visually simpler cases that have been split
over multiple lines. I only did the ones that are easy to verify the
resulting diff by having just that final GFP_KERNEL argument on the next
line.
Somebody should probably do a proper coccinelle script for this, but for
me the trivial script actually resulted in an assertion failure in the
middle of the script. I probably had made it a bit _too_ trivial.
So after fighting that far a while I decided to just do some of the
syntactically simpler cases with variations of the previous 'sed'
scripts.
The more syntactically complex multi-line cases would mostly really want
whitespace cleanup anyway.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>