Repository navigation
Conversation
…om the pool An address is only a name for a connection, and names get reused. A peer that reconnects from the same source port produces a successor that Add files under a key its predecessor still holds, and the successor wins it -- which is the intended outcome, since the newer connection is the reachable one. The predecessor's reader then exits, as it must, and deletes that key by name. The name now belongs to the successor, so the delete evicts a connection the dying reader has nothing to do with. The successor stays open and stays read; only the pool forgets it, so nothing re-adds it, because nothing knows it is gone. Every subsequent message routed to that address dials a second connection or fails outright. This is easiest to hit with SIP over TCP, where a UA re-registering from the same source port is routine, and it is worst there too: the orphaned connection is the one the far end is still listening on, so requests sent the other way are lost. Delete an entry only while it still names the connection being removed. The pool type is package-private, so taking the connection as a parameter on Delete and DeleteMultiple changes no exported API; every caller is a reader-exit path that already has it in hand. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
emiago
added a commit
that referenced
this pull request
Oct 1, 2026
Check connection identity under the pool lock before reader cleanup removes address entries. Cover stream and UDP successor preservation and ordinary cleanup with regression tests. Refs #372
Owner
|
thanks for reporting, fixed on main |
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.
The bug
connectionPool.DeleteandCloseAndDeleteremove an entry by address alone, withoutchecking that the entry still names the connection being removed:
An address is only a name for a connection, and names get reused. A peer that reconnects
from the same source port produces a successor that
Addfiles under a key its predecessorstill holds, and the successor wins it — which is the intended outcome, since the newer
connection is the reachable one.
Add's ownTODO how about multi connection support for same remote addressis about this situation.The predecessor's reader goroutine then exits, as it must, and runs its cleanup:
Both names now belong to the successor, so the dying reader evicts a connection it has
nothing to do with. The successor stays open and stays read — only the pool forgets it, so
nothing re-adds it, because nothing knows it is gone. Every subsequent message routed to
that address either dials a second connection or fails outright.
It is easiest to hit with SIP over TCP, where a UA re-registering from the same source port
is routine, and it is worst there too: the orphaned connection is the one the far end is
still listening on, so requests sent the other way are lost. We found it in a P-CSCF, where
TS 33.203 makes the re-registration case unavoidable — the UE keeps
port_usacross are-registration, so every refresh produces exactly this predecessor/successor pair on a
shared remote key.
The fix
Delete an entry only while it still names the connection being removed.
connectionPoolis package-private, so taking the connection as a parameter onDeleteandDeleteMultiplechanges no exported API. Every caller is a reader-exit path that alreadyhas the connection in hand (
transport_tcp.go,transport_ws.go,transport_udp.go), soeach was a one-word change.
Tests
Two new tests in
sip/transport_connection_pool_successor_test.go, verified by mutationrather than by coverage:
deleteIfCurrentdeletes unconditionally (i.e. the bug restored)TestAPredecessorDoesNotEvictItsSuccessorFAIL, control PASSdeleteIfCurrentnever deletes (i.e. a "fix" that leaks)TestASoleConnectionIsStillDeletedFAIL, other PASSThe second exists so the first cannot be satisfied by a change that simply stops deleting,
which would turn the pool into a leak.
go test ./sip/is green.go test ./...fails to build the root package onpattern testdata/certs/client.crt: no matching files found, which is identical before andafter this change.
gofmt -lreportssip/request.go, also pre-existing and untouched here.