Skip to content

negotiate_in_use reentrancy flag not cleared on early return error paths #5796

@cpsource

Description

@cpsource

Summary

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.

int s2n_negotiate(struct s2n_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: set

    uint64_t negotiate_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 reached
    return result;
}

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:

conn->negotiate_in_use = true;
DEFER_CLEANUP(bool *flag = &conn->negotiate_in_use, s2n_cleanup_bool_flag);

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.

Found during code review.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions