From 24a5de9398acd4c5fb1982b72f25de0b749ac2a7 Mon Sep 17 00:00:00 2001 From: Evgeny Date: Fri, 5 Jun 2026 20:01:28 +0300 Subject: [PATCH] =?UTF-8?q?memory=5Fpool:=20=D0=BA=D0=B0=D0=BD=D0=B0=D1=80?= =?UTF-8?q?=D0=B5=D0=B9=D0=BA=D0=B0=20(0xAA)=20+=20=D1=81=D1=87=D1=91?= =?UTF-8?q?=D1=82=D1=87=D0=B8=D0=BA=20double-free;=20lwip=5Ftcp:=20=D1=83?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D0=BD=D1=8B=20TCP=5FTW=5FMAX/tw=5Fiter,=20?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20=D1=85=D1=83?= =?UTF-8?q?=D0=BA=20=D0=B2=20tcp=5Falloc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit memory_pool: +2 байта за объектом — canary 0xAA для детекции buffer overflow, counter для детекции double-free. При free проверяется canary и counter==0, при alloc выставляется новый counter. lwip_tcp: убраны TCP_TW_MAX и счётчики tw_iter (толку нет, краш на 2й итерации). Оставлены: !pcb->ctx (dangling pointer), self-loop checks, next_owner диагностика. Добавлена проверка в tcp_alloc: если новый PCB найден в tw_pcbs → halt. --- lib/memory_pool.c | 48 +++++++++++++++++++++++++++++++----- lib/memory_pool.h | 11 +++++---- src/lwip_tcp/lwip_tcp.c | 26 +++++++++++++------ src/lwip_tcp/lwip_tcp_in.c | 7 +++--- src/lwip_tcp/lwip_tcp_opts.h | 2 -- src/lwip_tcp/lwip_tcp_priv.h | 2 +- src/proxy/tcp_proxy_client.c | 10 +------- 7 files changed, 71 insertions(+), 35 deletions(-) diff --git a/lib/memory_pool.c b/lib/memory_pool.c index 2efeb04b..30581eff 100644 --- a/lib/memory_pool.c +++ b/lib/memory_pool.c @@ -5,6 +5,38 @@ #include "memory_pool.h" #include "mem.h" +#define POOL_CANARY_OFF (pool->object_size) +#define POOL_TAG_OFF (pool->object_size + 1) +#define POOL_ALLOC_SIZE (pool->object_size + 2) +#define POOL_CANARY_VAL 0xAA + +static void pool_init_tags(struct memory_pool* pool, void* obj) +{ + uint8_t* canary = (uint8_t*)obj + POOL_CANARY_OFF; + uint8_t* counter = (uint8_t*)obj + POOL_TAG_OFF; + *canary = POOL_CANARY_VAL; + *counter = ++pool->alloc_tag_counter; + if (pool->alloc_tag_counter == 0) pool->alloc_tag_counter = 1; +} + +static void pool_check_and_clear_tags(struct memory_pool* pool, void* obj, const char* location) +{ + uint8_t* canary = (uint8_t*)obj + POOL_CANARY_OFF; + uint8_t* counter = (uint8_t*)obj + POOL_TAG_OFF; + + if (*canary != POOL_CANARY_VAL) { + DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "pool_free BUFFER OVERFLOW %p canary=0x%02x from %s! halting", obj, *canary, location); + volatile int _halt = 1; + while (_halt) {} + } + if (*counter == 0) { + DEBUG_ERROR(DEBUG_CATEGORY_MEMORY, "pool_free DOUBLE FREE %p from %s! halting", obj, location); + volatile int _halt = 1; + while (_halt) {} + } + *counter = 0; +} + static size_t g_total_free = 0; size_t memory_pool_get_total_free_blocks(void) { @@ -22,6 +54,7 @@ struct memory_pool* memory_pool_init(size_t object_size) { pool->free_count = 0; pool->allocations = 0; pool->reuse_count = 0; + pool->alloc_tag_counter = 1; return pool; } @@ -33,18 +66,21 @@ void* memory_pool_alloc_impl(struct memory_pool* pool, const char* location) { if (pool->free_head) { void* obj = pool->free_head; - pool->free_head = *(void**)obj; // Извлекаем next из начала блока + pool->free_head = *(void**)obj; pool->free_count--; g_total_free--; pool->reuse_count++; - memset(obj, 0, pool->object_size); // Опционально: очищаем память для безопасности + memset(obj, 0, pool->object_size); + pool_init_tags(pool, obj); DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "pool_alloc reused: %s, remaining=%zu", location, pool->free_count); return obj; } pool->allocations++; DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "pool_alloc: %s, total_allocs=%zu", location, pool->allocations); - return u_calloc_impl(1, pool->object_size, location); // Используем calloc для инициализации нулями + void* obj = u_calloc_impl(1, POOL_ALLOC_SIZE, location); + if (obj) pool_init_tags(pool, obj); + return obj; } // Освободить объект в пул или в free @@ -53,9 +89,10 @@ void memory_pool_free_impl(struct memory_pool* pool, void* obj, const char* loca return; } - // Если пул не заполнен, сохранить объект для повторного использования + pool_check_and_clear_tags(pool, obj, location); + if (pool->free_count < MEMORY_POOL_MAX_FREE) { - *(void**)obj = pool->free_head; // Сохраняем next в начало блока + *(void**)obj = pool->free_head; pool->free_head = obj; pool->free_count++; g_total_free++; @@ -63,7 +100,6 @@ void memory_pool_free_impl(struct memory_pool* pool, void* obj, const char* loca return; } - // Иначе освободить через free DEBUG_INFO(DEBUG_CATEGORY_MEMORY, "pool_free direct: %s", location); u_free_impl(obj, location); } diff --git a/lib/memory_pool.h b/lib/memory_pool.h index 80084657..268cdec8 100644 --- a/lib/memory_pool.h +++ b/lib/memory_pool.h @@ -12,11 +12,12 @@ #define PLOCATION __FILE__ ":" XSTR(__LINE__) "[POOL]" struct memory_pool { - void* free_head; // Голова linked list свободных блоков (next хранится в начале каждого блока) - int free_count; // Количество свободных блоков - size_t object_size; // Размер объектов в пуле - size_t allocations; // Общее количество аллокаций (включая новые malloc) - size_t reuse_count; // Количество повторных использований из пула + void* free_head; // Голова linked list свободных блоков (next хранится в начале каждого блока) + int free_count; // Количество свободных блоков + size_t object_size; // Размер объектов в пуле (пользовательские данные) + size_t allocations; // Общее количество аллокаций (включая новые malloc) + size_t reuse_count; // Количество повторных использований из пула + uint8_t alloc_tag_counter; // монотонный счётчик для детекции double-free (1 байт) }; // сам пул: diff --git a/src/lwip_tcp/lwip_tcp.c b/src/lwip_tcp/lwip_tcp.c index 7e99488c..5e5cb432 100644 --- a/src/lwip_tcp/lwip_tcp.c +++ b/src/lwip_tcp/lwip_tcp.c @@ -788,11 +788,10 @@ void tcp_slowtmr(struct lwip_tcp_ctx *ctx) // ---- process TIME-WAIT PCBs ---- prev = NULL; pcb = ctx->tw_pcbs; - int tw_iter = 0; while (pcb != NULL) { - if (++tw_iter > TCP_TW_MAX) { - DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS LOOP in tcp_slowtmr: %d iterations, aborting pcb=%p state=%d port=%u next_owner=%d", tw_iter, (void*)pcb, pcb->state, pcb->local_port, pcb->next_owner); - tcp_abort(pcb); + if (!pcb->ctx) { + DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS DANGLING pcb=%p ctx=NULL in tcp_slowtmr, clearing tw_pcbs", (void*)pcb); + ctx->tw_pcbs = NULL; break; } pcb_remove = 0; @@ -1032,11 +1031,10 @@ static void tcp_kill_timewait(struct lwip_tcp_ctx *ctx) if (!ctx) return; inactivity = 0; inactive = NULL; - int tw_iter = 0; for (pcb = ctx->tw_pcbs; pcb != NULL; pcb = pcb->next) { - if (++tw_iter > TCP_TW_MAX) { - DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS LOOP in tcp_kill_timewait: %d iterations, aborting pcb=%p state=%d port=%u next_owner=%d", tw_iter, (void*)pcb, pcb->state, pcb->local_port, pcb->next_owner); - tcp_abort(pcb); + if (!pcb->ctx) { + DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS DANGLING pcb=%p ctx=NULL in tcp_kill_timewait, clearing tw_pcbs", (void*)pcb); + ctx->tw_pcbs = NULL; return; } if ((uint32_t)(ctx->ticks - pcb->tmr) >= inactivity) { @@ -1105,6 +1103,18 @@ struct tcp_pcb *tcp_alloc(struct lwip_tcp_ctx *ctx, uint8_t prio) if (pcb != NULL) { memset(pcb, 0, sizeof(struct tcp_pcb)); +#ifdef DEBUG + { + struct tcp_pcb *_tmp; + for (_tmp = ctx->tw_pcbs; _tmp; _tmp = _tmp->next) { + if (_tmp == pcb) { + DEBUG_ERROR(DEBUG_CATEGORY_ALL, "tcp_alloc: REUSED PCB %p STILL IN tw_pcbs! halting for debug", (void*)pcb); + volatile int _halt = 1; + while (_halt) {} + } + } + } +#endif pcb->prio = prio; pcb->ctx = ctx; pcb->snd_buf = TCP_SND_BUF; diff --git a/src/lwip_tcp/lwip_tcp_in.c b/src/lwip_tcp/lwip_tcp_in.c index 80b104df..b62f7ca7 100644 --- a/src/lwip_tcp/lwip_tcp_in.c +++ b/src/lwip_tcp/lwip_tcp_in.c @@ -209,11 +209,10 @@ void lwip_tcp_input(struct lwip_tcp_ctx *ctx, struct pbuf *p, } if (pcb == NULL) { - int tw_iter = 0; for (pcb = ctx->tw_pcbs; pcb != NULL; pcb = pcb->next) { - if (++tw_iter > TCP_TW_MAX) { - DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS LOOP in lwip_tcp_input: %d iterations, aborting pcb=%p sport=%u dport=%u next_owner=%d", tw_iter, (void*)pcb, sport, dport, pcb->next_owner); - tcp_abort(pcb); + if (!pcb->ctx) { + DEBUG_ERROR(DEBUG_CATEGORY_ALL, "TW_PCBS DANGLING pcb=%p ctx=NULL in lwip_tcp_input, clearing tw_pcbs", (void*)pcb); + ctx->tw_pcbs = NULL; pbuf_free(p); return; } diff --git a/src/lwip_tcp/lwip_tcp_opts.h b/src/lwip_tcp/lwip_tcp_opts.h index 37d972fe..28cfbf11 100644 --- a/src/lwip_tcp/lwip_tcp_opts.h +++ b/src/lwip_tcp/lwip_tcp_opts.h @@ -23,8 +23,6 @@ #define TCP_KEEPINTVL_DEFAULT 75000 #define TCP_KEEPCNT_DEFAULT 9 -#define TCP_TW_MAX 256 // max tw_pcbs before cycle detection triggers - #define TCP_WND_SCALE 0 // disabled #define TCP_TIMESTAMPS 0 // disabled #define TCP_SACK_OUT 0 // disabled diff --git a/src/lwip_tcp/lwip_tcp_priv.h b/src/lwip_tcp/lwip_tcp_priv.h index 482727a0..882a030f 100644 --- a/src/lwip_tcp/lwip_tcp_priv.h +++ b/src/lwip_tcp/lwip_tcp_priv.h @@ -242,7 +242,7 @@ void lwip_tcp_stats_clear(struct lwip_tcp_ctx *ctx); #define TCP_RMV(pcbs, npcb) do { \ if(*(pcbs) == (npcb)) { *(pcbs) = (*pcbs)->next; } \ - else { struct tcp_pcb *_tmp; int _rmv_i = 0; for(_tmp = *(pcbs); _tmp != NULL && _rmv_i < TCP_TW_MAX; _tmp = _tmp->next) { if(++_rmv_i >= TCP_TW_MAX) break; if(_tmp->next == (npcb)) { _tmp->next_owner = PCB_NEXT_SLOWTMR; _tmp->next = (npcb)->next; break; } } } \ + else { struct tcp_pcb *_tmp; for(_tmp = *(pcbs); _tmp != NULL; _tmp = _tmp->next) { if(_tmp->next == (npcb)) { _tmp->next_owner = PCB_NEXT_SLOWTMR; _tmp->next = (npcb)->next; break; } } } \ (npcb)->next = NULL; \ (npcb)->next_owner = PCB_NEXT_RMV; \ } while(0) diff --git a/src/proxy/tcp_proxy_client.c b/src/proxy/tcp_proxy_client.c index 7f167b10..9d1225ee 100644 --- a/src/proxy/tcp_proxy_client.c +++ b/src/proxy/tcp_proxy_client.c @@ -478,18 +478,10 @@ static void tcp_proxy_client_handle_error(struct tcp_proxy_client* p, uint32_t s struct tcp_proxy_client_conn* pc = tcp_proxy_client_find_conn(p, stream_id); if (!pc) { DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "PROXY ERROR sid=%08x — no conn, dropping", stream_id); return; } DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "PROXY ERROR from exit sid=%08x fin_local=%d", stream_id, pc->fin_local); + pc->pcb = NULL;// уже освобождён. больше им нельзя пользоваться pc->rem_closed = 1; if (pc->tx_buf) { u_free(pc->tx_buf); pc->tx_buf = NULL; pc->tx_len = 0; } etcp_router_waiter_cancel(pc->proxy->inst, pc->proxy->via_node_id, &pc->tx_waiter); - if (pc->pcb) { - tcp_arg(pc->pcb, NULL); - tcp_recv(pc->pcb, NULL); - tcp_sent(pc->pcb, NULL); - tcp_err(pc->pcb, NULL); - tcp_poll(pc->pcb, NULL, 0); - tcp_abort(pc->pcb); - pc->pcb = NULL; - } tcp_proxy_client_conn_free(pc); }