]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: quic-be: drop long header packets with unsupported version
authorWilliam Dauchy <wdauchy@gmail.com>
Thu, 12 Nov 2026 16:50:46 +0000 (17:50 +0100)
committerFrederic Lecaille <flecaille@haproxy.com>
Thu, 12 Nov 2026 17:06:50 +0000 (18:06 +0100)
On backend connections, quic_rx_pkt_parse() accepts long header packets
with an unsupported version and Version Negotiation packets, as these
checks only apply to listeners. Such packets keep the
QUIC_PACKET_TYPE_UNKNOWN type, which maps to no encryption level, and hit
BUG_ON(!qel || !pktns) in qc_enc_level_alloc() from qc_try_rm_hp(), or a
NULL dereference without DEBUG_STRICT.

No malicious peer is required: a server which does not support the
version selected by haproxy responds with a Version Negotiation packet
(RFC 9000 6.1). A malicious server or an on-path attacker may also send a
packet with an unsupported version.

Drop these packets on backend connections, as required by RFC 9000 5.2.1,
since version negotiation is not supported on the backend side. To do
so, export quic_version_VN_reserved and compare the packet version to it
instead of testing a null version number.

This should be backported up to 3.3, where it was introduced by 43d88a44f
("MINOR: quic-be: Datagrams and packet parsing support").

Signed-off-by: William Dauchy <wdauchy@gmail.com>
include/haproxy/quic_conn-t.h
src/quic_rx.c

index 9870c9938eb9e29d251ca6a3b215bf082e154cb7..3b84b2db1e954e6e4527773482d23397bfcd1543 100644 (file)
@@ -222,6 +222,7 @@ extern const struct quic_version *preferred_version;
 extern const struct quic_version *quic_version_draft_29;
 extern const struct quic_version *quic_version_1;
 extern const struct quic_version *quic_version_2;
+extern const struct quic_version quic_version_VN_reserved;
 
 /* unused: 0x01 */
 /* Flag the packet number space as requiring an ACK frame to be sent. */
index 657269e6b200350a3b4a90e4f436692bf9840e6c..e295ab9284f2403a4c82daac6a480fb7074582ff 100644 (file)
@@ -2104,6 +2104,12 @@ static int quic_rx_pkt_parse(struct quic_conn *qc, struct quic_rx_packet *pkt,
                        goto drop_silent;
                }
 
+               if (!l && (!pkt->version || pkt->version == &quic_version_VN_reserved)) {
+                       TRACE_PROTO("RX unsupported version or VN packet on backend",
+                                   QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
+                       goto drop;
+               }
+
                if (pkt->type == QUIC_PACKET_TYPE_INITIAL) {
                        /* For Initial packets, and for servers (QUIC clients connections),
                         * there is no Initial connection IDs storage.