From 091249930984d21a729604be8a39dc01e1ae70b7 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Wed, 15 Jul 2026 12:46:37 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20segfault=20in=20callback=20chain=20itera?= =?UTF-8?q?tion=20=E2=80=94=20save=20next=20before=20invoke?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In etcp_conn_ready / etcp_on_up / etcp_on_down / etcp_connection_create / tcp_server_on_link, callback chains were iterated with: while (cbe) { cbe->fn(...); cbe = cbe->next; } If the callback removes itself from the chain (e.g. ca_ready_cb calls etcp_conn_remove_ready_cbk which u_free's the entry), cbe->next reads freed memory → SIGSEGV. Fixed by saving next pointer before invoking the callback: while (cbe) { n = cbe->next; cbe->fn(...); cbe = n; } --- src/etcp.c | 8 ++++---- src/etcp_connections.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/etcp.c b/src/etcp.c index 17ecdf80..6b1a3b2e 100644 --- a/src/etcp.c +++ b/src/etcp.c @@ -261,7 +261,7 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n // Вызываем callback для нового соединения если установлен if (instance) { struct etcp_cbk_entry* cbe = instance->new_conn_cbks; - while (cbe) { cbe->fn(etcp, cbe->arg); cbe = cbe->next; } + while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } } return etcp; @@ -271,13 +271,13 @@ 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); struct etcp_cbk_entry* cbe = etcp->up_cbks; - while (cbe) { cbe->fn(etcp, cbe->arg); cbe = cbe->next; } + while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } } 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); struct etcp_cbk_entry* cbe = etcp->down_cbks; - while (cbe) { cbe->fn(etcp, cbe->arg); cbe = cbe->next; } + while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } } @@ -541,7 +541,7 @@ void etcp_conn_ready(struct ETCP_CONN* conn) { etcp_metrics_start_timer(conn); // Вызываем callback если установлен - { struct etcp_cbk_entry* cbe = conn->ready_cbks; while (cbe) { cbe->fn(conn, cbe->arg); cbe = cbe->next; } } + { 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); } diff --git a/src/etcp_connections.c b/src/etcp_connections.c index 117ac270..ad95909c 100644 --- a/src/etcp_connections.c +++ b/src/etcp_connections.c @@ -41,8 +41,8 @@ static void tcp_server_on_link(struct stcp_link *link, void *arg) { inst->connections = conn; inst->connections_count++; struct etcp_cbk_entry* cbe = inst->new_conn_cbks; - while (cbe) { cbe->fn(conn, cbe->arg); cbe = cbe->next; } - { struct etcp_cbk_entry* rcb = conn->ready_cbks; while (rcb) { rcb->fn(conn, rcb->arg); rcb = rcb->next; } } + while (cbe) { struct etcp_cbk_entry* n = cbe->next; cbe->fn(conn, cbe->arg); cbe = n; } + { struct etcp_cbk_entry* rcb = conn->ready_cbks; while (rcb) { struct etcp_cbk_entry* n = rcb->next; rcb->fn(conn, rcb->arg); rcb = n; } } DEBUG_INFO(DEBUG_CATEGORY_ETCP, "TCP server new conn=%p total=%d", (void*)conn, inst->connections_count); }