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
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 (приемлемо).
|
|
|