]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: htx: don't reserve a block when there is no free space left
authorWilly Tarreau <w@1wt.eu>
Fri, 11 Sep 2026 09:35:09 +0000 (11:35 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 11 Sep 2026 12:28:37 +0000 (14:28 +0200)
htx_reserve_max_data() computed the available room up front but tested
the empty-message case before testing that room:

    int32_t len = htx_free_data_space(htx);

    if (htx->head == -1)
            goto rsv_new_block;

    if (!len)
            return (struct htx_ret){.ret = 0, .blk = NULL};

So a message which is both empty and has no room at all went to
rsv_new_block with len == 0. That matters because htx_reserve_nxblk()
only rejects a reservation when "blksz > htx_free_data_space(htx)", so
a zero-sized one passes even when the free space is zero, and the
"Empty message" path then evaluates htx_get_blk(htx, 0), that is
htx->blocks + (htx->size - sizeof(struct htx_blk)). For any size below
sizeof(struct htx_blk) this subtraction underflows the uint32_t and
yields a pointer roughly 4 GB away, which is immediately written to.

The case that is trivial to reach in theory is the shared empty HTX
(htx_empty, size 0) that htxbuf() and htx_from_buf() return for an
unallocated buffer. No caller can do it today: the only caller,
h1_parse_full_contig_chunks(), returns earlier when the maximum is not
larger than sizeof(struct htx_blk), and the H1 mux allocates the
destination buffer before parsing into it. So this is not a fix for a
reachable bug, just one less trap for whoever adds the next caller.

Swapping the two tests is transparent for every other case: when the
message is empty but its buffer is allocated, the free space is
necessarily non-zero, so the early return cannot trigger and we still
reach rsv_new_block as before.

src/htx.c

index 9f4acb49de1bc29a1451ff08df76f37b4e3ea2a7..cf70ffe2a428b30c913d7e53368c831607be8db0 100644 (file)
--- a/src/htx.c
+++ b/src/htx.c
@@ -1024,12 +1024,12 @@ struct htx_ret htx_reserve_max_data(struct htx *htx)
        int32_t len = htx_free_data_space(htx);
        uint32_t flags = 0;
 
-       if (htx->head == -1)
-               goto rsv_new_block;
-
        if (!len)
                return (struct htx_ret){.ret = 0, .blk = NULL};
 
+       if (htx->head == -1)
+               goto rsv_new_block;
+
        /* get the tail and head block */
        tailblk = htx_get_tail_blk(htx);
        if (tailblk == NULL)