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
This commit is contained in:
1 parent
e885fe30f5
commit
ee54bd52b3
2 files changed
+13
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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; }
|
||||
|
||||
Reference in new issue
Block a user