Browse Source

fix: утечки памяти в router-сервисах и при shutdown

- conn_mgr_router_recv_handler / md_etcp_recv не освобождали entry (рантайм-утечка на каждое сообщение)
- reality_relay: registry + shutdown hook (отложенный free не отрабатывал при остановке)
- etcp_router: uasync_drain_immediate после destroy (router_close_finalize)
- мелкие callback-списки: activity_cbks, node_cbks, props_cbks/apply_cbks
proxy
evgeny 2 weeks ago
parent
commit
8a9d8d0097
  1. 9
      src/chat/member_sync.c
  2. 12
      src/media_delivery/media_delivery.c
  3. 9
      src/routing_layer/conn_mgr_core.c
  4. 12
      src/routing_layer/topo_group.c
  5. 22
      src/transport_layer/reality_relay.c
  6. 5
      src/transport_layer/reality_relay.h
  7. 11
      src/utun_instance.c

9
src/chat/member_sync.c

@ -941,6 +941,15 @@ void member_sync_destroy(struct UTUN_INSTANCE* inst) {
ge = ge->next;
}
}
/* освободить callback-списки (иначе утекут при chat_core_destroy) */
if (CC(inst)) {
struct ms_props_cbk* pc = CC(inst)->props_cbks;
while (pc) { struct ms_props_cbk* n = pc->next; u_free(pc); pc = n; }
CC(inst)->props_cbks = NULL;
struct ms_apply_cbk* ac = CC(inst)->apply_cbks;
while (ac) { struct ms_apply_cbk* n = ac->next; u_free(ac); ac = n; }
CC(inst)->apply_cbks = NULL;
}
merkle_sync_destroy(inst);
}

12
src/media_delivery/media_delivery.c

@ -1232,17 +1232,20 @@ static void md_on_conn_status(struct ETCP_CONN* conn, int status, void* arg) {
/* ── main dispatch ── */
static void md_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (!conn || !entry || !entry->dgram || entry->len < 1) return;
if (!conn || !entry || !entry->dgram || entry->len < 1) {
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
return;
}
struct UTUN_INSTANCE* inst = conn->instance;
if (!inst) return;
if (!inst) { queue_dgram_free(entry); queue_entry_free(entry); return; }
struct media_delivery_ctx* md = &inst->md;
if (!md->initialized) { DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "%s: recv before init", MD_ID); return; }
if (!md->initialized) { DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "%s: recv before init", MD_ID); queue_dgram_free(entry); queue_entry_free(entry); return; }
const uint8_t* data = entry->dgram;
size_t len = entry->len;
/* Единый формат router_deliver: [svc_id][src_node_id(8)][dst_node_id(8)][subcmd][...] */
if (len < ROUTER_SVC_PAYLOAD_OFF + 1) return;
if (len < ROUTER_SVC_PAYLOAD_OFF + 1) { queue_dgram_free(entry); queue_entry_free(entry); return; }
uint64_t from_node;
memcpy(&from_node, data + ROUTER_SVC_SRC_OFF, 8);
uint8_t subcmd = data[ROUTER_SVC_PAYLOAD_OFF];
@ -1315,6 +1318,7 @@ static void md_etcp_recv(struct ETCP_CONN* conn, struct ll_entry* entry) {
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "%s: unknown subcmd 0x%02x from 0x%016llx",
MD_ID, subcmd, (unsigned long long)from_node);
}
queue_dgram_free(entry); queue_entry_free(entry);
}
/* ── table creation ── */

9
src/routing_layer/conn_mgr_core.c

@ -814,13 +814,17 @@ void cm_handle_disconnect(struct CONN_MGR* mgr, uint64_t node_id) {
}
void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry) {
if (!entry || !entry->dgram || entry->len < ROUTER_SVC_PAYLOAD_OFF + 2) return;
if (!entry || !entry->dgram || entry->len < ROUTER_SVC_PAYLOAD_OFF + 2) {
if (entry) { queue_dgram_free(entry); queue_entry_free(entry); }
return;
}
if (!conn || !conn->instance) {
DEBUG_ERROR(DEBUG_CATEGORY_ROUTING, "conn_mgr_router_recv: conn=%p", (void*)conn);
queue_dgram_free(entry); queue_entry_free(entry);
return;
}
uint8_t* d = entry->dgram + ROUTER_SVC_PAYLOAD_OFF; size_t len = entry->len - ROUTER_SVC_PAYLOAD_OFF;
if (d[0] != ETCP_RT_ID_CONN_MGR) return;
if (d[0] != ETCP_RT_ID_CONN_MGR) { queue_dgram_free(entry); queue_entry_free(entry); return; }
uint8_t sub = d[1];
/* conn_mgr — per-group: резолвим группу из заголовка доставки, а не instance->conn_mgr */
uint64_t group_id; memcpy(&group_id, entry->dgram + ROUTER_SVC_GROUP_OFF, 8);
@ -836,6 +840,7 @@ void conn_mgr_router_recv_handler(struct ETCP_CONN* conn, struct ll_entry* entry
case CM_SUBCMD_INTERM_SELECTED: if (mgr) cm_handle_interm_selected(mgr, src, d, len); break;
case CM_SUBCMD_DISCONNECT: if (len >= CM_DISCONNECT_SIZE && mgr) cm_handle_disconnect(mgr, ((struct CM_DISCONNECT*)d)->node_id); break;
}
queue_dgram_free(entry); queue_entry_free(entry);
}
int conn_mgr_open(struct UTUN_INSTANCE* inst,

12
src/routing_layer/topo_group.c

@ -408,6 +408,11 @@ static void topo_group_destroy(struct TOPO_GROUP* group) {
topo_nodeq_free_group_fields(group->instance->topo_groups, group->local_node);
u_free(group->local_node);
}
/* освободить подписчиков BGP-событий (member_sync и др.) */
while (group->node_cbks) {
struct topo_node_cbk_entry* c = group->node_cbks;
group->node_cbks = c->next; u_free(c);
}
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[GRP_DESTROY] 5 done");
}
@ -420,13 +425,8 @@ void topo_groups_remove_group(struct TOPO_GROUPS* g, uint64_t group_id) {
DEBUG_INFO(DEBUG_CATEGORY_BGP, "remove_group: id=%016llx type=%d ch=%s",
(unsigned long long)group_id, group->group_type, group->channel_id);
/* освободить подписчиков BGP-событий (member_sync и др.), т.к. группа умирает */
struct topo_node_cbk_entry* c = group->node_cbks;
while (c) { struct topo_node_cbk_entry* n = c->next; u_free(c); c = n; }
group->node_cbks = NULL;
queue_remove_data(g->group_list, &group->ll);
topo_group_destroy(group);
topo_group_destroy(group); /* node_cbks освобождаются внутри */
queue_entry_free(&group->ll);
}

