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_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_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_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 void router_restart_send(struct ETCP_ROUTER_CONN* rconn);
static struct ETCP_CONN* router_send_conn(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); 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 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_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 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_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_retransmit_one(struct ETCP_ROUTER_CONN* rconn, struct ROUTER_INFLIGHT* inf);
static void router_retrans_schedule(struct ETCP_ROUTER_CONN* rconn); 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; 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 // Управление ROUTER_CONN
// ==================================================================== // ====================================================================
@ -251,10 +272,9 @@ static void router_send_ctrl(struct ETCP_ROUTER_CONN* rconn, uint8_t flag_bits)
} }
// Отправка RST удалённой стороне — сброс чужой «старой» сессии. // Отправка RST удалённой стороне — сброс чужой «старой» сессии.
// Идёт через recv_conn: в асимметричной топологии route-lookup до remote вернёт NULL. // conn — живой физический conn, доставивший пакет (recv_conn может быть уже устаревшим).
static void router_send_rst(struct ETCP_ROUTER_CONN* rconn) { static void router_send_rst(struct ETCP_ROUTER_CONN* rconn, struct ETCP_CONN* conn) {
struct UTUN_INSTANCE* inst = rconn->inst; 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) conn = topo_group_find_conn_for_node(topo_groups_find(inst->topo_groups, rconn->group_id), rconn->remote_node_id);
if (!conn) { if (!conn) {
DEBUG_WARN(DEBUG_CATEGORY_ETCPROUTE, "router_rst: no route to %016llx svc_id=%u", 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, 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; (void)wire_len;
struct ETCP_ROUTER_CONN* rconn = *prconn; struct ETCP_ROUTER_CONN* rconn = *prconn;
if (!(hdr->flags & ROUTER_FLAG_START)) return 0; // не START — не рестарт 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", "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)peer_rid, (unsigned long long)rconn->reset_id,
(unsigned long long)hdr->src_node_id, hdr->svc_id); (unsigned long long)hdr->src_node_id, hdr->svc_id);
router_send_rst(rconn); router_send_rst(rconn, conn);
return 0; return 0;
} else { } else {
// slave принимает эпоху мастера и ресетится. // 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); rconn = etcp_router_conn_get(inst, hdr->group_id, hdr->src_node_id, hdr->svc_id);
if (!rconn) { free_entry(entry); return; } 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. // Сервер после рестарта «ждёт START»: пришёл не-START пакет от старой сессии → RST.
if (!rconn->peer_sync_done && !(hdr->flags & ROUTER_FLAG_START)) { if (!rconn->peer_sync_done && !(hdr->flags & ROUTER_FLAG_START)) {
router_send_rst(rconn); router_send_rst(rconn, conn);
free_entry(entry); return; 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) // Удалить все транзитные очереди соединения (вызывается при закрытии ETCP_CONN)
void etcp_router_transit_queues_destroy(struct ETCP_CONN* 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 #ifdef __cplusplus
} }

1
src/transport_layer/etcp.c

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

Loading…
Cancel
Save