From: Willy Tarreau Date: Thu, 6 Aug 2026 07:19:58 +0000 (+0200) Subject: BUG/MEDIUM: hpack: encode long methods and schemes using the long form X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1eb9f46e9031e531ce52b6b13a60ae1fdb3a65c6;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: hpack: encode long methods and schemes using the long form hpack_encode_scheme() and hpack_encode_method() document that they're limited to 127 chars since they only rely on hpack_encode_short_idx() for literals, but the H2 mux doesn't check this. This results in only the bytes modulo 256 being advertised on a backend connection. As such the remaining bytes will be confused with other HPACK opcodes. Note that in practice, the ability to exploit this to inject headers from a front H1 connection is very limited due to the strict alphabet enabled in schemes and methods which limits usable codes to literal headers with indexing for absent pseudo-headers (i.e. no :scheme, :method, :path, possibly one :authority when none is provided, but it must then match the host), and whose value will be of 43 chars minimum. The real impact in practice is to provoke protocol errors and cause shared H2 backend connections to be abruptly closed in environments using http-reuse always. Let's simply make both encoders fall back to hpack_encode_long_idx() for value larger than 127 bytes, like hpack_encode_path() does. The bug has been present since 1.9 with commit 39c80ebff ("MINOR: hpack: provide a function to encode an HTTP method"). Reported-by: Claude (ANT-2026-9SVV6W3Q) This fix must be backported to all stable versions. --- diff --git a/include/haproxy/hpack-enc.h b/include/haproxy/hpack-enc.h index 063d5c62e..01246dd22 100644 --- a/include/haproxy/hpack-enc.h +++ b/include/haproxy/hpack-enc.h @@ -202,10 +202,9 @@ static inline int hpack_encode_str_status(struct buffer *out, unsigned int statu /* Tries to encode a :method pseudo-header with the method in , which * also exists as a string in , into the aligned buffer . Returns * non-zero on success or 0 on failure (buffer full). The caller is responsible - * for ensuring that the string matches , that it's smaller than 127 - * bytes, and that the buffer is aligned. If is unknown then using - * HTTP_METH_OTHER will lead to the string being encoded as a literal. It's - * inlined because it's easily optimizable. + * for ensuring that the string matches and that the buffer is aligned. + * If is unknown then using HTTP_METH_OTHER will lead to the string + * being encoded as a literal. It's inlined because it's easily optimizable. */ static inline int hpack_encode_method(struct buffer *out, enum http_meth_t meth, struct ist str) { @@ -213,18 +212,19 @@ static inline int hpack_encode_method(struct buffer *out, enum http_meth_t meth, out->area[out->data++] = 0x82; // indexed field : idx[02]=(":method", "GET") else if (out->data < out->size && meth == HTTP_METH_POST) out->area[out->data++] = 0x83; // indexed field : idx[03]=(":method", "POST") - else + else if (str.len < 127) return hpack_encode_short_idx(out, 2, str); // name=":method" (idx 2) + else + return hpack_encode_long_idx(out, 2, str); // name=":method" (idx 2) return 1; } /* Tries to encode a :scheme pseudo-header with the scheme in , into * the aligned buffer . Returns non-zero on success or 0 on failure * (buffer full). Only "http" and "https" are recognized and handled as indexed - * values, others are turned into short literals. The caller is responsible for - * ensuring that the scheme is smaller than 127 bytes, and that the buffer is - * aligned. Normally the compiler will detect constant strings in the comparison - * if the code remains inlined. + * values, others are turned into literals. The caller is responsible for + * ensuring that the buffer is aligned. Normally the compiler will detect + * constant strings in the comparison if the code remains inlined. */ static inline int hpack_encode_scheme(struct buffer *out, struct ist scheme) { @@ -232,8 +232,10 @@ static inline int hpack_encode_scheme(struct buffer *out, struct ist scheme) out->area[out->data++] = 0x87; // indexed field : idx[07]=(":scheme", "https") else if (out->data < out->size && isteq(scheme, ist("http"))) out->area[out->data++] = 0x86; // indexed field : idx[06]=(":scheme", "http") - else + else if (scheme.len < 127) return hpack_encode_short_idx(out, 6, scheme); // name=":scheme" (idx 6) + else + return hpack_encode_long_idx(out, 6, scheme); // name=":scheme" (idx 6) return 1; }