From: Willy Tarreau Date: Fri, 11 Sep 2026 09:35:09 +0000 (+0200) Subject: MINOR: htx: don't reserve a block when there is no free space left X-Git-Tag: v3.5-dev7~23 X-Git-Url: https://git.kaiwu.me/$%7BGITURL%7D$%7Bcid%7D?a=commitdiff_plain;h=b38e2483f1e256de7078c2d6531a1db657aeb7a3;p=haproxy.git MINOR: htx: don't reserve a block when there is no free space left 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. --- diff --git a/src/htx.c b/src/htx.c index 9f4acb49d..cf70ffe2a 100644 --- 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)