Files
utun2/close_bug.txt
T
evgeny 38c9f229bc stcp: ed25519 pubkey exchange in handshake, fix instance destroy segfault, fix chat_tcp_test
- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll
- stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close
- utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP),
  add deferred drain in both Phase J and L
- stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39,
  exchange ed25519_pubkey in both handshake directions
- etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in
  etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb
- chat_sync.c: fix lk->conn NULL dereference for TCP links
- test_stcp.c: update stcp_server_create/stcp_client_connect callsites
- tools/chat_tcp_test: change transport udp→tcp
2026-08-07 00:48:27 +03:00

68 lines
2.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 (приемлемо).