Memory Safety: Medium
In pw_split_strv(), the return values of strndup() and strdup() were
passed directly to pw_array_add_ptr() without checking for NULL. If
memory allocation fails, NULL pointers would be stored in the string
array and later dereferenced by callers iterating the result.
The return value of pw_array_add_ptr() was also not checked, which
could lead to silently dropped strings.
Fix by checking both allocation and array insertion return values,
and properly cleaning up all previously allocated strings on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Safety: Medium
In the registry event handler, strdup(type) was not checked for
failure. A NULL o->type would cause NULL pointer dereferences in
subsequent code that uses the type string for comparison and logging.
Fix by checking the strdup() return value and cleaning up on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Safety: Medium
In pw_context_set_spa_libs(), strdup(lib) was not checked for failure.
A NULL entry->lib would cause a NULL dereference when the factory
library path is later looked up and used for dlopen().
Fix by checking the strdup() return value and cleaning up the regex
and array entry on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Safety: Medium
In pw_data_loop construction, strdup() calls for the thread affinity
and class strings were not checked for failure. A failed strdup()
would store NULL, leading to NULL pointer dereferences when these
strings are later used for thread configuration.
Fix by checking strdup() return values and failing initialization
with -ENOMEM on allocation failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Safety: Medium
The set_item() function called strdup() for key, type, and value
without checking the return values. If any strdup() fails due to
memory exhaustion, the NULL pointer would be stored in the item
struct and later dereferenced when the metadata is accessed or
logged.
Fix by checking strdup() return values and cleaning up on failure.
Change set_item() to return an error code so callers can handle
allocation failures.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
If the filter process doesn't dequeue/queue a buffer (as can be the
case in jack-tunnel-sink under xrun cases), pw-filter will set the
io to NEED_DATA with ID_INVALID.
This will then make the mixer in the next cycle not recycle any buffers
and it won't be able to produce any new ones either.
If the filter the dequeues/queues a buffer in the next process, it won't
dequeue a buffer for recycle because io is NEED_DATA/INVALID from the
previous cycle (io != HAVE_DATA -> continue).
This will the continue in an infinite loop producing "out of buffers"
forever.
Also check that we actually have a buffer to recycle, if we don't we can
try to dequeue one and place that in the io. This will then unlock the
loop, make the mixer recycle the buffer and produce a new one.
This is the same logic as is present in pw-stream for the same reason.
Fixes#5246
Maybe also #3547
Memory Safety: High
Three places in pw-cli allocated param info arrays using
malloc(n_params * sizeof(struct spa_param_info)) where n_params
comes from remote protocol data. The multiplication can overflow,
causing a small buffer to be allocated while n_params remains large.
Later code iterating over n_params entries would read past the
allocated buffer.
Fixed by using calloc(n_params, sizeof(...)) which internally checks
for multiplication overflow and returns NULL on failure. Also added
NULL checks and proper fallback when allocation fails.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Safety: Medium
The ring buffer allocation in the sendspin receiver module was not
checked for NULL. If calloc fails (e.g., due to a large stride value
from network-controlled audio format parameters), the code proceeds
to use the NULL pointer, causing a crash.
Also changed calloc(1, size*stride) to calloc(size, stride) so that
calloc itself checks for multiplication overflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
File and Resource Handling: Medium
Several file and socket operations were missing the close-on-exec flag,
which causes file descriptors to leak to child processes created via
fork+exec. This could allow child processes unintended access to
privileged resources.
- node-driver.c: SOCK_DGRAM socket for SIOCETHTOOL ioctl leaked to
child processes
- pw-container.c: Unix domain listen socket leaked to spawned
container processes
- compress-offload-api.c: ALSA compress-offload device fd leaked to
child processes
Added O_CLOEXEC to open() calls and SOCK_CLOEXEC to socket() calls.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Input Validation: Medium
The RAOP sink module used atoi() to parse port numbers from RTSP
Transport headers received over the network. atoi() does not validate
input and its int return was silently truncated to uint16_t, meaning
out-of-range or negative values could produce unexpected port numbers.
Replaced RTSP Transport header port parsing with strtoul() plus range
validation (1-65535). Replaced the raop.port property parsing with
spa_atou32() and range checking. Replaced raop.latency.ms parsing with
spa_atou32() for consistency with safe parsing patterns used elsewhere
in the codebase.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Input Validation: Medium
The RTP-SAP module used atoi() to parse rtp.rate, rtp.channels,
rtp.ssrc, and rtp.ts-offset properties into uint32_t fields. atoi()
returns int, which has undefined behavior on overflow and silently
converts negative values. When assigned to uint32_t, a negative result
wraps to a large value.
These properties can originate from received SDP announcements over the
network. Replaced with spa_atou32() which properly validates the input
and rejects non-numeric or out-of-range values. This is consistent with
how the same function already handles rtp.framecount using spa_atou32().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Input Validation: High
The WebSocket HTTP reply parser used atoi() to parse the Content-Length
header from network data. atoi() does not detect overflow or invalid
input, and its int return value was assigned to a size_t, meaning a
negative value from a malicious server would silently convert to a very
large unsigned value, potentially causing excessive memory allocation.
Replaced with spa_atou32() which validates the entire string is a valid
number and fits in uint32_t, plus an explicit upper bound (16 MB) on
content length to prevent resource exhaustion.
Similarly, pw_websocket_listen() used atoi() to parse the port number
into a uint16_t without validation. Replaced with spa_atou32() and a
range check against 65535.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only start receiving packets when we are streaming.
Otherwise the ROC source will start receiving and queueing packets and
consume a lot of memory while we don't read the packets from the queue.
Likewise, stop receiving packets when we pause.
Fixes#5250
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>
Memory Safety: Low
The strcpy() calls here operate on buffers that are correctly sized,
but using strcpy obscures the bounds guarantee and forces redundant
strlen() calls to compute pointer offsets. Replace with memcpy()
using the lengths already computed for the allocation, making the
bounds safety explicit and avoiding repeated string scanning.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Safety: Low
strcpy() into the fixed-size params.type[8] buffer has no bounds
checking. While the current literal string "params" fits exactly,
this pattern is fragile and would silently overflow if the string
were ever changed. Use snprintf() with sizeof() for bounds safety,
consistent with how params.name and params.follower_name are
handled on the lines immediately following.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Input Validation: Medium
atoi() on network-received data returns 0 on parse failure, which is
indistinguishable from a valid "0" input. It also accepts negative
values and does not detect overflow. Replace with strtol() and
validate that the status code is in the valid HTTP/RTSP range
(100-599) to prevent protocol state confusion from malformed
responses.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Safety: Medium
strdup() can return NULL on allocation failure. The return value was
used without checking, which would cause a NULL pointer dereference
(crash) when the name is later compared with spa_streq(). Add a NULL
check and free the partially-allocated struct on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Input Validation: High
system() passes its argument to /bin/sh -c, which interprets shell
metacharacters (;, |, &&, $(), etc.). If pw-container is invoked by
another program with untrusted input, this allows arbitrary command
execution. Replace with fork()+execvp() which executes the command
directly without shell interpretation, and passes all remaining
arguments to the child process.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a monitor mode that creates an Audio/Source combining audio from the
monitor ports of all Audio/Sink nodes. This allows capturing everything
that is being played back across all sinks into a single source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Input Validation / Memory Safety: Medium
The RTSP client used for RAOP/AirPlay communication accepted arbitrarily
large Content-Length values from the remote server without any upper
bound. A malicious or compromised AirPlay server could specify a very
large Content-Length, causing the client to allocate unbounded memory
and potentially exhaust system resources (denial of service).
Additionally, the return value of pw_array_add() was not checked. If
the allocation failed, the subsequent memcpy would dereference a NULL
pointer, causing a crash.
Add a 64KB limit on Content-Length (more than sufficient for RTSP
control messages) and check the pw_array_add return value.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>