From e66156eb49837c8e56678b13314230a686621f5d Mon Sep 17 00:00:00 2001 From: Evgeny Date: Thu, 2 Jul 2026 16:26:43 +0300 Subject: [PATCH] tcp_io: deferred free in tcp_conn_destroy to prevent UAF in callback chain --- lib/tcp_io.c | 13 ++++++++++--- lib/tcp_io.h | 1 + src/route_connectivity.c | 17 ++++++++++------- src/route_node.h | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/tcp_io.c b/lib/tcp_io.c index 5a7ea292..d22e7f74 100644 --- a/lib/tcp_io.c +++ b/lib/tcp_io.c @@ -104,8 +104,17 @@ struct tcp_conn* tcp_conn_create( return tc; } +static void tcp_conn_deferred_free(void* arg) { + struct tcp_conn* tc = (struct tcp_conn*)arg; + if (tc->entry_pool) memory_pool_destroy(tc->entry_pool); + if (tc->data_pool) memory_pool_destroy(tc->data_pool); + u_free(tc); +} + void tcp_conn_destroy(struct tcp_conn* tc) { if (!tc) return; + if (tc->destroyed) return; + tc->destroyed = 1; DEBUG_DEBUG(DEBUG_CATEGORY_SOCKET, "tcp_conn_destroy: fd=%d connected=%d error=%d fin_remote=%d fin_local=%d closed=%d", (int)tc->sock, tc->connected, tc->error, tc->fin_remote, tc->fin_local, tc->closed); @@ -132,9 +141,7 @@ void tcp_conn_destroy(struct tcp_conn* tc) { if (tc->write_buf) memory_pool_free(tc->data_pool, tc->write_buf); if (tc->sock != SOCKET_INVALID) { socket_close_wrapper(tc->sock); tc->sock = SOCKET_INVALID; } - memory_pool_destroy(tc->entry_pool); - memory_pool_destroy(tc->data_pool); - u_free(tc); + uasync_call_soon(tc->ua, tc, tcp_conn_deferred_free); } static void tcp_conn_handle_error(struct tcp_conn* tc, int err) diff --git a/lib/tcp_io.h b/lib/tcp_io.h index 8e8166f6..fc4bf2c0 100644 --- a/lib/tcp_io.h +++ b/lib/tcp_io.h @@ -46,6 +46,7 @@ struct tcp_conn { uint8_t fin_remote; // FIN получен от удалённой стороны (recv == 0) uint8_t fin_local; // FIN отправлен удалённой стороне (shutdown SHUT_WR) uint8_t closed; // сокет полностью закрыт (close) + uint8_t destroyed; // 1 = tcp_conn_destroy вызван, tc ожидает отложенного free // Частичная отправка (из data_pool, не в очереди — досылается первой) uint8_t* write_buf; diff --git a/src/route_connectivity.c b/src/route_connectivity.c index 66b540f8..e3b2e88b 100644 --- a/src/route_connectivity.c +++ b/src/route_connectivity.c @@ -20,6 +20,7 @@ #define CONN_MAX_SOCKET_CANDIDATES 8 struct conn_probe_ctx { + struct conn_probe_ctx* next; /* linked list in nq->connectivity.probe_list */ struct UTUN_INSTANCE* instance; struct NODEINFO_Q* nq; uint8_t addr_type; // ADDR_TYPE_* @@ -248,7 +249,8 @@ static void conn_probe_finish(struct conn_probe_ctx* ctx, int ok) { if (!ctx) return; if (ctx->nq) { struct NODE_CONNECTIVITY* c = &ctx->nq->connectivity; - if (c->probe_ctx == ctx) c->probe_ctx = NULL; + struct conn_probe_ctx** p = (struct conn_probe_ctx**)&c->probe_list; + while (*p) { if (*p == ctx) { *p = ctx->next; break; } p = &(*p)->next; } switch (ctx->addr_type) { case ADDR_TYPE_INTERFACE: @@ -352,7 +354,8 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct NODEIN ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535; - nq->connectivity.probe_ctx = ctx; + ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; + nq->connectivity.probe_list = ctx; pend++; conn_probe_start_series(ctx); } @@ -411,7 +414,8 @@ void route_connectivity_probe_node(struct UTUN_INSTANCE* instance, struct NODEIN ctx->timeout_ms = CONN_PROBE_TIMEOUT_MS; ctx->best_across_sockets = 65535; - nq->connectivity.probe_ctx = ctx; + ctx->next = (struct conn_probe_ctx*)nq->connectivity.probe_list; + nq->connectivity.probe_list = ctx; pend++; conn_probe_start_series(ctx); } @@ -433,10 +437,9 @@ void route_connectivity_cancel_node(struct UTUN_INSTANCE* instance, struct NODEI if (!instance || !nq) return; nq->connectivity.probe_status = PROBE_STATUS_NONE; nq->connectivity.pending_count = 0; - if (nq->connectivity.probe_ctx) { - ((struct conn_probe_ctx*)nq->connectivity.probe_ctx)->nq = NULL; - nq->connectivity.probe_ctx = NULL; - } + struct conn_probe_ctx* ctx = (struct conn_probe_ctx*)nq->connectivity.probe_list; + while (ctx) { ctx->nq = NULL; ctx = ctx->next; } + nq->connectivity.probe_list = NULL; DEBUG_INFO(DEBUG_CATEGORY_BGP, "connectivity probe cancelled for node %016llx", (unsigned long long)nq->node.node_id); } diff --git a/src/route_node.h b/src/route_node.h index a54f22ed..d5aa157f 100644 --- a/src/route_node.h +++ b/src/route_node.h @@ -56,7 +56,7 @@ struct NODE_CONNECTIVITY { uint64_t nat_probe_time; uint64_t real_probe_time; uint64_t ping_req_time; // время последнего полученного ping request от этого узла (0.1ms tb) - void* probe_ctx; // активный conn_probe_ctx (для cancel) + void* probe_list; // linked list of active conn_probe_ctx (для cancel) }; /**