]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: shctx: Use the next block when data exactly filled a block
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 3 Feb 2026 06:46:22 +0000 (07:46 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 18 Feb 2026 08:44:15 +0000 (09:44 +0100)
When the hot list was removed in 3.0, a regression was introduced.
Theorically, it is possible to override data in a block when new data are
appended. It happens when data are copied. If the data size is a multiple of
the block size, all data are copied and the last used block is full. But
instead of saving a reference on the next block as the restart point for the
next copies, we keep a reference on the last full one. On the next read, we
reuse this block and old data are crushed. To hit the bug, no new blocks
should be reserved between the two data copy attempts.

Concretely, for now, it seems not possible to hit the bug. But with a block
size set to 1024, if more than 1024 bytes are reseved, with a first copy of
1024 bytes and a second one with remaining data, data in the first block
will be crushed.

So to fix the bug, the reference of the last block used to write data (which
is in fact the next one to use to perform the next copy) is only updated
when a block is full. In that case the next block is used.

This patch should be backported as far as 3.0 after a period of observation.

src/shctx.c

index 931bc4f5f68d1ac0b204d869c3013a40640a4636..fb1611396f02e11f4906915189c6e11cb42e6ef0 100644 (file)
@@ -206,9 +206,12 @@ int shctx_row_data_append(struct shared_context *shctx, struct shared_block *fir
                data += remain;
                len -= remain;
                first->len += remain; /* update len in the head of the row */
-               first->last_append = block;
 
                block = LIST_ELEM(block->list.n, struct shared_block*, list);
+
+               /* Update <last_append> block only if the previous one is full */
+               if (start + remain == shctx->block_size)
+                       first->last_append = block;
        } while (block != first);
 
        return len;