]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: http-fetch: fix a NULL channel dereference in smp_fetch_body()
authorWilly Tarreau <w@1wt.eu>
Mon, 27 Jul 2026 08:01:30 +0000 (10:01 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 27 Jul 2026 13:28:37 +0000 (15:28 +0200)
smp_fetch_body() is also used from the health-check context, where the HTX
message comes from <check->bi> and where there is no channel at all: for
"res.body", SMP_RES_CHN(smp) evaluates to NULL because smp->strm is NULL. The
function is aware of this and guards the channel a few lines below:

if (!finished && (check || (chn && !channel_full(chn, global.tune.maxrewrite) &&

but the trash chunk retrieval isn't:

chk = get_best_trash_chunk(&chn->buf, htx->data);

so as soon as a check response is made of more than one HTX DATA block, a
bogus address close to NULL is dereferenced and the worker crashes. Let's use
the check input buffer when there is no channel, as done elsewhere.

Note that no reproducer could be built: with the h1 mux the successive DATA
blocks of a check response always end up appended to the same HTX block, so
the multi-block case was not reachable in practice. The dereference is
nevertheless plainly invalid.

This is a regression introduced by commit ac37158a6 ("BUG/MEDIUM: chunk:
Review chunks usage to not retrieve a large buffer by error") in 3.5-dev2,
which replaced get_trash_chunk_sz(htx->data), that did not look at the
channel, with get_best_trash_chunk(&chn->buf, htx->data). smp_fetch_body_param()
received the same change but is only reachable through "req.body_param", for
which <check> is always NULL and thus <chn> always set, so it is not affected.

It must be backported wherever the commit above was backported, so 3.4 and
above.

src/http_fetch.c

index 2441a2969b9d881b35c231e5dcff2cc8b70411c1..6133983c16a763e9e06bbf5583273cd2e7b95f36 100644 (file)
@@ -685,7 +685,10 @@ static int smp_fetch_body(const struct arg *args, struct sample *smp, const char
                                /* More than one DATA block we must use a trash */
                                if (!chk) {
                                        smp->flags &= ~SMP_F_CONST;
-                                       chk = get_best_trash_chunk(&chn->buf, htx->data);
+                                       /* <chn> is NULL in the health-check context,
+                                        * where the message comes from <check->bi>
+                                        */
+                                       chk = get_best_trash_chunk((chn ? &chn->buf : &check->bi), htx->data);
                                        if (!chk || !chunk_istcat(chk, body))
                                                return 0;
                                }