BUG/MINOR: mux-quic: do not send too big MAX_STREAMS ID

QUIC stream IDs are expressed as QUIC variable integer which cover the
range for 0 to 2^62 - 1. As such, it is forbidden to send an ID for
MAX_STREAMS flow-control frame which would allow to overcome this value.

This patch fixes MAX_STREAMS emission to ensure sent value is valid.
This also ensures that the peer cannot open a stream with an invalid ID
as this would cause a flow-control violation instead.

This must be backported up to 2.6.

(cherry picked from commit f3c75a52df29247e5d502344127d42efb2c12b82)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit cf920ac69b67eef6e714ac7ebc7e9ddc55554e52)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit e1b921ede8714aa4385961d0f70a8114268a2773)
Signed-off-by: Willy Tarreau <w@1wt.eu>
diff --git a/src/mux_quic.c b/src/mux_quic.c
index aca72bf..7ef7446 100644
--- a/src/mux_quic.c
+++ b/src/mux_quic.c
@@ -13,6 +13,7 @@
 #include <haproxy/qmux_http.h>
 #include <haproxy/qmux_trace.h>
 #include <haproxy/quic_conn.h>
+#include <haproxy/quic_enc.h>
 #include <haproxy/quic_frame.h>
 #include <haproxy/quic_sock.h>
 #include <haproxy/quic_stream.h>
@@ -627,6 +628,9 @@
 	/* Only stream ID not already opened can be used. */
 	BUG_ON(id < *largest);
 
+	/* MAX_STREAMS emission must not allowed too big stream ID. */
+	BUG_ON(*largest > QUIC_VARINT_8_BYTE_MAX);
+
 	while (id >= *largest) {
 		const char *str = *largest < id ? "initializing intermediary remote stream" :
 		                                  "initializing remote stream";
@@ -1441,6 +1445,8 @@
 	return 1;
 }
 
+#define QUIC_MAX_STREAMS_MAX_ID (1ULL<<60)
+
 /* Signal the closing of remote stream with id <id>. Flow-control for new
  * streams may be allocated for the peer if needed.
  */
@@ -1451,8 +1457,28 @@
 	TRACE_ENTER(QMUX_EV_QCS_END, qcc->conn);
 
 	if (quic_stream_is_bidi(id)) {
+		/* RFC 9000 4.6. Controlling Concurrency
+		 *
+		 * If a max_streams transport parameter or a MAX_STREAMS frame is
+		 * received with a value greater than 260, this would allow a maximum
+		 * stream ID that cannot be expressed as a variable-length integer; see
+		 * Section 16. If either is received, the connection MUST be closed
+		 * immediately with a connection error of type TRANSPORT_PARAMETER_ERROR
+		 * if the offending value was received in a transport parameter or of
+		 * type FRAME_ENCODING_ERROR if it was received in a frame; see Section
+		 * 10.2.
+		 */
+		if (qcc->lfctl.ms_bidi == QUIC_MAX_STREAMS_MAX_ID) {
+			TRACE_DATA("maximum streams value reached", QMUX_EV_QCC_SEND, qcc->conn);
+			goto out;
+		}
+
 		++qcc->lfctl.cl_bidi_r;
-		if (qcc->lfctl.cl_bidi_r > qcc->lfctl.ms_bidi_init / 2) {
+		/* MAX_STREAMS needed if closed streams value more than twice
+		 * the initial window or reaching the stream ID limit.
+		 */
+		if (qcc->lfctl.cl_bidi_r > qcc->lfctl.ms_bidi_init / 2 ||
+		    qcc->lfctl.cl_bidi_r + qcc->lfctl.ms_bidi == QUIC_MAX_STREAMS_MAX_ID) {
 			TRACE_DATA("increase max stream limit with MAX_STREAMS_BIDI", QMUX_EV_QCC_SEND, qcc->conn);
 			frm = qc_frm_alloc(QUIC_FT_MAX_STREAMS_BIDI);
 			if (!frm) {
@@ -1477,8 +1503,8 @@
 		 */
 	}
 
+ out:
 	TRACE_LEAVE(QMUX_EV_QCS_END, qcc->conn);
-
 	return 0;
 
  err: