Commit graph

1114 commits

Author SHA1 Message Date
Wim Taymans
4a34da368e security: fix potential buffer over-read in combine-sink name encoding
spa_json_encode_string was called with sizeof(name)-1, which would
not write a null terminator on truncation. Use sizeof(name) and skip
sink names that don't fit in the buffer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:27:37 +02:00
Wim Taymans
912f7f5c64 security: add missing NULL check after pw_properties_new in zeroconf
pw_properties_new can return NULL under OOM. The result was used
directly without a check, leading to a NULL pointer dereference.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:25:19 +02:00
Wim Taymans
e1f4c441f4 security: fix OOB read in IEC958 format enum parsing
In the SPA_CHOICE_Enum case, values[index+1] was used to skip the
default value at index 0, but the bounds check only validated index,
not index+1. Move bounds checks into each case with the correct limit.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:19:41 +02:00
Wim Taymans
390874e7c3 security: fix JSON injection in simple-protocol-tcp address
The listen address was inserted into a JSON array without escaping.
Build the address string first, then encode it with
spa_json_encode_string.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:15:36 +02:00
Wim Taymans
0ae17566f2 security: reject unknown tags in message_get to prevent va_arg desync
The switch in message_get had no default case. An unrecognized tag byte
from a malicious client would skip the switch body without consuming
the va_arg parameter, desynchronizing all subsequent argument reads
and causing undefined behavior.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 09:14:08 +02:00
Wim Taymans
d4a1278018 security: add missing create_tag checks in stream command handlers
do_cork_stream, do_flush_trigger_prebuf_stream, and do_set_stream_name
did not check whether the stream had completed format negotiation.
Add create_tag guards matching the pattern in do_set_stream_buffer_attr.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:26:01 +02:00
Wim Taymans
6d2600c09d security: fix one-byte OOB read in module_args_add_props
A trailing backslash in a module argument string would cause the
escape handling to advance past the null terminator, reading one
byte out of bounds on the next loop iteration.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:24:13 +02:00
Wim Taymans
c6faaff410 security: add missing NULL check after strndup in cmd.c
strndup can return NULL under OOM. The result was passed directly to
spa_json_begin_array which would dereference the NULL pointer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:21:16 +02:00
Wim Taymans
8e7ca70352 security: add missing create_tag check in update_stream_sample_rate
If a client sends UPDATE_PLAYBACK_STREAM_SAMPLE_RATE before format
negotiation completes, stream->ss.rate could be 0, causing a
floating-point division by zero. Add the same create_tag guard used
in do_set_stream_buffer_attr.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:20:04 +02:00
Wim Taymans
890c06117a security: fix integer overflow in port latency offset conversion
Client-supplied int64_t offset was multiplied by 1000 without overflow
check. Use spa_overflow_mul to detect and reject values that would
overflow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:19:03 +02:00
Wim Taymans
6a8c2469c5 security: fix create_tag check to allow upload stream memblocks
The create_tag guard added in a2de6c886 also rejected memblocks for
upload streams, which never clear create_tag. Upload streams allocate
their buffer immediately, so the NULL deref risk does not apply to
them. Exempt STREAM_TYPE_UPLOAD from the check.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:16:52 +02:00
Wim Taymans
3a3579ed68 security: fix operation counter leak in operation_complete
operation_complete removed the operation from the list and freed it
but never decremented client->n_operations. After 64 completed
operations the client would be permanently locked out.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 18:15:22 +02:00
Wim Taymans
344c9265a6 security: fix JSON injection in pulse module arguments
Use spa_json_encode_string to escape user-supplied strings before
inserting them into JSON configs in module-always-sink,
module-x11-bell, and module-switch-on-connect.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:56:12 +02:00
Wim Taymans
7c2d8f7251 security: add missing NULL checks after message_alloc in reply
Both reply_new and reply_error passed the message_alloc result directly
to message_put without checking for NULL, which would cause a NULL
pointer dereference on allocation failure.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:54:21 +02:00
Wim Taymans
1b8962d7c2 security: fix JSON injection in native-protocol-tcp address
The listen address was inserted into JSON without escaping. Build the
address string first, then encode it with spa_json_encode_string to
prevent injection of arbitrary JSON keys.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:52:19 +02:00
Wim Taymans
c5c2d197dc security: fix JSON injection in LADSPA plugin/label strings
The plugin and label parameters in module-ladspa-sink and
module-ladspa-source were inserted into the filter-chain JSON config
without escaping. Use spa_json_encode_string to prevent injection.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:50:49 +02:00
Wim Taymans
bc4e1a989c security: reject zero-channel volume in PulseAudio message parsing
read_cvolume accepted channels=0, creating a degenerate zero-length
volume array that is passed to pw_stream_set_control and SPA pod
building. Reject zero channels alongside the existing CHANNELS_MAX
upper bound check.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:23:43 +02:00
Wim Taymans
807b93fb05 security: add per-client pending sample limit in PulseAudio protocol
There was no limit on concurrent PLAY_SAMPLE operations per client.
Each creates a PipeWire stream, allowing a client to exhaust server
resources. Add a MAX_PENDING_SAMPLES (64) limit per client.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:19:08 +02:00
Wim Taymans
138e30df38 security: add per-client operation count limit in PulseAudio protocol
There was no limit on pending operations per client. Commands like
SET_SINK_VOLUME each allocate an operation that persists until a
manager sync completes. A client flooding these commands can exhaust
server memory. Add a MAX_OPERATIONS (64) limit per client.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:17:38 +02:00
Wim Taymans
f32295429f security: fix module leak on OOM in PulseAudio do_load_module
If module_create succeeded but the subsequent calloc for
pending_module failed, the module was leaked in the modules map.
Move the calloc before module_create so failure cleanup is trivial.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:13:56 +02:00
Wim Taymans
2d8dc8b457 security: fix JSON injection in PulseAudio do_set_default
The device name was interpolated into a JSON metadata string without
escaping. A node with crafted name containing quote characters could
inject arbitrary JSON keys into the default sink/source metadata.
Use spa_json_encode_string to properly escape the value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:09:50 +02:00
Wim Taymans
d3e1be8b6e security: fix division by zero in PulseAudio set_stream_buffer_attr
A client can create a stream with invalid sample_spec (rate=0) via
format_info negotiation, then send SET_STREAM_BUFFER_ATTR before
negotiation completes. fix_playback_buffer_attr divides by ss.rate,
crashing the daemon. Reject buffer attr changes on streams that
have not completed format negotiation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 17:08:17 +02:00
Wim Taymans
cd7bb1e37d security: validate sample rate in PulseAudio update_stream_sample_rate
The client-provided rate was used without validation. A zero or
excessively large rate produces extreme correction values passed
to pw_stream_set_control. Reject rates that are zero or exceed
RATE_MAX.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:51:46 +02:00
Wim Taymans
5f02641859 security: add missing NULL check in PulseAudio message_dump
pw_properties_new can return NULL on OOM. Passing NULL to read_props
causes a NULL pointer dereference through pw_properties_set. Only
reachable when debug logging is enabled.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:45:09 +02:00
Wim Taymans
9c1bc64af4 security: add missing NULL check after message_alloc in PulseAudio server
message_alloc can return NULL on allocation failure but the result
was not checked, causing the next do_read call to misinterpret
the NULL as a protocol error instead of an OOM condition.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:42:59 +02:00
Wim Taymans
52afec565b security: add total sample cache size limit in PulseAudio protocol
There was no limit on the total size of the sample cache. A client
could upload many samples to exhaust server memory. Add a configurable
pulse.max-sample-cache property (default 64MB) to cap the total size
of all cached samples.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:39:57 +02:00
Wim Taymans
37990b5e90 security: add per-client stream limit in PulseAudio protocol
There was no limit on the number of streams a single client could
create. Each stream allocates a 4MB ring buffer, allowing a malicious
client to exhaust server memory. Add a configurable pulse.max-streams
property (default 64) to limit streams per client.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:28:05 +02:00
Wim Taymans
80ec1f1d10 security: fix JSON injection in PulseAudio stream-restore
The device_name from a client message was interpolated directly into
a JSON string without escaping. A malicious client could inject
arbitrary JSON keys by including quote characters in the device name.
Use spa_json_encode_string to properly escape the value.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:16:44 +02:00
Wim Taymans
a2de6c886e security: fix NULL dereference in PulseAudio handle_memblock
A client can send memblock data to a playback stream channel before
format negotiation completes and the stream buffer is allocated,
causing a NULL pointer dereference crash. Reject memblock data for
streams that are still being created (create_tag != SPA_ID_INVALID).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:12:49 +02:00
Wim Taymans
808bcf39cd security: fix heap OOB read in PulseAudio sample cache playback
The sample cache upload buffer is allocated as MAXLENGTH (4MB) but
sample->length can be up to SCACHE_ENTRY_SIZE_MAX (16MB). During
playback, the read offset can exceed the buffer size, causing an
out-of-bounds heap read. Wrap the offset into the ring buffer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 16:10:44 +02:00
Wim Taymans
d23ec56f0d security: fix stack buffer overflow in PulseAudio channel map parsing
format_info_to_spec parses the format.channel_map property without
checking against CHANNELS_MAX (64) before writing to map->map[].
A client supplying more than 64 channel names overflows the stack-
allocated channel_map buffer.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-29 15:49:50 +02:00
Wim Taymans
0c4a0dcf7d security: fix file descriptor leak in PulseAudio server on_connect error path
File and Resource Handling: Medium