22
src/transport_layer/reality_relay.c

@ -48,8 +48,12 @@ struct reality_relay {
int closed;
size_t bytes_c2d, bytes_d2c;
struct reality_relay *next; // цепочка активных релеев (для shutdown)
};
static struct reality_relay *g_relays = NULL; // активные релеи
static void relay_free(struct reality_relay *r);
static void relay_flush_c2d(struct reality_relay *r);
static void relay_flush_d2c(struct reality_relay *r);
@ -65,6 +69,12 @@ static void relay_free_cb(void *arg) {
static void relay_free(struct reality_relay *r) {
if (!r || r->closed) return;
r->closed = 1;
{ // вынуть из реестра активных релеев
struct reality_relay **pp = &g_relays;
while (*pp && *pp != r) pp = &(*pp)->next;
if (*pp) *pp = r->next;
r->next = NULL;
}
if (r->dns_q) { adns_cancel(r->dns_q); r->dns_q = NULL; }
if (r->client_sid) { uasync_remove_socket_t(r->ua, r->client_sock); r->client_sid = NULL; }
if (r->dest_sid) { uasync_remove_socket_t(r->ua, r->dest_sock); r->dest_sid = NULL; }
@ -265,6 +275,7 @@ int reality_relay_start(struct UASYNC *ua, socket_t client_sock,
r->client_sock = client_sock;
r->dest_sock = SOCKET_INVALID;
r->connecting = 1;
r->next = g_relays; g_relays = r;
// берём владение client_sock сразу (снимаем старую регистрацию) — до парсинга,
// чтобы любая ошибка ниже не оставила сокет зарегистрированным в uasync.
@ -315,3 +326,14 @@ int reality_relay_start(struct UASYNC *ua, socket_t client_sock,
r->host, r->port, initial_len);
return 0;
}
void reality_relay_shutdown(void) {
int count = 0;
while (g_relays) {
struct reality_relay *r = g_relays;
relay_free(r); // сам вынимает r из g_relays и откладывает u_free через call_soon
count++;
}
if (count > 0)
DEBUG_INFO(DEBUG_CATEGORY_REALITY, "reality_relay_shutdown: closed %d active relays", count);
}

5
src/transport_layer/reality_relay.h

@ -26,6 +26,11 @@ int reality_relay_start(struct UASYNC *ua, socket_t client_sock,
const char *dest,
const uint8_t *initial_data, size_t initial_len);
// Принудительно завершить все активные релеи (вызывается при shutdown).
// Освобождение отложено через uasync_call_soon — после вызова нужно
// uasync_drain_immediate, чтобы реальный u_free прошёл.
void reality_relay_shutdown(void);
#ifdef __cplusplus
}
#endif

11
src/utun_instance.c

@ -26,6 +26,7 @@
#include "radio/radio.h"
#include "radio/radio_headless.h"
#include "stcp_server.h"
#include "transport_layer/reality_relay.h"
#include "control_server.h"
#include "transport_layer/node_conn_direct.h"
#include "transport_layer/socket_monitor.h"
@ -511,6 +512,7 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
/* Phase J: TCP connections + sockets — MUST be before ETCP connections */
stcp_server_list_destroy_all(instance);
reality_relay_shutdown(); /* активные reality-релеи откладывают u_free через call_soon */
if (instance->tcp_connections) {
struct ll_entry* entry = instance->tcp_connections->head;
int tc = 0;
@ -577,6 +579,9 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
/* Phase N: etcp_router + nat_det + firewall */
etcp_router_destroy(instance);
/* etcp_router_destroy откладывает queue_entry_free(rconn->ll) через uasync_call_soon
(router_close_finalize) — выкачать их, пока ua ещё жив. */
uasync_drain_immediate(instance->ua);
if (instance->nat_det) { nat_detection_destroy(instance->nat_det); instance->nat_det = NULL; }
fw_free(&instance->fw);
DEBUG_INFO(DEBUG_CATEGORY_SYS, "[DESTROY] N done — router/nat_det/fw");
@ -634,6 +639,12 @@ void utun_instance_destroy(struct UTUN_INSTANCE *instance) {
instance->nodeinfo_cbks = r->next; u_free(r);
}
// Cleanup activity callback chain
while (instance->activity_cbks) {
struct utun_activity_cbk_entry* r = instance->activity_cbks;
instance->activity_cbks = r->next; u_free(r);
}
// Shared SQLite DB закрываем в самом конце: routing/chat/media/db_sync
// используют его во время всего teardown (см. topo_groups_destroy).
if (instance->topo_sqlite_db) {

Loading…
Cancel
Save