Detailed Description of the Problem
In ssl_sock_io_cb (src/ssl_sock.c), when a connection has conn_in_list=1 and CO_FL_SESS_IDLE set, and session_reinsert_idle_conn() fails (returns 0), the code calls CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx)) which frees the connection object, then sets t = NULL. However, execution falls through linearly to the leave: label block, which dereferences the now-freed conn via objt_server(conn->target) because ret is still 0 and conn_in_list is still 1.
if (conn->flags & CO_FL_SESS_IDLE) {
if (!session_reinsert_idle_conn(conn->owner, conn)) {
CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx)); // conn freed
t = NULL;
// no goto, falls through
}
}
// ...
leave:
if (!ret && conn_in_list) {
struct server *srv = objt_server(conn->target); // UAF: conn dereferenced
Expected Behavior
After destroy() is called on a connection, no further dereferences of that connection object should occur. Either conn_in_list should be set to 0 before calling destroy, or execution should jump past the leave: block entirely.
Steps to Reproduce the Behavior
Trigger conditions:
- A backend connection with CO_FL_SESS_IDLE flag set is being returned to the session idle list
- ssl_sock_io_cb is invoked with TASK_F_USR1 set (conn_in_list becomes 1)
- session_reinsert_idle_conn() fails due to sess_alloc_sess_conns() returning NULL under memory pressure
- destroy() is called, freeing conn
- The leave: block executes and dereferences freed conn->target
Do you have any idea what may have caused this?
session_reinsert_idle_conn() can fail if sess_alloc_sess_conns() fails internally. On that failure path, destroy() is called but conn_in_list is never cleared and there is no goto to skip the post-leave re-insertion block, causing the UAF.
Do you have an idea how to solve the issue?
// Option A: clear conn_in_list before destroy
conn_in_list = 0;
CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx));
t = NULL;
or
// Option B: skip leave block after destroy
CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx));
t = NULL;
goto done; // jump past leave: block
What is your configuration?
N/A — this is a code-level bug reproducible independent of configuration.
Output of haproxy -vv
N/A — identified via static analysis of src/ssl_sock.c.
Last Outputs and Backtraces
Additional Information
CWE-416 (Use-After-Free), estimated high severity
Affected function: ssl_sock_io_cb, specifically the conn_in_list cleanup path at the leave: label
The UAF dereference is deterministic on the failure path, not a race condition
Detailed Description of the Problem
In ssl_sock_io_cb (src/ssl_sock.c), when a connection has conn_in_list=1 and CO_FL_SESS_IDLE set, and session_reinsert_idle_conn() fails (returns 0), the code calls CALL_MUX_NO_RET(conn->mux, destroy(conn->ctx)) which frees the connection object, then sets t = NULL. However, execution falls through linearly to the leave: label block, which dereferences the now-freed conn via objt_server(conn->target) because ret is still 0 and conn_in_list is still 1.
Expected Behavior
After destroy() is called on a connection, no further dereferences of that connection object should occur. Either conn_in_list should be set to 0 before calling destroy, or execution should jump past the leave: block entirely.
Steps to Reproduce the Behavior
Trigger conditions:
Do you have any idea what may have caused this?
session_reinsert_idle_conn() can fail if sess_alloc_sess_conns() fails internally. On that failure path, destroy() is called but conn_in_list is never cleared and there is no goto to skip the post-leave re-insertion block, causing the UAF.
Do you have an idea how to solve the issue?
or
What is your configuration?
Output of
haproxy -vvLast Outputs and Backtraces
Additional Information
CWE-416 (Use-After-Free), estimated high severity
Affected function: ssl_sock_io_cb, specifically the conn_in_list cleanup path at the leave: label
The UAF dereference is deterministic on the failure path, not a race condition