Skip to content

Fix heap-use-after-free on UDP retransmission send error - #876

Merged
orgads merged 1 commit into
masterfrom
copilot/fix-heap-use-after-free
Jul 2, 2026
Merged

Fix heap-use-after-free on UDP retransmission send error#876
orgads merged 1 commit into
masterfrom
copilot/fix-heap-use-after-free

Conversation

Copilot AI commented Jun 30, 2026

Copy link
Copy Markdown

When a scheduled UDP retransmission failed to send, call::run() kept using the call object after it had already been freed.

send_raw() returns a negative value and calls delete this when the socket write fails (e.g. EBADF/EPIPE), matching the convention the normal send path already relies on (see the "call was already deleted by send_raw" branch). The retransmission path, however, guarded the result with < -1, but write errors return exactly -1, so the guard
never fired: run() fell through to call_scenario->messages[last_send_index]->nb_sent_retrans++, dereferencing the freed call. Under a REGISTER + long load this reliably crashed with a heap-use-after-free / SIGSEGV in free() once a
send failed while a call sat in the paused-task wheel.

Change the guard to < 0 so a send failure returns immediately, before any member of the deleted call is touched.

Add a regression test that associates a call with a closed socket, arms a retransmission whose deadline is in the past, and asserts run() returns false without incrementing nb_sent_retrans.

Fixes #875.

Copilot AI linked an issue Jun 30, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Fix heap-use-after-free in call::run() under REGISTER + pause load Guard call::run() retransmission path against use-after-free after fatal send errors Jun 30, 2026
Copilot AI requested a review from orgads June 30, 2026 12:19
Comment thread build/Testing/Temporary/CTestCostData.txt Outdated
When a scheduled UDP retransmission failed to send, call::run() kept
using the call object after it had already been freed.

send_raw() returns a negative value and calls "delete this" when the
socket write fails (e.g. EBADF/EPIPE), matching the convention the
normal send path already relies on (see the "call was already deleted
by send_raw" branch). The retransmission path, however, guarded the
result with "< -1", but write errors return exactly -1, so the guard
never fired: run() fell through to
"call_scenario->messages[last_send_index]->nb_sent_retrans++",
dereferencing the freed call. Under a REGISTER + long <pause> load this
reliably crashed with a heap-use-after-free / SIGSEGV in free() once a
send failed while a call sat in the paused-task wheel.

Change the guard to "< 0" so a send failure returns immediately,
before any member of the deleted call is touched.

Add a regression test that associates a call with a closed socket,
arms a retransmission whose deadline is in the past, and asserts run()
returns false without incrementing nb_sent_retrans.

Fixes #875.
@orgads
orgads force-pushed the copilot/fix-heap-use-after-free branch from edbc36a to 192f70e Compare July 2, 2026 15:49
@orgads orgads changed the title Guard call::run() retransmission path against use-after-free after fatal send errors Fix heap-use-after-free on UDP retransmission send error Jul 2, 2026
@orgads
orgads marked this pull request as ready for review July 2, 2026 15:51
@orgads
orgads merged commit 5dff0ec into master Jul 2, 2026
16 checks passed
@orgads
orgads deleted the copilot/fix-heap-use-after-free branch July 2, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

heap-use-after-free in call::run() under a REGISTER + pause load scenario

2 participants