tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

commit 1cf0377057a97d6d944dee153c2ca0dc1c7897de
parent 5dbb8ea475061fd15bfd76056fd354178f965381
Author: David Goulet <dgoulet@torproject.org>
Date:   Mon, 17 Nov 2025 12:35:56 -0500

Merge branch 'maint-0.4.8'

Diffstat:
Achanges/ticket41162 | 5+++++
Msrc/core/mainloop/connection.c | 2+-
Msrc/core/or/circuitlist.c | 6+++++-
Msrc/core/or/conflux.c | 38++++++++++++++++++++++++++++++++++----
Msrc/core/or/conflux.h | 2+-
Msrc/core/or/conflux_pool.c | 5++++-
Msrc/core/or/connection_edge.c | 4+++-
Msrc/core/or/relay.c | 27++++++++++-----------------
Msrc/core/or/relay.h | 2+-
9 files changed, 64 insertions(+), 27 deletions(-)

diff --git a/changes/ticket41162 b/changes/ticket41162 @@ -0,0 +1,5 @@ + o Major bugfixes (conflux, exit): + - When dequeuing out-of-order conflux cells, the circuit could be close in + between two dequeue which could lead to a mishandling of a NULL pointer. + Fixes bug 41162; bugfix on 0.4.8.4. + diff --git a/src/core/mainloop/connection.c b/src/core/mainloop/connection.c @@ -971,7 +971,7 @@ connection_free_,(connection_t *conn)) return; tor_assert(!connection_is_on_closeable_list(conn)); tor_assert(!connection_in_array(conn)); - if (BUG(conn->linked_conn)) { + if (conn->linked_conn) { conn->linked_conn->linked_conn = NULL; if (! conn->linked_conn->marked_for_close && conn->linked_conn->reading_from_linked_conn) diff --git a/src/core/or/circuitlist.c b/src/core/or/circuitlist.c @@ -2722,8 +2722,12 @@ circuits_handle_oom(size_t current_allocation) * outbuf due to a malicious destination holding off the read on us. */ if ((conn->type == CONN_TYPE_DIR && conn->linked_conn == NULL) || CONN_IS_EDGE(conn)) { - if (!conn->marked_for_close) + if (!conn->marked_for_close) { + if (CONN_IS_EDGE(conn)) { + TO_EDGE_CONN(conn)->end_reason = END_STREAM_REASON_RESOURCELIMIT; + } connection_mark_for_close(conn); + } mem_recovered += single_conn_free_bytes(conn); if (conn->type == CONN_TYPE_DIR) { diff --git a/src/core/or/conflux.c b/src/core/or/conflux.c @@ -891,9 +891,11 @@ conflux_process_relay_msg(conflux_t *cfx, circuit_t *in_circ, * now be checked for remaining elements */ cfx->last_seq_delivered++; return true; - } else if (BUG(leg->last_seq_recv <= cfx->last_seq_delivered)) { - log_warn(LD_BUG, "Got a conflux cell with a sequence number " - "less than the last delivered. Closing circuit."); + } else if (leg->last_seq_recv <= cfx->last_seq_delivered) { + /* Anyone can mangle these sequence number. */ + log_fn(LOG_PROTOCOL_WARN, LD_BUG, + "Got a conflux cell with a sequence number " + "less than the last delivered. Closing circuit."); circuit_mark_for_close(in_circ, END_CIRC_REASON_INTERNAL); return false; } else { @@ -939,9 +941,37 @@ conflux_process_relay_msg(conflux_t *cfx, circuit_t *in_circ, * or has a hole. */ conflux_msg_t * -conflux_dequeue_relay_msg(conflux_t *cfx) +conflux_dequeue_relay_msg(circuit_t *circ) { conflux_msg_t *top = NULL; + /* Related to #41162. This is really a consequence of the C-tor maze. + * The function above can close a circuit without returning an error + * due to several return code ignored. Auditting all of the cell code + * path and fixing them to not ignore errors could bring many more + * issues as this behavior has been in tor forever. So do the bandaid + * fix of bailing if the circuit is closed. */ + if (circ->marked_for_close) { + static ratelim_t rlim = RATELIM_INIT(60 * 60); + log_fn_ratelim(&rlim, (circ->conflux == NULL) ? LOG_WARN : LOG_NOTICE, + LD_CIRC, + "Circuit was closed at %s:%u when dequeuing from OOO", + circ->marked_for_close_file, circ->marked_for_close); + return NULL; + } + conflux_t *cfx = circ->conflux; + if (cfx == NULL) { + static ratelim_t rlim = RATELIM_INIT(60 * 60); + log_fn_ratelim(&rlim, LOG_WARN, LD_CIRC, + "Bug: Non marked for close circuit with NULL conflux"); + return NULL; + } + if (cfx->ooo_q == NULL) { + static ratelim_t rlim = RATELIM_INIT(60 * 60); + log_fn_ratelim(&rlim, LOG_WARN, LD_CIRC, + "Bug: Non marked for close circuit with NULL OOO queue"); + return NULL; + } + if (smartlist_len(cfx->ooo_q) == 0) return NULL; diff --git a/src/core/or/conflux.h b/src/core/or/conflux.h @@ -60,7 +60,7 @@ bool conflux_should_multiplex(int relay_command); bool conflux_process_relay_msg(conflux_t *cfx, circuit_t *in_circ, crypt_path_t *layer_hint, const relay_msg_t *msg); -conflux_msg_t *conflux_dequeue_relay_msg(conflux_t *cfx); +conflux_msg_t *conflux_dequeue_relay_msg(circuit_t *circ); void conflux_note_cell_sent(conflux_t *cfx, circuit_t *circ, uint8_t relay_command); void conflux_relay_msg_free_(conflux_msg_t *msg); diff --git a/src/core/or/conflux_pool.c b/src/core/or/conflux_pool.c @@ -2127,7 +2127,10 @@ conflux_pool_init(void) void conflux_log_set(int loglevel, const conflux_t *cfx, bool is_client) { - tor_assert(cfx); + /* This could be called on a closed circuit. */ + if (cfx == NULL) { + return; + } log_fn(loglevel, LD_BUG, diff --git a/src/core/or/connection_edge.c b/src/core/or/connection_edge.c @@ -1080,7 +1080,9 @@ static mainloop_event_t *attach_pending_entry_connections_ev = NULL; static void connection_edge_about_to_close(edge_connection_t *edge_conn) { - if (!edge_conn->edge_has_sent_end) { + /* Under memory pressure, the OOM handler can close connections without + * sending END cell. If we are NOT in that scenario, log loudly. */ + if (!edge_conn->edge_has_sent_end && !have_been_under_memory_pressure()) { connection_t *conn = TO_CONN(edge_conn); log_warn(LD_BUG, "(Harmless.) Edge connection (marked at %s:%d) " "hasn't sent end yet?", diff --git a/src/core/or/relay.c b/src/core/or/relay.c @@ -589,17 +589,13 @@ relay_send_command_from_edge_,(streamid_t stream_id, circuit_t *orig_circ, circ = conflux_decide_circ_for_send(orig_circ->conflux, orig_circ, relay_command); if (!circ) { - log_warn(LD_BUG, "No circuit to send for conflux for relay command %d, " - "called from %s:%d", relay_command, filename, lineno); - conflux_log_set(LOG_WARN, orig_circ->conflux, - CIRCUIT_IS_ORIGIN(orig_circ)); - circ = orig_circ; - } else { - /* Conflux circuits always send multiplexed relay commands to - * to the last hop. (Non-multiplexed commands go on their - * original circuit and hop). */ - cpath_layer = conflux_get_destination_hop(circ); + /* Something is wrong with the conflux set. We are done. */ + return -1; } + /* Conflux circuits always send multiplexed relay commands to + * to the last hop. (Non-multiplexed commands go on their + * original circuit and hop). */ + cpath_layer = conflux_get_destination_hop(circ); } /* This is possible because we have protocol error paths when deciding the @@ -2118,7 +2114,7 @@ connection_edge_process_relay_cell(const relay_msg_t *msg, circuit_t *circ, } /* Now, check queue for more */ - while ((c_msg = conflux_dequeue_relay_msg(circ->conflux))) { + while ((c_msg = conflux_dequeue_relay_msg(circ))) { conn = relay_lookup_conn(circ, c_msg->msg, CELL_DIRECTION_OUT, layer_hint); ret = connection_edge_process_ordered_relay_cell(c_msg->msg, circ, @@ -2930,14 +2926,11 @@ cell_queues_check_size(void) /** Return true if we've been under memory pressure in the last * MEMORY_PRESSURE_INTERVAL seconds. */ -int +bool have_been_under_memory_pressure(void) { - if (last_time_under_memory_pressure == 0) { - return false; - } - return last_time_under_memory_pressure + MEMORY_PRESSURE_INTERVAL - < approx_time(); + return approx_time() < + last_time_under_memory_pressure + MEMORY_PRESSURE_INTERVAL; } /** diff --git a/src/core/or/relay.h b/src/core/or/relay.h @@ -67,7 +67,7 @@ extern uint64_t oom_stats_n_bytes_removed_hsdir; void dump_cell_pool_usage(int severity); size_t packed_cell_mem_cost(void); -int have_been_under_memory_pressure(void); +bool have_been_under_memory_pressure(void); /* For channeltls.c */ void packed_cell_free_(packed_cell_t *cell);