Fix heap-use-after-free on UDP retransmission send error - #876
Merged
Conversation
Copilot
AI
changed the title
[WIP] Fix heap-use-after-free in call::run() under REGISTER + pause load
Guard Jun 30, 2026
call::run() retransmission path against use-after-free after fatal send errors
orgads
reviewed
Jun 30, 2026
orgads
requested changes
Jun 30, 2026
Copilot stopped work on behalf of
orgads due to an error
June 30, 2026 15:02
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
force-pushed
the
copilot/fix-heap-use-after-free
branch
from
July 2, 2026 15:49
edbc36a to
192f70e
Compare
call::run() retransmission path against use-after-free after fatal send errors
orgads
marked this pull request as ready for review
July 2, 2026 15:51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 callsdelete thiswhen 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 guardnever fired:
run()fell through tocall_scenario->messages[last_send_index]->nb_sent_retrans++, dereferencing the freed call. Under aREGISTER+ long load this reliably crashed with a heap-use-after-free / SIGSEGV in free() once asend failed while a call sat in the paused-task wheel.
Change the guard to
< 0so 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.