From 5bcfd33063a460ed6efafa840e354e383d4252c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20L=C3=A9caille?= Date: Fri, 4 Mar 2022 15:44:21 +0100 Subject: [PATCH] BUG/MAJOR: quic: Wrong quic_max_available_room() returned value Around limits for QUIC integer encoding, this functions could return wrong values which lead to qc_build_frms() to prepare wrong CRYPTO (less chances) or STREAM frames (more chances). qc_do_build_pkt() could build wrong packets with bad CRYPTO/STREAM frames which could not be decoded by the peer. In such a case ngtcp2 closes the connection with an ENCRYPTION_ERROR error in a transport CONNECTION_CLOSE frame. --- include/haproxy/xprt_quic.h | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/include/haproxy/xprt_quic.h b/include/haproxy/xprt_quic.h index 5e66b5480..48568e9a6 100644 --- a/include/haproxy/xprt_quic.h +++ b/include/haproxy/xprt_quic.h @@ -331,7 +331,8 @@ static inline uint64_t quic_max_int(size_t sz) * buffer with as size for a data field of bytes prefixed by its QUIC * variable-length (may be 0). * Also put in <*len_sz> the size of this QUIC variable-length. - * So after returning from this function we have : <*len_sz> + = . + * So after returning from this function we have : <*len_sz> + <= + * (<*len_sz> = { max(i), i + ret <= }) . */ static inline size_t max_available_room(size_t sz, size_t *len_sz) { @@ -346,8 +347,22 @@ static inline size_t max_available_room(size_t sz, size_t *len_sz) *len_sz = quic_int_getsize(ret); /* Difference between the two sizes. Note that >= <*len_sz>. */ diff = sz_sz - *len_sz; - if (unlikely(diff > 0)) - ret += diff; + if (unlikely(diff > 0)) { + /* Let's try to take into an account remaining bytes. + * + * <----------------> + * <--------------><--------> +----> + * | + * +---------------------------+-----------.... + * <--------------------------------> + */ + size_t max_int = quic_max_int_by_size(*len_sz); + + if (max_int + *len_sz <= sz) + ret = max_int; + else + ret = sz - diff; + } return ret; }