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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Three modules had "impl->capture_info.rate = !impl->playback_info.rate"
which evaluates to 0 (logical NOT of a non-zero rate) instead of
copying the playback rate. This is a copy-paste typo from the line
above which correctly uses "= impl->capture_info.rate".
Affects module-filter-chain, module-loopback, module-example-filter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
It's a terrible idea, doesn't work so well (locks up the data-loop when
read is blocked) and a security mightmare. If you really need to pipe
samples through some program, do that somewhere else, like from the
command line with pw-cat and pw-record.
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
When the search path is /usr/lib/, /usr/lib/foo.so fails to load because
there is no / after the search path. Fix this by requiring that either
the search path end with / or the following char is a /.
When we add a Format property after we dereffed all the other params in
the builder, we might relocate the builder memory and invalidate all
previously dereffed params, causing corruption.
Instead, first add all the params to the builder and then deref the
params.
There is a special case when we have both a capture and playback
stream. The capture stream will receive all filter params and the
playback stream will just receive its Format param.
Fixes#5202
If we pass a path /usr/libevil/mycode.so, it might have a prefix of
/usr/lib but we should still reject it. Do thi by checking that after
the prefix match, we start a new directory.
Check that the number of fds for the message does not exceed the number
of received fds with SCM_RIGHTS.
The check was simply doing an array bounds check. This could still lead
to out-of-sync fds or usage of uninitialized/invalid fds when the
message header claims more fds than there were passed with SCM_RIGHTS.
Found by Claude Code.
They are emited from the streaming thread and therefore can be emitted
concurrently with the events on the main thread. This can cause crashes
when the hook list is iterated.
Instead, make those events into callbacks that are more efficient,
and threadsafe.
Add a control.ump port property. When true, the port wants UMP and the
mixer will convert to it. When false, the port supports both UMP and
Midi1 and no conversions will happen. When unset, the mixer will always
convert UMP to midi1.
Remove the CONTROL_types property from the filter. This causes problems
because this is the format negotiated with peers, which might not
support the types but can still be linked because the mixer will
convert.
The control.ump port property is supposed to be a temporary fix until we
can negotiate the mixer ports properly with the CONTROL_types.
Remove UMP handling from bluetooth midi, just use the raw Midi1 events
now that the mixer will give those and we are supposed to output our
unconverted format.
Fix midi events in-place in netjack because we can.
Update docs and pw-mididump to note that we are back to midi1 as the
default format.
With this, most of the midi<->UMP conversion should be gone again and we
should be able to avoid conversion problems in ALSA and PipeWire.
Fixes#5183
Avoid doing conversions in the nodes between Midi formats, just assume
the imput is what we expect and output what we naturally produce.
For ALSA this means we produce and consume Midi1 or Midi2 depending on the
configurtation.
All of the other modules (ffado, RTP, netjack and VBAN) really only
produce and consume MIDI1.
Set the default MIDI format to MIDI1 in ALSA.
Whith this change, almost everything now produces and consumes MIDI1
again (previously the buffer format was forced to MIDI2).
The problem is that MIDI2 to and from MIDI1 conversion has problems in
some cases in PipeWire and ALSA and breaks compatibility with some
hardware.
The idea is to let elements produce their prefered format and that the
control mixer also negotiates and converts to the node prefered format.
There is then a mix of MIDI2 and MIDI1 on ports but with the control
port adapting, this should not be a problem.
There is one remaining problem to make this work, the port format is
taken from the node port and not the mixer port, which would then expose
the prefered format on the port and force negotiation to it with the
peer instead of in the mixer.
See #5183
Don't accept absolute library paths that are not in the search path,
skip the ../ in paths to avoid opening arbitrary libraries from
unexpected places.
When the driver changes, the clock position can also change and there
would be a discont in the rtp_timestamp.
This is not usually a problem except in RAOP mode where the base rtp
timestamp is negotiated and anything that deviates too much is to be
discarded.
If we are not using direct_timestamp for the sender, make sure we always
keep the rtp_time aligned to avoid this problem.
See #5167
Don't close an -1 fd in clear_data.
If we let the client allocate buffer, set our fd and data to invalid
values. If the client decides to renegotiate before we get the buffer
data we might otherwise try to clear the mem_id (default 0) or
close the fd (also default 0).
Fixes#5162
Sink/Source pairs should not have the same link-group otherwise the
session manager will not be able to autoconnect them with a loopback or
some other internally linked stream.
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
Roc-toolkit log records are captured via a callback and
written to PipeWire log with corresponding verbosity level.
The log.level config parameter limits record verbosity at
the roc-toolkit level.
Patch by Lairton Lelis da Fonseca Junior (@lairton)
Remove the hard skip for IPv4 link-local addresses and add an interface
binding (matching the existing IPv6 link-local behavior).
The host needs a link-local address on the interface (ip addr add
169.254.x.x/16 dev wlan0 or via NetworkManager +ipv4.addresses).
Fixes#4830
Socket activation uses sd_listen_fds from libsystemd, and can only be
compiled on systems with systemd.
This is an issue for Alpine / postmarketOS, where upstream has no
systemd package, but downstream depends on upstream's pipewire package
and wants to rely on socket activation. This also prevents using
socket-activation on other non-systemd distributions, including
non-Linux.
Implement equivalent functionality without a dependency on libsystemd.
This can easily be overlooked if the RTP rate equals the clock rate, which
is fairly common (for example, RTP rate and clock rate both being 48 kHz).
And, if an ASRC is active, and converting the output of the RTP source
node, the resampler's delay need to be taken into the account as well.
Clear the ringbuffer in stream_stop() when processing stops to prevent old invalid packets
from being sent when processing resumes via rtp_audio_flush_packets().
This ensures a clean state when the stream restarts.
Clearing the ring buffer is important not only in the direct timestamp
mode, but also in the constant delay mode, since missed packets can lead
to gaps in the ring buffer. These gaps may have stale data inside if the
ringbuffer is not cleared after reading from it.