Add nice_agent_clear_stream_local_addresses and fix use-after-free of selected_pair.local during re-gather#18
Conversation
|
@copilot does libnice allow adding additional local_addresses while its running, and will a new entry be picked up immediately, and new clients gathered? |
You can add them at any time, but they are not picked up automatically — they only take effect on the next Looking at
So in practice:
The typical pattern with the new nice_agent_clear_stream_local_addresses(agent, stream_id);
nice_agent_add_stream_local_address(agent, stream_id, &new_addr);
nice_agent_gather_candidates(agent, stream_id);If you want "added → immediately gathered" without an explicit re-gather call, that behaviour doesn't exist in libnice today and would need a new code path. |
|
@copilot In pulse_network_monitor.c, I've spotted a bug. We get events from glib_network_monitor, and act on them. But, we seems to get an IPv4 address first, then if there is a IPv6 dhcp, it will take a few rounds before it finally has the "final" IPv6 address. |
That file isn't part of libnice — That said, here's the shape of what you're describing in case it's useful when you raise it in the right repo:
If you can point me at the correct repo/PR I'm happy to do the implementation there. |
Agent-Logs-Url: https://github.com/pexip/libnice/sessions/285af649-49e4-46aa-b4da-604a0fdfd6bf Co-authored-by: bitflows <19825042+bitflows@users.noreply.github.com>
703dcd1 to
e228324
Compare
|
@copilot Can you see what it would take to implement a cancel stream function, that stops the waiting for any STUN/TURN servers? We need to be able to perform either ice restart (possibly with a new candidate gather and a reconfigure) or a full stream remove once completed. The idea here is to speed up ICE restart, so in case a libnice gathering or negotiation in ongoing, and we need to reconfigure the parameters due to an additional TURN server, or change the local_addresses, due to a link change. Separately from thjis, could we possibly add a new TURN server WHILE the gathering/negotiation is in progress? |
Good news: most of the machinery for a "cancel stream" already exists internally — it's mainly a matter of exposing it and wiring it together. 1. Cancel/stop waiting for STUN/TURN on a stream Gathering is driven by
So a public For the ICE-restart speed-up you describe: today 2. Adding a TURN server while gathering/negotiation is in progress
Two ways to get "added → gathered immediately":
If you'd like, point me at where the cancel function should live (here in libnice, given the prune helpers are all internal) and I can implement |
|
@copilot Summary The next nice_agent_send() (agent/agent.c) reads component->selected_pair.local->sockptr out of that freed candidate and calls nice_socket_send() with a garbage NiceSocket *, crashing with SIGSEGV: Code Trigger / reproduction C Required change If candidate == component->selected_pair.local, tear down the selected pair the same way the existing teardown code does: destroy/unref component->selected_pair.keepalive.tick_source if set, then memset(&component->selected_pair, 0, sizeof(CandidatePair)) (see component_update_selected_pair() and component_free() in agent/component.c for the canonical teardown). |
Exposes a way to drop the configured local addresses on a stream so callers can replace them, complementing the existing
nice_agent_add_stream_local_address.agent/agent.h: declarenice_agent_clear_stream_local_addresses(NiceAgent *, guint stream_id).agent/agent.c: implementation takes the agent lock, looks up the stream, frees eachNiceAddressinstream->local_addresses, frees the list, and nulls it out — mirroring the cleanup already performed instream_free(). ReturnsFALSEfor an invalidstream_id.nice/libnice.sym: export the new symbol.No tests added: the existing suite has no coverage of the per-stream local-address API (only the agent-wide
nice_agent_add_local_addressis used intests/), so integration coverage is expected to live in the consuming layer.Fix use-after-free of
component->selected_pair.localduring re-gatherWhen the local interface set changes and
nice_agent_gather_candidates()is called again (e.g. during an ICE restart/reconfigure triggered by a network change), libnice prunes local candidates that live on interfaces that no longer exist and frees them. The prune path never checked whether the freed candidate was the one referenced bycomponent->selected_pair.local, leaving that pointer dangling. The nextnice_agent_send()would readselected_pair.local->sockptrout of freed memory and callnice_socket_send()with a garbageNiceSocket *, crashing with SIGSEGV.agent/component.c/agent/component.h: addcomponent_invalidate_selected_pair_if_local(). If the component's selected local candidate matches the one being freed, it destroys/unrefsselected_pair.keepalive.tick_source(avoiding a separate timer-based leak/UAF) andmemsetsselected_pairto zero — the canonical teardown already used bycomponent_update_selected_pair()andcomponent_free().agent/agent.c: call the helper from the prune loop innice_agent_gather_candidates(), immediately beforenice_candidate_free(candidate). Withselected_pair.local == NULL,nice_agent_send()already no-ops, so sends are safely dropped until ICE re-selects a pair.No in-tree regression test was added: the meson test harness only wires standalone tests with no libnice-internal dependency, and faithfully reproducing this UAF requires a full two-agent negotiation with a controllable interface set, so end-to-end coverage is expected to live in the consuming layer (
rtp_ice_restart_and_reconfigure_changing_local_addresses).agent.candcomponent.cwere verified to compile cleanly.