You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

67 lines
2.8 KiB

# cm_invite_pending — утечка указателей и corruption при destroy
## Корень проблемы
У `cm_invite_pending` три внешних держателя указателя в разных подсистемах:
uasync heap (overall_timer) → node->arg = inv
NCD (node_conn_direct) → arg = inv в cm_invite_ncd_callback
STCP (stcp_link) → arg = inv в cm_tcp_ready_cb
Когда cm_invite_cancel делает u_free(inv), коллбэки от уже запланированных
событий приходят после освобождения → пишут в переиспользованную память →
corruption (float-значения в полях mgr/node_id).
## Решение
1. Поле uint8_t cancelled:1 в struct cm_invite_pending
(рядом с tcp_ready:1 в conn_mgr_priv.h)
2. cm_invite_cancel:
- inv->cancelled = 1 первой же строкой
- отмена таймера, закрытие ncd/tcp handles
- удаление из invite_list
- uasync_post(ua, cm_invite_free_deferred, inv) вместо немедленного u_free
3. Все коллбэки — guard первой строкой:
void cm_invite_ncd_callback(..., void* arg) {
struct cm_invite_pending* inv = arg;
if (inv->cancelled) return;
...
}
4. cm_invite_fail:
- if (inv->cancelled) return
- сохранить cb/handle/node_id/group_id ДО вызова cm_invite_cancel
- cm_invite_cancel(inv) (cancelled=1, deferred free)
- cb(handle, ...) (коллбэк после cancel, inv ещё жив)
- u_free(handle)
## Почему корректно
cm_invite_cancel:
inv->cancelled = 1 ← блокировка коллбэков (однопоточно, немедленно)
uasync_cancel_timeout ← таймер удалён из heap
node_conn_direct_close ← handle закрыт
uasync_post(free, inv) ← освобождение в конец очереди событий
[уже запланированные коллбэки]:
if (inv->cancelled) return ← выход без действий
[deferred free]:
u_free(inv) ← все коллбэки уже отработали
## Краевые случаи
conn_mgr_destroy → cm_invite_cancel:
Отмена таймера, закрытие handles, deferred free. Ни один коллбэк
не дёрнет освобождённую память.
NCD/STCP коллбэк после cancel:
cancelled=1 → мгновенный return.
Таймаут приглашения после cancel:
cancelled=1 → return, не вызывает cm_invite_fail повторно.
uasync_destroy до deferred free:
inv утекает при shutdown (приемлемо).