From 8bfb456161798aea623551e6c5b79361f3e3892d Mon Sep 17 00:00:00 2001 From: evgeny Date: Fri, 18 Sep 2026 17:58:40 +0300 Subject: [PATCH] etcp: fix OOB/DoS in packet parsing and ping path - FILLER: drop (uint16_t) truncation in fill_len bound (OOB pointer) - ACK: require len >= ETCP_ACK_BASE_SIZE (8); wrap-aware clamp of till to next_tx_id + 16MB cumulative-ACK iteration limit (DoS) - ping: bound len before sc_encrypt (stack overflow), ulen bound, allocate ETCP_DGRAM + PACKET_DATA_SIZE (heap overflow) Signed-off-by: uTun --- src/transport_layer/etcp.c | 10 +++++++--- src/transport_layer/etcp.h | 1 + src/transport_layer/etcp_connections.c | 11 ++++++----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/transport_layer/etcp.c b/src/transport_layer/etcp.c index 3fe2f45a..2053d61c 100644 --- a/src/transport_layer/etcp.c +++ b/src/transport_layer/etcp.c @@ -1546,9 +1546,10 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) { // Process sections as per protocol.txt switch (type) { case ETCP_SECTION_ACK: { - if (len < 2) { len = 0; break; } + if (len < ETCP_ACK_BASE_SIZE) { len = 0; break; } int elm_cnt=data[1]; uint32_t till=data[2] | (data[3]<<8) | (data[4]<<16) | (data[5]<<24); + if ((int32_t)(till - etcp->next_tx_id) > 0) till = etcp->next_tx_id; // wrap-aware: пир не мог подтвердить больше, чем мы отправили uint16_t rx_dup_count_16 = data[6] | (data[7]<<8); uint16_t old_tx_dup_16 = etcp->tx_dup_count & 0xFFFF; int16_t diff = (int16_t)(rx_dup_count_16 - old_tx_dup_16); @@ -1567,7 +1568,10 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) { uint16_t dts=data[-ack_section_len+14+i*8] | (data[-ack_section_len+15+i*8]<<8); etcp_ack_recv(etcp, seq, ts, dts); } - while ((int32_t)(etcp->rx_ack_till-till)<0) { etcp->rx_ack_till++; etcp_ack_recv(etcp, etcp->rx_ack_till, -1, -1); }// подтверждаем всё по till + uint32_t ack_gap = 0; + while ((int32_t)(etcp->rx_ack_till-till)<0 && ack_gap < ETCP_ACK_GAP_MAX_PACKETS) { + etcp->rx_ack_till++; etcp_ack_recv(etcp, etcp->rx_ack_till, -1, -1); ack_gap++; + }// подтверждаем всё по till break; } case ETCP_SECTION_TIMESTAMP: { @@ -1794,7 +1798,7 @@ void etcp_conn_input(struct ETCP_DGRAM* pkt) { case ETCP_SECTION_FILLER: { if (len < FILLER_HDR_SIZE) { len = 0; break; } uint16_t fill_len = data[1] | (data[2] << 8); - if ((uint16_t)(fill_len + FILLER_HDR_SIZE) > len) { len = 0; break; } + if (fill_len > len - FILLER_HDR_SIZE) { len = 0; break; } data += FILLER_HDR_SIZE + fill_len; len -= FILLER_HDR_SIZE + fill_len; break; } diff --git a/src/transport_layer/etcp.h b/src/transport_layer/etcp.h index 6d498408..5676ae96 100644 --- a/src/transport_layer/etcp.h +++ b/src/transport_layer/etcp.h @@ -61,6 +61,7 @@ uint16_t get_current_timestamp(void); #define INFLIGHT_INITIAL_HASH_SIZE 1024 #define MAX_INFLIGHT_SIZE 16384 // максимальное число элементов в inflight приёмной очереди (для предотвращения атак) +#define ETCP_ACK_GAP_MAX_PACKETS 8192 // предел кумулятивного ACK за приём: 8192 × PACKET_DATA_SIZE(2048) = 16 MB (защита от DoS) #define ASM_BUF_MAX_SIZE (64 * 1024) // максимальный размер буфера сборки фрагментов // в этот список пакет добавляется когда перемещается из input_queue в input_send_q, при этом к пакету добавляется struct INFLIGHT_PACKET из inflight_pool. diff --git a/src/transport_layer/etcp_connections.c b/src/transport_layer/etcp_connections.c index 3da4127e..e89dc758 100644 --- a/src/transport_layer/etcp_connections.c +++ b/src/transport_layer/etcp_connections.c @@ -1366,11 +1366,11 @@ static int etcp_send_ping_raw(struct ETCP_DGRAM* dgram, struct ETCP_SOCKET* e_so return -1; } int len = dgram->data_len - dgram->noencrypt_len; - if (len < 0 || len > PACKET_DATA_SIZE - IP_UDP_OVERHEAD_V4) { - DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "ping packet data invalid len=%d", len); + uint8_t enc_buf[1600]; + if (len < 0 || len + (int)dgram->noencrypt_len + SC_ENCRYPT_OVERHEAD > (int)sizeof(enc_buf)) { + DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "ping packet too large len=%d ne=%d", len, dgram->noencrypt_len); return -1; } - uint8_t enc_buf[1600]; size_t enc_buf_len = 0; dgram->timestamp = get_current_timestamp(); dgram->flag_up = 1; @@ -1477,7 +1477,7 @@ int etcp_send_ping_to_socket(struct UTUN_INSTANCE* instance, struct ETCP_SOCKET* ctx->user_data_len = user_data_len; } memcpy(ctx->peer_pubkey, peer_pubkey_bin, SC_PUBKEY_SIZE); - struct ETCP_DGRAM* dgram = u_malloc(PACKET_DATA_SIZE); + struct ETCP_DGRAM* dgram = u_malloc(sizeof(struct ETCP_DGRAM) + PACKET_DATA_SIZE); if (!dgram) { if (ctx->user_data) u_free(ctx->user_data); u_free(ctx); @@ -1629,6 +1629,7 @@ static int handle_ping(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt, const uint64_t peer_id = be64toh(*(uint64_t*)(pkt->data + 2)); uint64_t nonce = be64toh(*(uint64_t*)(pkt->data + 10)); uint16_t ulen = be16toh(*(uint16_t*)(pkt->data + 18)); + if (ulen > pkt->data_len - 20) ulen = (uint16_t)(pkt->data_len - 20); const uint8_t* udata = (ulen > 0) ? (pkt->data + 20) : NULL; if ((flags & ETCP_PING_FLAG_SEND_RTT) && ulen >= 2) { @@ -1641,7 +1642,7 @@ static int handle_ping(struct ETCP_SOCKET* e_sock, struct ETCP_DGRAM* pkt, const if (e_sock->instance->topo_groups && topo_node_ping_request_cbk(e_sock->instance->topo_groups, peer_id)) pong_flags |= ETCP_PING_FLAG_WANT_RTT; - struct ETCP_DGRAM* resp = u_malloc(PACKET_DATA_SIZE); + struct ETCP_DGRAM* resp = u_malloc(sizeof(struct ETCP_DGRAM) + PACKET_DATA_SIZE); if (resp) { resp->link = NULL; resp->noencrypt_len = SC_PUBKEY_ENC_SIZE;