From: Willy Tarreau Date: Mon, 27 Jul 2026 09:44:30 +0000 (+0200) Subject: BUG/MINOR: http-act: work on a copy of the sample in del-headers-bin X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=be9e357f903fbf1aaf6fef70f87f19d8224a3606;p=thirdparty%2Fhaproxy.git BUG/MINOR: http-act: work on a copy of the sample in del-headers-bin http_action_del_headers_bin() walks the varint-encoded list of header names directly in the sample expression result: p = b_orig(&hdrs_bin->data.u.str); end = b_tail(&hdrs_bin->data.u.str); and uses the names it decodes there while calling http_remove_header() on the HTX message in between. But the sample may perfectly well point into that very message: a string sample is cast to a binary one in place, so an expression such as "req.hdr(x-list)" hands out a pointer inside the header block it describes. http_remove_header() memmoves the payload of the block it shortens and marks the blocks it removes as unused, so

, and may then designate stale or recycled bytes and the loop goes on deleting names decoded from garbage. The header value must be a valid varint-encoded list for this to happen, which is possible since only NUL, CR and LF are rejected in an H1 header value. This is the exact same problem as the one fixed for the sibling actions by commit 43932db85 ("BUG/MEDIUM: http-act: Make a copy of the sample expr in (set/add)-headers-bin"), which this action was left out of. Let's copy the sample into a private chunk the same way before decoding it. This should be backported to 3.4, like the commit above. --- diff --git a/src/http_act.c b/src/http_act.c index 0bb7b05f7..02d8f4496 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -1927,6 +1927,7 @@ static enum act_return http_action_del_headers_bin(struct act_rule *rule, struct struct http_msg *msg = ((rule->from == ACT_F_HTTP_REQ) ? &s->txn.http->req : &s->txn.http->rsp); struct htx *htx = htxbuf(&msg->chn->buf); struct sample *hdrs_bin; + struct buffer *copy = NULL; char *p, *end; enum act_return ret = ACT_RET_CONT; struct ist n; @@ -1936,8 +1937,19 @@ static enum act_return http_action_del_headers_bin(struct act_rule *rule, struct if (!hdrs_bin) return ACT_RET_CONT; - p = b_orig(&hdrs_bin->data.u.str); - end = b_tail(&hdrs_bin->data.u.str); + /* The sample may point into the very HTX message we're about to modify + * (e.g. a header value), and http_remove_header() moves its payload + * around, which would leave our p/end/n pointers dangling. Work on a + * private copy to stay safe. + */ + copy = alloc_trash_chunk(); + if (!copy || b_data(&hdrs_bin->data.u.str) > b_size(copy)) + goto fail_rewrite; + memcpy(b_orig(copy), b_orig(&hdrs_bin->data.u.str), b_data(&hdrs_bin->data.u.str)); + b_set_data(copy, b_data(&hdrs_bin->data.u.str)); + + p = b_orig(copy); + end = b_tail(copy); while (p < end) { if (decode_varint(&p, end, &sz) == -1) goto fail_rewrite; @@ -1980,6 +1992,7 @@ static enum act_return http_action_del_headers_bin(struct act_rule *rule, struct ret = ACT_RET_ERR; leave: + free_trash_chunk(copy); return ret; fail_rewrite: