From: Olivier Houchard Date: Thu, 6 Aug 2026 07:47:15 +0000 (+0200) Subject: BUG/MEDIUM: http-ana: check the cookie rewrite result before moving the offsets X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=26cd7ec2e3478bb2e5163babcb9647de1bd7b1c7;p=thirdparty%2Fhaproxy.git BUG/MEDIUM: http-ana: check the cookie rewrite result before moving the offsets In the "rewrite" and "prefix" cookie modes, http_manage_server_side_cookies() calls http_replace_header_value() and ignores its return value. When the expansion doesn't fit it returns 0 and leaves ctx.value untouched, yet the code still computes from srv->cklen, advances next and hdr_end by it, and in "prefix" mode writes "val_beg[srv->cklen] = COOKIE_DELIM" over data that was never moved. The delimiter thus lands in whatever follows the cookie and the response is forwarded with that byte corrupted, while the cookie is left unprefixed, silently losing persistence. The maxrewrite reserve normally covers the expansion, so this needs the inserted string to be larger than it. Reproduced both with a 1100-byte server cookie name and the default tune.maxrewrite, and with a 61-byte one and "tune.maxrewrite 16": the Set-Cookie comes back untouched and a '~' appears in the middle of the next header's value. Let's check the return value like every other rewrite site does: on failure nothing is touched, the failed_rewrites counters are incremented and the parsing of that response's cookies stops there. This has been there since the HTX cookie handling was added in 1.9 by commit fcda7c685 ("MINOR: proto_htx: Add functions to manage cookies on HTX messages"). It may be backported to all stable versions. Reported-by: Claude (ANT-2026-FVC9MZEJ) --- diff --git a/src/http_ana.c b/src/http_ana.c index 30451da4c..f11352975 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -3862,7 +3862,8 @@ static void http_manage_server_side_cookies(struct stream *s, struct channel *re ctx.value = ist2(val_beg, val_end - val_beg); ctx.lws_before = ctx.lws_after = 0; - http_replace_header_value(htx, &ctx, ist2(srv->cookie, srv->cklen), 0); + if (!http_replace_header_value(htx, &ctx, ist2(srv->cookie, srv->cklen), 0)) + goto rewrite_err; delta = srv->cklen - (val_end - val_beg); sliding = (ctx.value.ptr - val_beg); hdr_beg += sliding; @@ -3880,7 +3881,8 @@ static void http_manage_server_side_cookies(struct stream *s, struct channel *re int sliding, delta; ctx.value = ist2(val_beg, 0); ctx.lws_before = ctx.lws_after = 0; - http_replace_header_value(htx, &ctx, ist2(srv->cookie, srv->cklen + 1), 0); + if (!http_replace_header_value(htx, &ctx, ist2(srv->cookie, srv->cklen + 1), 0)) + goto rewrite_err; delta = srv->cklen + 1; sliding = (ctx.value.ptr - val_beg); hdr_beg += sliding; @@ -3899,6 +3901,13 @@ static void http_manage_server_side_cookies(struct stream *s, struct channel *re */ } } + return; + + rewrite_err: + if (s->be_tgcounters) + _HA_ATOMIC_INC(&s->be_tgcounters->failed_rewrites); + if (s->sess->fe_tgcounters) + _HA_ATOMIC_INC(&s->sess->fe_tgcounters->failed_rewrites); } /*