Browse Source

memory_pool: канарейка (0xAA) + счётчик double-free; lwip_tcp: убраны TCP_TW_MAX/tw_iter, добавлен хук в tcp_alloc

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.
etcp-inflight-fix
Evgeny 4 months ago
parent
commit
24a5de9398
  1. 48
      lib/memory_pool.c
  2. 11
      lib/memory_pool.h
  3. 26
      src/lwip_tcp/lwip_tcp.c
  4. 7
      src/lwip_tcp/lwip_tcp_in.c
  5. 2
      src/lwip_tcp/lwip_tcp_opts.h
  6. 2
      src/lwip_tcp/lwip_tcp_priv.h
  7. 10
      src/proxy/tcp_proxy_client.c

48
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);
}

11
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 байт)
};
// сам пул:

26
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;

7
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;
}

2
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

2
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)

10
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);
}

Loading…
Cancel
Save