From f669254da35204ba3611cbdc25f255e8412419df Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 15 Jul 2026 14:43:47 +0300 Subject: [PATCH] feat: hard crash on etcp_connection_close from callback chain Added callbacks_running flag to ETCP_CONN: - set to 1 before up_cbks / down_cbks / ready_cbks iteration - reset to 0 after iteration completes - etcp_connection_close checks flag and does intentional SIGSEGV with FATAL log message if called during callback chain This catches illegal synchronous close from within callbacks instead of producing cryptic UAF segfaults later. --- src/etcp.c | 13 +++++++++++++ src/etcp.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/etcp.c b/src/etcp.c index 6b1a3b2e..b31d82be 100644 --- a/src/etcp.c +++ b/src/etcp.c @@ -216,6 +216,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n etcp->initialized=0; etcp->links_up=0; etcp->reset_done=0; + etcp->callbacks_running=0; etcp->last_rr_link=NULL; etcp->name = u_strdup(name); // Initialize log_name with local node_id (peer will be updated later when known) @@ -270,14 +271,18 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n static void etcp_on_up(struct ETCP_CONN* etcp) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "[%s] UP links_up=%d initialized=%d", etcp->log_name, etcp->links_up, etcp->initialized); + etcp->callbacks_running = 1; struct etcp_cbk_entry* cbe = etcp->up_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } + etcp->callbacks_running = 0; } static void etcp_on_down(struct ETCP_CONN* etcp) { DEBUG_WARN(DEBUG_CATEGORY_BGP, "[%s] DOWN links_up=%d", etcp->log_name, etcp->links_up); + etcp->callbacks_running = 1; struct etcp_cbk_entry* cbe = etcp->down_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } + etcp->callbacks_running = 0; } @@ -286,6 +291,12 @@ void etcp_connection_close(struct ETCP_CONN* etcp) { DEBUG_TRACE(DEBUG_CATEGORY_ETCP, ""); if (!etcp) return; + if (etcp->callbacks_running) { + DEBUG_ERROR(DEBUG_CATEGORY_DEBUG, "[%s] FATAL: etcp_connection_close called from inside callback chain — SEGFAULTING to show backtrace", + etcp->log_name); + *(volatile int*)0 = 0; + } + if (etcp->links_up!=0) { etcp->links_up=0; etcp_on_down(etcp); @@ -541,8 +552,10 @@ void etcp_conn_ready(struct ETCP_CONN* conn) { etcp_metrics_start_timer(conn); // Вызываем callback если установлен + conn->callbacks_running = 1; { struct etcp_cbk_entry* cbe = conn->ready_cbks; while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } } if (conn->links_up) etcp_on_up(conn); + conn->callbacks_running = 0; } diff --git a/src/etcp.h b/src/etcp.h index de6ca971..0b3b7faa 100644 --- a/src/etcp.h +++ b/src/etcp.h @@ -226,6 +226,7 @@ struct ETCP_CONN { uint8_t tx_state; // 0 - n/a, 1 - data_wait (queues empty), 2 - link_wait (link busy) uint8_t links_up; // 0 - канал не готов для передачи, 1 - канал готов для передачи (хотя бы один линк не down) uint8_t reset_done; // 0 - рукопожатие не завершено (реинит разрешён), 1 - соединение стабильно (реинит заблокирован) + uint8_t callbacks_running; // 1 - внутри итерации колбэк-цепочек, etcp_connection_close запрещён // Callback chains for ready/up/down notifications struct etcp_cbk_entry* ready_cbks; // цепочка callback'ов при готовности соединения