Browse Source

conn_mgr: fix reverse-phase timer leak causing use-after-free crash

cm_ncd_callback re-entered cm_start_phase_reverse on every NCD timeout
during REVERSE phase (shared NCD revived by other modules), leaking reverse
timers: only the last one was tracked in entry->main.timer, so cm_entry_cleanup
cancelled only that one and the earlier timers fired on the freed entry.

- ignore repeated NCD timeouts while already in REVERSE (phase==2)
- cancel entry->main.timer before overwriting in reverse/indirect phases
- remove entry from reverse_pending in cm_entry_cleanup
proxy
evgeny 2 weeks ago
parent
commit
ee54bd52b3
  1. 12
      src/routing_layer/conn_mgr_core.c
  2. 1
      src/routing_layer/conn_mgr_indirect.c

12
src/routing_layer/conn_mgr_core.c

@ -181,6 +181,12 @@ struct CONN_MGR_ENTRY* cm_ensure_entry(struct CONN_MGR* mgr, uint64_t node_id) {
void cm_entry_cleanup(struct CONN_MGR_ENTRY* entry) {
if (!entry || entry->state == CONN_MGR_STATE_DISCONNECTED) return;
if (entry->main.timer) { uasync_cancel_timeout(entry->mgr->instance->ua, entry->main.timer); entry->main.timer = NULL; }
{ struct cm_reverse_pending** pp = &entry->mgr->reverse_pending;
while (*pp) {
if ((*pp)->entry == entry) { struct cm_reverse_pending* rp = *pp; *pp = rp->next; u_free(rp); }
else pp = &(*pp)->next;
}
}
{ struct cm_exchange_pending** pp = &entry->mgr->exchange_pending;
while (*pp) {
if ((*pp)->entry == entry) {
@ -281,6 +287,11 @@ void cm_ncd_callback(struct NODE_CONN_DIRECT* ncd_h, enum ncd_event ncd_ev, void
cm_entry_cleanup(entry);
return;
}
/* Инициатор с handle'ами: выбор следующей фазы делаем только из DIRECT (phase==0).
* В REVERSE (phase==2) эскалацией владеет собственный 15с таймер, а повторные
* NCD-таймауты (шаримый NCD оживляется другими модулями) игнорируем — иначе
* cm_start_phase_reverse утечёт старыми таймерами и выстрелит в освобождённый entry. */
if (entry->main.phase == 2) return;
{ struct TOPO_GROUP* g = entry->mgr->group;
struct TOPO_GROUP_NODE* t = topo_node_find_by_id(g, entry->node_id);
if (cm_has_direct_ip(entry->mgr, g->local_node) && !(t && cm_has_direct_ip(entry->mgr, t)))
@ -684,6 +695,7 @@ void cm_start_phase_reverse(struct CONN_MGR_ENTRY* entry) {
etcp_route_send(entry->mgr->instance, group->group_id, entry->node_id, qe, 1, 0);
struct cm_reverse_pending* rp = u_calloc(1, sizeof(*rp));
if (rp) { rp->request_id=req_id; rp->entry=entry; rp->next=entry->mgr->reverse_pending; entry->mgr->reverse_pending=rp; }
if (entry->main.timer) { uasync_cancel_timeout(entry->mgr->instance->ua, entry->main.timer); entry->main.timer = NULL; }
entry->main.timer = uasync_set_timeout(entry->mgr->instance->ua, CONN_MGR_CONNECT_REVERSE_TIMEOUT_MS*10,
entry, cm_reverse_timeout_cb, "conn_mgr_reverse");
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "conn_mgr: phase 2 (reverse) sent DIRECT_REQ to 0x%016llx with %u addrs",

1
src/routing_layer/conn_mgr_indirect.c

@ -27,6 +27,7 @@ void cm_start_phase_indirect(struct CONN_MGR_ENTRY* entry) {
struct ll_entry* qe = queue_entry_new(0); if (!qe) return;
qe->dgram=u_malloc(1+sizeof(req)); qe->dgram[0]=ETCP_RT_ID_CONN_MGR; memcpy(qe->dgram+1,&req,sizeof(req)); qe->len=(uint16_t)(1+sizeof(req));
etcp_route_send(mgr->instance, mgr->group->group_id, entry->node_id, qe, 1, 0);
if (entry->main.timer) { uasync_cancel_timeout(mgr->instance->ua, entry->main.timer); entry->main.timer = NULL; }
entry->main.timer = uasync_set_timeout(mgr->instance->ua, CONN_MGR_INTERM_EXCHANGE_TIMEOUT_MS*10, entry, cm_exchange_timeout_cb, "conn_mgr_interm_exch");
struct cm_exchange_pending* ep = u_calloc(1,sizeof(*ep));
if (ep) { ep->request_id=entry->main.request_id; ep->entry=entry; ep->timer=entry->main.timer; ep->next=mgr->exchange_pending; mgr->exchange_pending=ep; }

Loading…
Cancel
Save