In on_connect(), if client_new() fails or pw_loop_add_io() fails, the
accepted client_fd is never closed. The error path only calls
client_free() which relies on pw_loop_destroy_source() to close the fd,
but if the source was never created, the fd leaks.

Fix by closing client_fd in the error path when it has not been
transferred to a loop source.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 14:28:06 +02:00
Wim Taymans
a12cc84df4 security: fix integer overflow in PulseAudio message read_arbitrary
Memory Safety: High

The read_arbitrary() bounds check used `m->offset + len > m->length`
where len is an attacker-controlled uint32_t read from the PulseAudio
protocol message. When m->offset is small and len is close to
UINT32_MAX, the addition wraps around to a small value, bypassing
the bounds check. This allows read_arbitrary() to return a pointer
within the message buffer but report an enormous length to the caller,
leading to out-of-bounds memory reads.

Fixed by rearranging the arithmetic to use subtraction:
`len > m->length - m->offset`, which cannot overflow since
m->offset <= m->length is maintained as an invariant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-28 12:56:28 +02:00
Wim Taymans
ebe9b087ad security: replace strcat with bounds-explicit memcpy in pulse utils
Memory Safety: Low

Although the preceding length check ensures the strcat is safe, using
strcat makes the bounds guarantee implicit. Replace with memcpy using
the already-computed length, making the bounded copy explicit and
avoiding a redundant scan of the destination string.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 16:14:23 +02:00
Wim Taymans
6efaf12d00 security: clamp channel count in PulseAudio volume control handler
Memory Safety: High

