]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: http-ana: check the cookie rewrite result before moving the offsets
authorOlivier Houchard <ohouchard@haproxy.com>
Thu, 6 Aug 2026 07:47:15 +0000 (09:47 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 7 Aug 2026 08:29:19 +0000 (10:29 +0200)
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 <delta> 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)
src/http_ana.c

index 30451da4c5c5d76926ee1872e081f5b0006af453..f113529750e11081dee12c3fc7145a46abf2eb16 100644 (file)
@@ -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);
 }
 
 /*