From 63f853957af3ee062493bb3700f964ce456125b0 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Mon, 4 May 2026 10:22:42 +0200 Subject: [PATCH] BUG/MINOR: quic: fix buffer overflow with sockaddr_in46 New type sockaddr_in46 has been recently introduced. It serves as a union which can store either an IPv4 or IPv6 address. The objective is to reduce the storage size for QUIC datagrams which previously uses a sockaddr_storage field. On qc_new_conn(), source and destination addresses from the datagram are passed to the function as sockaddr_storage so that they are copied into the newly built quic_conn instance. However, the involved memcpy() is producing a buffer overflow as sockaddr_in46 is smaller than sockaddr_storage type. This patch fixes this by defining a new helper function in46un_to_addr(). This allows to convert safely sockaddr_in46 to a plain sockaddr type. The function is now used before invoking qc_new_conn(). Note that there is still other several places where union sockaddr_in46 is casted as sockaddr_storage type. However, these should be safe as in these cases sockaddr fields are accessed individually after checking ss_family. The memory issue only exists when plain memcpy is used. This bug was detected using ASAN. It generates the following traces when a QUIC connection is instantiated. ==37474==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7c3bb9a61100 at pc 0x5631f52c3946 bp 0x7ffc83e45b50 sp 0x7ffc83e45310 READ of size 128 at 0x7c3bb9a61100 thread T0 #0 0x5631f52c3945 in __asan_memcpy (/home/amaury/work/haproxy-quic-dev/haproxy+0x3ae945) (BuildId: a7ccfd74b7a71a869b8ff8d13f6dcde8c82c1487) #1 0x5631f55f9e34 in qc_new_conn /home/amaury/work/haproxy-quic-dev/src/quic_conn.c:1311:2 #2 0x5631f558d5c3 in quic_rx_pkt_retrieve_conn /home/amaury/work/haproxy-quic-dev/src/quic_rx.c:1875:10 #3 0x5631f558330b in quic_dgram_parse /home/amaury/work/haproxy-quic-dev/src/quic_rx.c:2463:29 #4 0x5631f5625da6 in quic_lstnr_dghdlr /home/amaury/work/haproxy-quic-dev/src/quic_sock.c:206:3 #5 0x5631f6a64173 in run_tasks_from_lists /home/amaury/work/haproxy-quic-dev/src/task.c:660:26 #6 0x5631f6a6ba1e in process_runnable_tasks /home/amaury/work/haproxy-quic-dev/src/task.c:913:9 #7 0x5631f5e984c3 in run_poll_loop /home/amaury/work/haproxy-quic-dev/src/haproxy.c:2982:3 #8 0x5631f5e9a715 in run_thread_poll_loop /home/amaury/work/haproxy-quic-dev/src/haproxy.c:3212:2 #9 0x5631f5e9f732 in main /home/amaury/work/haproxy-quic-dev/src/haproxy.c:3853:2 #10 0x7f2bba8276c0 (/usr/lib/libc.so.6+0x276c0) (BuildId: ca0db5ab57a36507d61bbcf4988d344974331f19) #11 0x7f2bba8277f8 in __libc_start_main (/usr/lib/libc.so.6+0x277f8) (BuildId: ca0db5ab57a36507d61bbcf4988d344974331f19) #12 0x5631f51be594 in _start (/home/amaury/work/haproxy-quic-dev/haproxy+0x2a9594) (BuildId: a7ccfd74b7a71a869b8ff8d13f6dcde8c82c1487) No need to backport. --- include/haproxy/quic_sock.h | 34 ++++++++++++++++++++++++++++++++++ src/quic_rx.c | 7 +++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/include/haproxy/quic_sock.h b/include/haproxy/quic_sock.h index e398d384a..8ffe0bafb 100644 --- a/include/haproxy/quic_sock.h +++ b/include/haproxy/quic_sock.h @@ -109,6 +109,40 @@ static inline int quic_increment_curr_handshake(struct listener *l) return next; } +/* Initializes sockaddr_storage from union sockaddr_in46 type. + * Only unspec, IPv4 and IPv6 addresses are supported. + */ +static inline void in46un_to_addr(const union sockaddr_in46 *src, + struct sockaddr_storage *dst) +{ + struct sockaddr_in *in; + struct sockaddr_in6 *in6; + + switch (((struct sockaddr_storage *)src)->ss_family) { + case AF_UNSPEC: + memset(dst, 0, sizeof(*dst)); + break; + + case AF_INET: + in = (struct sockaddr_in *)dst; + in->sin_family = AF_INET; + in->sin_addr = src->in4.sin_addr; + in->sin_port = src->in4.sin_port; + break; + + case AF_INET6: + in6 = (struct sockaddr_in6 *)dst; + in6->sin6_family = AF_INET6; + in6->sin6_addr = src->in6.sin6_addr; + in6->sin6_port = src->in6.sin6_port; + break; + + default: + ABORT_NOW(); + break; + } +} + #endif /* USE_QUIC */ #endif /* _HAPROXY_QUIC_SOCK_H */ diff --git a/src/quic_rx.c b/src/quic_rx.c index 6c1c2733b..45c33c442 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -1868,9 +1868,12 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt, pool_free(pool_head_quic_connection_id, conn_id); } else { + struct sockaddr_storage saddr, daddr; + in46un_to_addr(&pkt->saddr, &saddr); + in46un_to_addr(&dgram->daddr, &daddr); + qc = qc_new_conn(l, pkt, &token_odcid, NULL, conn_id, - (struct sockaddr_storage *)&dgram->daddr, - (struct sockaddr_storage *)&pkt->saddr); + &daddr, &saddr); if (qc == NULL) { quic_cid_delete(conn_id); /* Removes CID from global tree as it points to a NULL qc. */ pool_free(pool_head_quic_connection_id, conn_id);