The stream_control_info() callback copied control->n_values floats
into stream->volume.values without checking bounds. The source allows
up to MAX_VALUES (256) entries but the destination volume array is
only CHANNELS_MAX (64) entries, so a stream with more than 64 channel
volumes would overflow the buffer. Clamp n_values to CHANNELS_MAX
before the copy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:24:30 +02:00
Wim Taymans
ed2c0ad4ee spa: add spa_alloca that does overflow and limit checks
Make a function like alloca but with overflow checks and a max
allocation size.

Use this function where we can and also make sure that all alloca calls
are in some way limited.
2026-04-27 10:53:44 +02:00
Wim Taymans
a9f1ad414e security: fix integer truncation in peer_name alloca size
Memory Safety: Medium

The strlen() return value (size_t) is stored in an int before being
passed to alloca(). If a malicious client sets an extremely long
PW_KEY_NODE_NAME property, the addition could overflow the int,
resulting in a small or negative alloca size and a subsequent buffer
overflow in snprintf().

Change the type to size_t and add a bounds check to prevent
excessively large stack allocations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 16:09:08 +02:00
Wim Taymans
d60ae4a1df security: fix unchecked alloca in pulse protocol message handling
Memory Safety: High

The add_stream_group() function computes a buffer size from the sum of
multiple string lengths, including user-controlled dictionary values
(media role, app name, etc.), and passes it to alloca() without any
bounds check. A malicious client could send very long property strings
causing an integer overflow in the size computation (wrapping a
negative/small int) or an excessively large stack allocation, leading
to a stack overflow.

Add a bounds check to reject sizes that are negative or exceed 1024
bytes before calling alloca().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 16:08:45 +02:00
Wim Taymans
84f8230a47 security: fix TOCTOU race and symlink following in pulse protocol socket
File and Resource Handling: Medium

The PulseAudio protocol server socket initialization uses stat() to
check for existing socket files before unlinking and rebinding. This has
the same two vulnerabilities recently fixed in module-protocol-native:

1. stat() follows symlinks, so an attacker who places a symlink at the
   socket path (e.g., in XDG_RUNTIME_DIR/pulse/) can trick the daemon
   into unlinking the symlink target.

2. The gap between stat() and unlink() creates a TOCTOU race window.

