You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In tls/s2n_handshake_io.c lines 1714-1746, the negotiate_in_use reentrancy flag is set at line 1718 but only cleared at line 1744. Multiple early-return error paths between these lines skip the cleanup, permanently deadlocking the connection.
ints2n_negotiate(structs2n_connection*conn, s2n_blocked_status*blocked)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE(!conn->negotiate_in_use, S2N_ERR_REENTRANCY);
conn->negotiate_in_use= true; // line 1718: setuint64_tnegotiate_start=0;
POSIX_GUARD(s2n_default_monotonic_clock(NULL, &negotiate_start)); // line 1723: early return!// ... lines 1731-1741 have additional POSIX_GUARD calls that can early return ...conn->negotiate_in_use= false; // line 1744: never reachedreturnresult;
}
Early-return points that skip the flag reset:
Line 1723: POSIX_GUARD(s2n_default_monotonic_clock(...))
Line 1731: POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn))
Line 1732: POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn))
Line 1735: POSIX_GUARD(s2n_default_monotonic_clock(...))
Line 1740-1741: POSIX_GUARD_RESULT(s2n_event_handshake_populate/send(...))
After any of these fail, all subsequent s2n_negotiate() calls on the same connection will fail with S2N_ERR_REENTRANCY.
Suggested Fix
Use a cleanup pattern to ensure the flag is always cleared:
Or restructure so all POSIX_GUARD calls happen inside s2n_negotiate_impl(), before the flag is set.
Impact
Any transient failure in clock retrieval, buffer cleanup, or event reporting permanently deadlocks the connection — no further negotiation is possible without destroying and recreating it.
Git History
Commit 85649797c (Nov 2021, "Detect nested s2n_negotiate calls Detect nested s2n_negotiate calls #3119") introduced the negotiate_in_use flag with this same pattern. The original commit message notes it was modeled after the mechanism in s2n_send/s2n_recv from commit c8d04e461, which has the same class of bug.
Prior Art Search
Searched GitHub issues for: negotiate_in_use reentrancy, reentrancy flag error path, negotiate_in_use deadlock, s2n_negotiate reentrancy — no existing reports found.
Searched git log for reentrancy and negotiate_in_use — only found the original commit above.
Summary
In
tls/s2n_handshake_io.clines 1714-1746, thenegotiate_in_usereentrancy flag is set at line 1718 but only cleared at line 1744. Multiple early-return error paths between these lines skip the cleanup, permanently deadlocking the connection.Early-return points that skip the flag reset:
POSIX_GUARD(s2n_default_monotonic_clock(...))POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn))POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn))POSIX_GUARD(s2n_default_monotonic_clock(...))POSIX_GUARD_RESULT(s2n_event_handshake_populate/send(...))After any of these fail, all subsequent
s2n_negotiate()calls on the same connection will fail withS2N_ERR_REENTRANCY.Suggested Fix
Use a cleanup pattern to ensure the flag is always cleared:
Or restructure so all POSIX_GUARD calls happen inside
s2n_negotiate_impl(), before the flag is set.Impact
Any transient failure in clock retrieval, buffer cleanup, or event reporting permanently deadlocks the connection — no further negotiation is possible without destroying and recreating it.
Git History
85649797c(Nov 2021, "Detect nested s2n_negotiate calls Detect nested s2n_negotiate calls #3119") introduced thenegotiate_in_useflag with this same pattern. The original commit message notes it was modeled after the mechanism ins2n_send/s2n_recvfrom commitc8d04e461, which has the same class of bug.Prior Art Search
negotiate_in_use reentrancy,reentrancy flag error path,negotiate_in_use deadlock,s2n_negotiate reentrancy— no existing reports found.reentrancyandnegotiate_in_use— only found the original commit above.Found during code review.