Browse Source

router: fix UAF on rconn->recv_conn after ETCP_CONN destroy

recv_conn хранил заимствованный указатель на физический conn без очистки
при его освобождении. Добавлен etcp_router_conn_destroyed(), вызываемый из
etcp_connection_close фазы 1; RST теперь отправляется через живой conn,
доставивший пакет, а не через устаревший recv_conn.
router-recv-conn-uaf
evgeny 2 weeks ago
parent
commit
51a5c5bcbf
  1. 38
      src/routing_layer/etcp_router.c
  2. 3
      src/routing_layer/etcp_router.h
  3. 1
      src/transport_layer/etcp.c

38
src/routing_layer/etcp_router.c

@ -33,7 +33,7 @@ static void router_schedule_ack(struct ETCP_ROUTER_CONN* rconn);
static void router_try_assembly(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn);
static void router_deliver(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn, const uint8_t* wire, size_t wire_len);
static void router_send_ctrl(struct ETCP_ROUTER_CONN* rconn, uint8_t flag_bits);
static void router_send_rst(struct ETCP_ROUTER_CONN* rconn);
static void router_send_rst(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn);
static void router_restart_send(struct ETCP_ROUTER_CONN* rconn);
static struct ETCP_CONN* router_send_conn(struct ETCP_ROUTER_CONN* rconn);
static void router_send_kick(struct ETCP_ROUTER_CONN* rconn);
@ -50,7 +50,7 @@ static void router_conn_reset(struct ETCP_ROUTER_CONN* rconn);
static void free_entry(struct ll_entry* e) { queue_dgram_free(e); queue_entry_free(e); }
static void router_handle_ack(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN* rconn, uint32_t seq, uint64_t src_node_id, uint16_t ack_ts);
static void router_forward_transit(struct UTUN_INSTANCE* inst, struct ll_entry* entry, struct SVC_ROUTE_HDR* hdr);
static int router_check_peer_restart(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN** prconn, struct SVC_ROUTE_HDR* hdr, size_t wire_len);
static int router_check_peer_restart(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN** prconn, struct SVC_ROUTE_HDR* hdr, size_t wire_len, struct ETCP_CONN* conn);
static void router_handle_data_packet(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn, uint32_t seq, const uint8_t* wire, size_t wire_len);
static void router_retransmit_one(struct ETCP_ROUTER_CONN* rconn, struct ROUTER_INFLIGHT* inf);
static void router_retrans_schedule(struct ETCP_ROUTER_CONN* rconn);
@ -153,6 +153,27 @@ void etcp_router_transit_queues_destroy(struct ETCP_CONN* conn) {
conn->transit_queues = NULL;
}
// Сбросить recv_conn у всех ROUTER_CONN, ссылающихся на уничтожаемый ETCP_CONN.
// recv_conn — заимствованный указатель; без очистки он висит после u_free(conn) (UAF).
void etcp_router_conn_destroyed(struct ETCP_CONN* conn) {
if (!conn || !conn->instance || !conn->instance->router_conns) return;
struct ll_queue* q = conn->instance->router_conns;
for (uint32_t slot = 0; slot < q->hash_size; slot++) {
struct ll_entry* entry = q->hash_table[slot];
while (entry) {
struct ll_entry* next = entry->hash_next;
struct ETCP_ROUTER_CONN* rconn = (struct ETCP_ROUTER_CONN*)entry;
if (rconn->recv_conn == conn) {
DEBUG_DEBUG(DEBUG_CATEGORY_ETCPROUTE,
"router: clear recv_conn remote=%016llx svc_id=%u (conn destroyed)",
(unsigned long long)rconn->remote_node_id, rconn->svc_id);
rconn->recv_conn = NULL;
}
entry = next;
}
}
}
// ====================================================================
// Управление ROUTER_CONN
// ====================================================================
@ -251,10 +272,9 @@ static void router_send_ctrl(struct ETCP_ROUTER_CONN* rconn, uint8_t flag_bits)
}
// Отправка RST удалённой стороне — сброс чужой «старой» сессии.
// Идёт через recv_conn: в асимметричной топологии route-lookup до remote вернёт NULL.
static void router_send_rst(struct ETCP_ROUTER_CONN* rconn) {
// conn — живой физический conn, доставивший пакет (recv_conn может быть уже устаревшим).
static void router_send_rst(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn) {
struct UTUN_INSTANCE* inst = rconn->inst;
struct ETCP_CONN* conn = rconn->recv_conn;
if (!conn) conn = topo_group_find_conn_for_node(topo_groups_find(inst->topo_groups, rconn->group_id), rconn->remote_node_id);
if (!conn) {
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_rst: no route to %016llx svc_id=%u",
@ -1069,7 +1089,7 @@ static void router_forward_transit(struct UTUN_INSTANCE* inst, struct ll_entry*
}
static int router_check_peer_restart(struct UTUN_INSTANCE* inst, struct ETCP_ROUTER_CONN** prconn,
struct SVC_ROUTE_HDR* hdr, size_t wire_len) {
struct SVC_ROUTE_HDR* hdr, size_t wire_len, struct ETCP_CONN* conn) {
(void)wire_len;
struct ETCP_ROUTER_CONN* rconn = *prconn;
if (!(hdr->flags & ROUTER_FLAG_START)) return 0; // не START — не рестарт
@ -1091,7 +1111,7 @@ static int router_check_peer_restart(struct UTUN_INSTANCE* inst, struct ETCP_ROU
"router: peer reset_id=%016llx != mine=%016llx — master keeps epoch, RST to %016llx svc_id=%u",
(unsigned long long)peer_rid, (unsigned long long)rconn->reset_id,
(unsigned long long)hdr->src_node_id, hdr->svc_id);
router_send_rst(rconn);
router_send_rst(rconn, conn);
return 0;
} else {
// slave принимает эпоху мастера и ресетится.
@ -1207,11 +1227,11 @@ static void etcp_router_recv_cb(struct ETCP_CONN* conn, struct ll_entry* entry)
rconn = etcp_router_conn_get(inst, hdr->group_id, hdr->src_node_id, hdr->svc_id);
if (!rconn) { free_entry(entry); return; }
}
if (router_check_peer_restart(inst, &rconn, hdr, entry->len) != 0) { free_entry(entry); return; }
if (router_check_peer_restart(inst, &rconn, hdr, entry->len, conn) != 0) { free_entry(entry); return; }
// Сервер после рестарта «ждёт START»: пришёл не-START пакет от старой сессии → RST.
if (!rconn->peer_sync_done && !(hdr->flags & ROUTER_FLAG_START)) {
router_send_rst(rconn);
router_send_rst(rconn, conn);
free_entry(entry); return;
}

3
src/routing_layer/etcp_router.h

@ -245,6 +245,9 @@ void etcp_router_conn_restart(struct UTUN_INSTANCE* inst, uint64_t group_id, uin
// Удалить все транзитные очереди соединения (вызывается при закрытии ETCP_CONN)
void etcp_router_transit_queues_destroy(struct ETCP_CONN* conn);
// Сбросить recv_conn у всех ROUTER_CONN, ссылающихся на уничтожаемый ETCP_CONN.
void etcp_router_conn_destroyed(struct ETCP_CONN* conn);
#ifdef __cplusplus
}

1
src/transport_layer/etcp.c

@ -407,6 +407,7 @@ void etcp_connection_close(struct ETCP_CONN* etcp) {
routing_del_conn(etcp);
etcp_router_transit_queues_destroy(etcp);
etcp_router_conn_destroyed(etcp);
if (etcp->normalizer) { pn_deinit((struct PKTNORM*)etcp->normalizer); etcp->normalizer = NULL; etcp->send_input_q = NULL; }

Loading…
Cancel
Save