Fix by using lstat() instead of stat() and adding an explicit symlink
check that refuses to proceed if the path is a symbolic link, consistent
with the approach in module-protocol-native.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
a6155387da security: fix unchecked alloca in pulse-server property list handling
Memory Safety: Medium

Two alloca() calls in the PulseAudio protocol server were missed by the
previous alloca bounds-checking fix (commit 0d2877c0d):

1. fill_node_info_proplist() adds n_items counts from node properties
   and client properties without checking the total before alloca().
   A client with a very large number of properties can exhaust the stack.

2. fill_card_info() uses pi->n_props from port info for an alloca()
   without bounds checking. A card object with many port properties can
   similarly exhaust the stack.

Add MAX_ALLOCA_SIZE checks consistent with the existing pattern to
prevent stack overflow from large property counts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
00413a3263 security: fix stack exhaustion via unbounded alloca in pulse-server
Memory Safety: Medium

Several functions in the PulseAudio protocol implementation use alloca()
to allocate arrays of port_info, profile_info, or dict_item structs
based on counts derived from card parameters or client property lists.
These counts have no upper bounds, so a card object with a very large
number of parameters or a client sending many properties can cause
alloca() to exhaust the stack, resulting in a stack overflow crash.

Add a MAX_ALLOCA_SIZE (64KB) limit and check element counts before each
alloca() call. If the requested allocation exceeds the limit, the
function returns -ENOMEM instead of crashing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-24 15:55:35 +02:00
Wim Taymans
8d352fe52e security: fix integer overflow in PulseAudio message buffer allocation
Memory Safety: High

In ensure_size(), the check `m->length + size <= m->allocated` could
overflow when both m->length and size are large uint32_t values,
wrapping around to a small number and incorrectly passing the bounds
check. This could allow writing past the end of the allocated buffer.

Rewrite the check as `size <= m->allocated - m->length` which cannot
overflow since we already verified m->length <= m->allocated. Also add
an explicit overflow check for the new allocation size calculation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 17:46:47 +02:00
Wim Taymans
6353eb526d security: fix unbounded sprintf in check_flatpak
Memory Safety: Medium

sprintf was used to format a /proc path without bounds checking.
While pid_t values are practically bounded, using snprintf with
sizeof(root_path) ensures the buffer cannot overflow regardless
of the input value, following defense-in-depth principles.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-23 16:24:46 +02:00
Wim Taymans
e490c503fd pulse-server: update initial stream is_paused state
When the stream starts corked, we set the INACTIVE flag and we also need
to set the stream state as PAUSED or else uncork will not unpause
anything.
2026-04-15 18:25:28 +02:00
Wim Taymans
823dcd8843 scheduler: make nodes move to IDLE when inactive
When a node is inactive but linked to a driver, the only reason it is
not being scheduled is because it is inactive.

We already set up the links and negotiate the format and buffers to
prepare going to RUNNING. This patch now also make the node go to IDLE,
which makes the adapter negotiate a forma and buffers with the internal
node.

This makes things more symetrical, when linking a node, it becomes IDLE,
when activating it becomes RUNNABLE, when inactive it goes back to IDLE.
The switch to RUNNING will also be faster when things are already set up
in the IDLE state.

The main advantage is that it allows us to implement the startup of
corked streams in pulseaudio better. Before this patch we had to set the
stream to active to make it go through the Format and buffer negotiation
and then quickly set it back to inactive, hopefully without skipping a
cycle. After this patch, the corked stream goes all the way to IDLE,
where it then waits to become active.

See #4991
2026-04-14 14:28:29 +02:00
Wim Taymans
0e0c325194 fix some uninitialized variables warnings 2026-04-08 11:29:36 +02:00
Jonas Holmberg
f4e174870e module-protocol-native: Fix socket activation
Fix path comparison in is_socket_unix() and don't unset LISTEN_FDS since
the function that uses it is called more than once and it was not unset
when sd_listen_fds() was used.

Fixes #5140
2026-03-02 10:28:26 +01:00
Wim Taymans
dee2d5ee06 zeroconf: sanitize the properties
Use some constants for the zeroconf properties. Make the right ones are
used in all places.
2026-02-27 17:31:42 +01:00
Wim Taymans
a1db2b8d35 pulse-server: port zeroconf publish to helper 2026-02-27 12:22:52 +01:00
Wim Taymans
fa04146cfb modules: move zeroconf code to zeroconf-utils 2026-02-26 17:09:45 +01:00