Skip to content

tcp_trsp: fix lock-order-inversion deadlock in generate_transport_errors#557

Open
hecko wants to merge 1 commit into
masterfrom
hecko/tcp-trsp-generate-transport-errors-deadlock
Open

tcp_trsp: fix lock-order-inversion deadlock in generate_transport_errors#557
hecko wants to merge 1 commit into
masterfrom
hecko/tcp-trsp-generate-transport-errors-deadlock

Conversation

@hecko

@hecko hecko commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Bug

The TCP transport can deadlock the SIP worker threads under concurrent transport errors and sending.

When a TCP connection is torn down (on_read/on_write error, idle timeout, or parse error), tcp_trsp_socket::close() runs while holding sock_mut and calls generate_transport_errors(). For each still-queued outgoing message it calls trans_layer::transport_error(), which re-enters the transaction layer (process_rcvd_msg) and takes a transaction-bucket lock:

  • core/sip/tcp_trsp.cppclose()generate_transport_errors()transport_error()
  • core/sip/trans_layer.cpp:1026 transport_error()process_rcvd_msg()bucket->lock()

Meanwhile the session processor / retransmission path holds a transaction-bucket lock and calls tcp_trsp_socket::send(), which then takes sock_mut:

  • core/sip/trans_layer.cpp:2421/2575/2834 tr->msg->send(...) (under bucket lock) → tcp_trsp_socket::send()AmLock _l(sock_mut)

The two lock orders are inverted — sock_mut → bucket (tcp worker) vs bucket → sock_mut (session proc) — a classic lock-order inversion that can wedge the involved threads.

Fix

Release sock_mut at the start of generate_transport_errors(). This is safe because closed is already set before it runs, so send() will no longer touch send_q. To keep locking balanced across the paths that reach close() (which now unlocks the mutex internally), on_read()/on_write() acquire the mutex through a small AmControlledLock helper and relinquish ownership on those paths. No functional/behavioural change otherwise.

Verified that every close() call site holds sock_mut and the destructor does not call close(), so the single deliberate unlock is always balanced. Changed translation unit compiles cleanly.

Acknowledgement

Backport of the fix from yeti-switch/sems — commit d285e2a5 ("tcp_trsp: fix deadlock between session proc send() and tcp worker generate_transport_errors()") by Michael Furmur. sems-server's transport code is identical to the pre-fix version, so the same deadlock is present here.


Generated by Claude Code

When a TCP connection is torn down (on_read/on_write error, idle timeout,
parse error), tcp_trsp_socket::close() runs while holding sock_mut and calls
generate_transport_errors(). For every still-queued outgoing message that in
turn calls trans_layer::transport_error(), which re-enters the transaction
layer (process_rcvd_msg) and takes a transaction-bucket lock.

Meanwhile the session processor / transaction retransmission path holds a
transaction-bucket lock and calls tcp_trsp_socket::send(), which then takes
sock_mut. The two lock orders are inverted (sock_mut -> bucket vs
bucket -> sock_mut), so under concurrent TCP transport errors and sending
the daemon can deadlock, wedging the affected SIP worker threads.

Release sock_mut at the start of generate_transport_errors(). This is safe
because 'closed' is already set, so send() no longer touches send_q. To keep
lock/unlock balanced, on_read()/on_write() now hold the mutex via a small
AmControlledLock helper and give up ownership on the paths that reach close().

Backport of the fix from yeti-switch/sems (commit d285e2a5, Michael Furmur).
The transport code in sems-server is identical to the pre-fix version, so the
same deadlock is present here.
Copilot AI review requested due to automatic review settings July 13, 2026 03:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

2 participants