Browse Source

Fix group node scan stall and UAF on server link teardown

- node_conn_direct_close: immediate conn cleanup when links are down
  (graceful fin_wait on a down conn left a lingering entry, so re-open
  in Phase3 stuck in 'pending' with no timer and scan never continued)
- tcp_link_close_cb: skip etcp_link_close if conn was already closed
  synchronously during etcp_on_link_down cascade (fixes use-after-free)
v2
evgeny 3 weeks ago
parent
commit
311cee0ee1
  1. 10
      src/transport_layer/etcp_connections.c
  2. 5
      src/transport_layer/node_conn_direct.c

10
src/transport_layer/etcp_connections.c

@ -1119,6 +1119,7 @@ static void tcp_link_close_cb(struct stcp_link *sl, int err, void *arg) {
if (!link || !link->etcp) return;
if (link->etcp->state == 2) return;
int old_state = link->link_state;
struct ETCP_CONN *etcp = link->etcp; /* сохранить до каскада: etcp_on_link_down может закрыть conn и освободить link */
stcp_link_close(sl);
link->tcp_link = NULL;
@ -1126,8 +1127,13 @@ static void tcp_link_close_cb(struct stcp_link *sl, int err, void *arg) {
if (link->is_server) {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP server link %d down err=%d, closing", link->etcp->log_name, link->local_link_id, err);
etcp_on_link_down(link->etcp, link);
etcp_link_close(link);
etcp_on_link_down(etcp, link);
if (etcp->state != 2) {
etcp_link_close(link);
} else {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP server link %d already closed by conn teardown, skip",
etcp->log_name, link->local_link_id);
}
} else {
DEBUG_INFO(DEBUG_CATEGORY_ETCP, "[%s] TCP link %d down err=%d, scheduling reconnect", link->etcp->log_name, link->local_link_id, err);
etcp_on_link_down(link->etcp, link);

5
src/transport_layer/node_conn_direct.c

@ -874,7 +874,7 @@ void node_conn_direct_close(struct NODE_CONN_DIRECT* h) {
}
h->cb = NULL; h->cb_arg = NULL;
if (entry->connect_timer) { uasync_cancel_timeout(entry->ua, entry->connect_timer); entry->connect_timer = NULL; }
if (conn && conn->state != 2 && !entry->timed_out) {
if (conn && conn->state != 2 && !entry->timed_out && conn->links_up > 0) {
/* устанавливаем fin_wait, отправляем CLOSE, ставим таймер */
conn->fin_wait = 1;
conn->fin_wait_clear_cb = ncd_fin_wait_cancelled;
@ -903,8 +903,9 @@ void node_conn_direct_close(struct NODE_CONN_DIRECT* h) {
/* закрываем conn синхронно чтобы линки сразу ушли из socket->links_queue:
* иначе в том же тике новый open того же узла создаст дубль-conn и коллизию адресов.
* u_free(conn) всё равно отложен (phase 2 в etcp_connection_close), UAF нет. */
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] close: node=0x%016llx state=%d timed_out=%d cb_running=%d -> %s",
DEBUG_INFO(DEBUG_CATEGORY_NCD, "[ncd] close: node=0x%016llx state=%d timed_out=%d cb_running=%d links_up=%d -> %s",
(unsigned long long)node_id, conn->state, entry->timed_out, conn->callbacks_running,
conn->links_up,
conn->callbacks_running ? "deferred" : "sync");
if (conn->callbacks_running) uasync_call_soon(entry->ua, conn, ncd_deferred_close_conn);
else etcp_connection_close(conn);

Loading…
Cancel
Save