]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: sample: fix info leak in regsub when exp_replace fails
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 9 Apr 2026 11:06:43 +0000 (13:06 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Fri, 10 Apr 2026 07:33:37 +0000 (09:33 +0200)
exp_replace() returns int and returns -1 when the back-reference
expansion overflows the output buffer (regex.c:51). output->data is
size_t, so -1 becomes SIZE_MAX. There was no error check.

The subsequent comparisons interpret SIZE_MAX as a huge length:
"output->data > b_room(trash)" tries to grow trash, then
"max > output->data" is false so max stays at trash->size, and
memcpy(trash, output->area, trash->size) copies the full chunk.
output->area is a pool_alloc()'d chunk that is NOT zeroed; the
bytes after the partial exp_replace output are stale data from a
prior pool user (request headers, response bodies from the same
worker thread).

Trigger with a backreference whose expansion exceeds bufsize:

    http-request set-header X %[req.hdr(In),regsub('(.+)','\1\1')]

and a request with In: of ~9000 bytes. The X header sent to the
backend then contains ~9KB of stale heap data.

With tune.bufsize.large set, get_larger_trash_chunk() upgrades trash
and the memcpy reads up to ~50KB past the (smaller) output->area
allocation.

http_ana.c:2728 and http_act.c:551 already check exp_replace() for
-1; this call site was missed when backreferences were added.

This patch must be backported to all stable versions.

src/sample.c

index 22172a33fe707746ba36aaa81e6b7431941e405e..d741716f87ee1e1ccb4e81a304ea22777a7b0743 100644 (file)
@@ -3298,7 +3298,12 @@ static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void
                if (!output)
                        break;
 
-               output->data = exp_replace(output->area, output->size, start, arg_p[1].data.str.area, pmatch);
+               max = exp_replace(output->area, output->size, start, arg_p[1].data.str.area, pmatch);
+               if ((int)max < 0) {
+                       free_trash_chunk(output);
+                       break;
+               }
+               output->data = max;
 
                /* If too many data to copy try to get a larger chunk */
                if (output->data > b_room(trash)) {