]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: quic: fix buffer overflow with sockaddr_in46
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 4 May 2026 08:22:42 +0000 (10:22 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Mon, 4 May 2026 08:49:49 +0000 (10:49 +0200)
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
src/quic_rx.c

index e398d384a8357b4fe7603bea892b0aa389972c79..8ffe0bafb6b5aa046c0047f2ea18554f9fae3975 100644 (file)
@@ -109,6 +109,40 @@ static inline int quic_increment_curr_handshake(struct listener *l)
        return next;
 }
 
+/* Initializes <dst> sockaddr_storage from <src> 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 */
 
index 6c1c2733bcd21946a8b6841aa553ad7e322cf459..45c33c4426408ade19f6ea3fca8a90e02b134260 100644 (file)
@@ -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);