transport_start() creates update_delay_event before ensuring SCO I/O.
When spa_bt_transport_ensure_sco_io() fails, the fail path only cleans
up codec_data and leaks the event source on the loop.
Destroy update_delay_event in fail, matching do_remove_source().
handle_acp_poll() copies the poll revents into ACP and clears them, but
never actually looks at them. When one of the polled fds reports
POLLERR/POLLHUP - which happens when the ALSA card goes away, e.g. the
audio function of a USB dock being unplugged - the source is never
removed, and because that condition is level-triggered the loop just
keeps redispatching the handler.
The result is wireplumber pegged at 100% CPU, spinning on the card's
control fd:
read(24, ..., 72) = -1 ENODEV (No such device)
with fd 24 pointing at a now-deleted /dev/snd/controlC<n>. It stays like
that until the card comes back or the service is restarted.
Accumulate the returned mask and, if anything reports SPA_IO_ERR or
SPA_IO_HUP, drop the poll sources so we stop hammering a dead descriptor.
The udev monitor still drives the real device removal and setup_sources()
re-arms the poll if the card reappears - the same teardown alsa-pcm.c
already does for its own control sources.
Signed-off-by: Mike Lothian <mike@fireburn.co.uk>
Fix incorrect JSON iterator usage when parsing retransmissions and
max_transport_latency fields in BIS configuration.
The code incorrectly used it[2] iterator instead of it[1] when parsing
BIS object fields, causing undefined behavior and configuration values
being ignored.
Signed-off-by: liujiahe <liujiahe@uniontech.com>
These expected pointer to variables instead of variables directly, which
is inconsistent with how the other atomic macros behave. Furthermore,
SPA_STORE_ONCE was plain broken, since the underlying operation expects
an additional argument that was not included.
META_VideoCrop is present on every buffer negotiated through the
adapter, even when the producer never sets a meaningful crop
region. Before commit c634ef961, gst_buffer_get_video_crop_meta
returned NULL on new GstBuffers so the zero values were never
applied. After that commit, gst_buffer_add_video_crop_meta always
succeeds, and an invalid crop produces black frames.
Without this, the following produces black frames,
Producer:
gst-launch-1.0 videotestsrc is-live=true ! \
video/x-raw,format=I420,width=1280,height=720,framerate=24/1 ! \
pipewiresink mode=provide \
stream-properties="properties,media.class=Video/Source,media.role=Camera" \
client-name=VirtualCam
Consumer:
gst-launch-1.0 pipewiresrc path=<id> ! videoconvert ! autovideosink
A forced setup_filter_graphs() deactivates and re-instantiates every graph,
which frees and recreates the underlying plugin handles (node_cleanup sets
node->hndl[i] = NULL before re-instantiating). This was done on a graph that
was still referenced by the RT data-loop snapshot (filter_graph[]), so the
RT thread could run a graph whose handles were NULL mid-rebuild, leading to a
NULL handle dereference in the filter-graph process path.
Mirror the safe ordering already used by load_filter_graph()/clean_filter_handles():
before reconfiguring, mark the graphs not-setup and sync_filter_graph() so the
data loop drops them from filter_graph[] under the loop lock. They are
republished by the sync that follows setup. The cheap snapshot swap is done
under the lock; the heavy re-instantiation stays off the RT path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Memory Safety: Medium
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>
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>
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>
When a single output port is linked to multiple input ports of the same
client and the client uses multiple threads to process the input ports,
get_buffer_output() is called from multiple threads concurrently and
causes a race.
Multiple threads will try to dequeue a buffer concurrently and set the
HAVE_DATA io status, which causes the port to run out of buffers quickly
and the io are to become corrupted.
Use CAS to make sure only one thread dequeues and sets the io status.
The other concurrent threads will spin until there is a buffer. The fast
path will be that the buffer is already dequeued and then it is simply
reused.
Fixes#5324
rtp_stream_new() acquires a data loop with pw_context_acquire_loop() but
the out: error path never calls pw_context_release_loop(), leaking the loop
reference on every failure after acquisition.
Mirror rtp_stream_destroy() and other modules that pair acquire with release.
When adding the freeze/thaw recalc graph calls, triggering the actual
recalc of the graph (when an active node with a driver was removed) was
removed.
The thaw only actually recalcs the graph when something set the
recalc_pending flag so we still need something to set this. Normally
this would be because of some of the ports or links that got destroyed
but if not, the original recalc trigger needs to remain as a fallback.
Some operations like deactivating nodes deactivates all links to the
node and this causes a graph recalc for each link.
It's actually a bit more problematic with suspend, which first
deactivates the links and then suspends the ports. The suspension of the
ports cause a recalc, which for the not yet suspended ports makes the
link active again, causing issues then when the port is suspended later.
Make recalc a counter and only recalc when the counter is 0. If the
counter is not 0, set the pending recalc, which will trigger when the
counter goes to 0.
With this, we can add a freeze/thaw operation on the recalc and delay
recalculation until all ports and links are handled.
We can also group some other operations with a freeze/thaw pair, such as
the destruction of ports, which destroys all links. Also the destruction
of nodes can freeze the recalc until all ports are destroyed.
update_delay is called primarily when the stream format or latency changes,
and from playback thread, if stream reports different delay as before.
This function calculates the number of compensate samples for each stream
based on the latencies of other streams (which must be in a streaming state).
During the first playback on a new format, update_delay is called multiple times
due to format or latency changes. The delay is calculated only from streams
that are currently streaming. If some streams are not yet streaming, their
latencies are ignored, and the delay is updated later in the processing
thread. The processing thread also stores the stream delay in a local variable
(accessed only from that thread, thus requiring no locking).
On a subsequent playback using the same format, update_delay is still called a
few times, and the delay is updated based on the currently streaming streams.
If some streams are not streaming, their latencies are ignored.
However, this time, the processing thread fails to update the delay for the
previously non-streaming streams. Because the format didn't change, the streams
delay matches the last stored delay from the previous playback. As a
result, the compensate samples are not recalculated.
To properly update the compensate samples, update_delay must also be called
when a stream's state changes to streaming (avoiding the need to clear the
thread-buffered value, which would require locking in the processing
thread).
When jack state changes cause port properties to be updated, mark EnumRoute and
Route params as changed and call emit_info() so that the device re-emits the
route parameters with the updated port properties.
In multiple cases the `flags` member of `spa_dict` is left unitialized,
so try to avoid that. For example in `fill_node_info_proplist()` it is
accessed in `spa_dict_lookup_item()`.
This also modifies `collect_props()` to not depend on a partially initialized
`dict` parameter.
Only try to round down the period_size to the lowest power of 2 when we
are resampling and scaling the period_size to match.
In all other cases, we should honour the requested quantum duration.
Fixes#5302
Bring the port to INIT before going to CONFIGURE when we do the suspend
logic.
This is to ensure that there always is a state change to emit the state
change notification. This was, the link can detect the suspend case and
cancel any pending results.
One of the problem with suspend is that the state changes on the ports
are done from different places; the port and link. This causes issues
like:
1. do_negotiate calls pw_port_set_param(Format,..) with the negotiated
format. This returns async and the link queues a complete_ready
callback.
2. The node is suspended, pw_impl_port_set_param(Format, NULL) is called
to clear the port format. This bypasses the link.
3. The reply from step 2 arrives and triggers complete_ready, this
brings the port state to READY and the link state to ALLOCATING.
4. The link continues allocating and sets buffers on the port. This then
fails because the last format set was NULL.
Ideally all port states should be managed in one place and the async
port state changes should be kept in the port itself as well but this
will need some more work.
Fixes#3547
Thse should not be portal clients and can go directly to rtkit.
The reason is that there is a locking problem in the portal rt
implementation that cause timeouts for some reason.
When one of the nodes from a group is running, all the other nodes in the
group should run as well.
The link group is only for the session manager to avoid linking the same
link group to avoid loops, like linking the output of a loopback to the
input.
The problem is that we removed the node.link-group from driver nodes (in
ACP and elsewhere) but now the source/sink pairs (when in the same group
like for pro-audio) don't run together in all cases, causing sync issues.
See #5210
Keep the flag dont_inhibit_auto_suspend around and use it do decide when
to send suspend messages to the client.
We don't always want to send suspend messages when the stream state changes
because that could happen because the stream was, for example, relinked.
The intention of the suspend message is mostly for monitor streams that
use the dont-inhibit flag and want to follow the suspend state of the
sink.
See #5273
Instead of just setting the port state directly, go through the
port_update_state() function. This will make suspend_node() also
emit the port state change event to the links. This then causes
the link to reset to INIT, which cancels pending complete_ready work
items and prevents them from changing the port to READY without a
valid format.
One possible situation where this could happen is when a suspend
happens while the port was waiting for a set_param(Format) reply from
the client-node.
See #3547
Setting the volume control ports with a volume param also initializes
the controls. This ensures the controls are not restored to their
default value when the graph is activated.
Fixes#5192
When we are asked to add noise bits, don't call the clear function.
Make the passthrough and clear-on-empty flags available with a new flag
field to make it more extensible.
Fixes#5260
Make a new library.filter-path for the filter-graph that will filter and
restrict the dlopen filenames (used for the LADSPA plugin only).
By default this is false and so filter-chain can load from absolute
paths without extra checks.
Enable the extra checks for the pulse LADSPA modules and the
audioconvert filter graphs because these allow loading LADSPA plugins
into other processes.
Fixes#5222
If the "debug" log level is not enabled for the "spa.alsa" log topic,
then there is no point in going into the loop and splitting the data
into lines, so skip that.
Do not use `strcspn()` because it assumes a null terminated string,
but the `fopencookie()` write callback receives a (ptr, length) pair.
So use `memchr()` instead to find the `\n`.
The `fopencookie()` write callback should return the number of consumed
bytes, but it currently only ever returns 0, which signals an error
condition according to the documentation.
Fix that by not overwriting `size`.
Fixes: 73073eb33f ("alsa: redirect alsa output to log file")
When the graph rate changes it is possible that the follower node can
renegotiate to the new suggested audioconvert rate without requiring
resampling and so the extra check for the disabled resampler is not
required.
Fixes#4933