Browse Source

fix: prevent use-after-free in etcp_ack_recv when link closed with pending inflight

When etcp_link_close() frees a link while INFLIGHT_PACKET entries
still reference it via last_link, a later ACK causes SIGSEGV in
bbr_main(link->bbr=NULL).

Fix:
- etcp_inflight_nullify_link(): nullifies last_link in both
  input_wait_ack and input_send_q for entries pointing to dead_link
- Called from etcp_link_close() before u_free(link)
- Guard etcp_ack_recv: check acked_pkt->last_link->bbr before
  accessing BBR/inflight stats
- Also fix bbr leak in etcp_link_close !link->conn path

Verified: 30/30 runs of test_etcp_link_stress pass (was ~50% crash)
Full suite: 71/71 pass
topo_upd
evgeny 2 months ago
parent
commit
f667e7bd74
  1. 2
      src/transport_layer/etcp.c
  2. 15
      src/transport_layer/etcp_connections.c

2
src/transport_layer/etcp.c

@ -1410,7 +1410,7 @@ void etcp_ack_recv(struct ETCP_CONN* etcp, uint32_t seq, uint16_t ts, uint16_t d
} }
// === subtract inflight from the LAST link the packet was sent on === // === subtract inflight from the LAST link the packet was sent on ===
if (acked_pkt->last_link) { if (acked_pkt->last_link && acked_pkt->last_link->bbr) {
struct ETCP_LINK* link = acked_pkt->last_link; struct ETCP_LINK* link = acked_pkt->last_link;
link->inflight_bytes -= acked_pkt->ll.len; link->inflight_bytes -= acked_pkt->ll.len;
link->inflight_packets--; link->inflight_packets--;

15
src/transport_layer/etcp_connections.c

@ -1074,6 +1074,18 @@ void etcp_link_update_inflight_lim(struct ETCP_LINK* link, uint32_t new_lim) {
etcp_conn_on_inflight_lim_changed(link->etcp); etcp_conn_on_inflight_lim_changed(link->etcp);
} }
// Обнуляет last_link для всех inflight-записей ссылающихся на dead_link.
// Вызывается перед u_free(link) чтобы etcp_ack_recv не упал на use-after-free.
static void etcp_inflight_nullify_link(struct ll_queue* q, struct ETCP_LINK* dead_link) {
if (!q) return;
struct ll_entry* entry = q->head;
while (entry) {
struct INFLIGHT_PACKET* inf = (struct INFLIGHT_PACKET*)entry->data;
if (inf->last_link == dead_link) inf->last_link = NULL;
entry = entry->next;
}
}
void etcp_link_close(struct ETCP_LINK* link) { void etcp_link_close(struct ETCP_LINK* link) {
DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, ""); DEBUG_TRACE(DEBUG_CATEGORY_CONNECTION, "");
if (!link) return; if (!link) return;
@ -1096,6 +1108,7 @@ void etcp_link_close(struct ETCP_LINK* link) {
if (link->shaper_timer) uasync_cancel_timeout(link->etcp->instance->ua, link->shaper_timer); if (link->shaper_timer) uasync_cancel_timeout(link->etcp->instance->ua, link->shaper_timer);
if (link->keepalive_timer) uasync_cancel_timeout(link->etcp->instance->ua, link->keepalive_timer); if (link->keepalive_timer) uasync_cancel_timeout(link->etcp->instance->ua, link->keepalive_timer);
etcp_conn_on_inflight_lim_changed(link->etcp); etcp_conn_on_inflight_lim_changed(link->etcp);
u_free(link->bbr);
u_free(link); u_free(link);
return; return;
} }
@ -1136,6 +1149,8 @@ void etcp_link_close(struct ETCP_LINK* link) {
link->etcp->log_name, link->local_link_id, sockaddr_storage_to_str(&link->remote_addr).str, link->total_decrypted, link->etcp->log_name, link->local_link_id, sockaddr_storage_to_str(&link->remote_addr).str, link->total_decrypted,
(unsigned long long)link->acked_bytes, link->inflight_bytes, link->inflight_packets); (unsigned long long)link->acked_bytes, link->inflight_bytes, link->inflight_packets);
etcp_fire_link_status_cbk(link, link->link_state, link->link_status); etcp_fire_link_status_cbk(link, link->link_state, link->link_status);
etcp_inflight_nullify_link(link->etcp->input_wait_ack, link);
etcp_inflight_nullify_link(link->etcp->input_send_q, link);
u_free(link->bbr); u_free(link->bbr);
u_free(link); u_free(link);
} }

Loading…
Cancel
Save