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>
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>
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>
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>
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>
Add MAX_RECV_PACKETS limit to both sync_wait functions to prevent
busy-spinning on the real-time thread under a packet flood, where
SO_RCVTIMEO never fires because data is always available.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Memory Safety: Medium
The parse_permissions_struct macro in protocol-native uses alloca()
to allocate space for permissions received from protocol messages.
With MAX_PERMISSIONS=4096 and sizeof(struct pw_permission)=8, this
could allocate up to 32KB on the stack from a single message. Combined
with parse_dict (up to 16KB), a crafted message could consume ~48KB
of stack space.
Reduce MAX_PERMISSIONS from 4096 to 1024 (matching MAX_DICT) to limit
the maximum stack allocation to 8KB. This is still more than sufficient
for any legitimate permission update - typical systems have far fewer
than 1024 objects that need individual permission entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Input Validation: High
The netjack2_recv_data loop terminates based on the is_last flag
from received network packets. A malicious peer could continuously
send packets with is_last=0, causing the receive loop to run
indefinitely and blocking the audio processing thread. This is
a denial of service vulnerability.
Add a maximum packet count (1024) per receive cycle. This is
well above what any legitimate netjack2 session would produce
but prevents a malicious peer from stalling the processing thread.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Memory Safety: Critical
write_event() returns a negative int on error (-ENOSPC or -ERANGE),
but its return value was added directly to the uint32_t len variable
without checking. A negative return value would wrap len to a very
large number due to unsigned integer conversion, causing subsequent
buffer writes to go far out of bounds. This could lead to stack
corruption and potential code execution.
Fix by checking the return value of write_event() before using it.
If write_event() fails, abort the flush operation safely.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
netjack2_driver_sync_wait can return an negative value on error, don't
do any processing in that case instead of wrapping around the negative
value into a huge unsigned int and breaking things..
Clamp sync.frames to quantum_limit in both sync_wait functions so all
recv paths (float, int, opus, and the fallback memset in recv_data) use
a bounded frame count. A malicious remote could send a large sync.frames
causing buffer overflows in recv_int, recv_opus, and the unfilled-buffer
memset.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Fix handle_follower_available to properly clean up on all error paths
after the follower has been added to the list. Add missing NULL checks
for pw_properties_copy and check the netjack2_init return value.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a configurable netjack2.max-followers property (default 64) to
limit the number of concurrent followers, preventing resource exhaustion
from unbounded follower connections.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Cast the denominator to uint64_t to prevent sample_rate * 8 from
overflowing uint32_t, which could produce a tiny denominator and
an inflated max_encoded_size.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Validate that the encoded length from the network does not exceed
the available encoded data region before passing it to the opus
decoder.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Validate that the midi buffer metadata fits within the buffer size
before computing the offset, preventing a size_t underflow. Also
bounds-check non-inline event data pointers against the validated
buffer region.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The period calculation now subtracts the per-port int32_t overhead
from max_size before computing how many float samples fit. This guarantees
active_ports * (period * sizeof(float) + sizeof(int32_t)) <= max_size, so
packet_size = sizeof(header) + active_ports * sub_period_bytes <= mtu.
sub_cycle is bounded by nframes / sub_period_size, matching the sender's
num_packets = nframes / sub_period_size. Also ensure sub_period_size != 0
to avoid division by 0.
peer->params.name and peer->params.follower_name are null-terminated
by nj2_session_params_ntoh, whereas the raw params->name from the network
packet had no such guarantee.
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>
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
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>
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>