]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MINOR: shctx: fix shctx_row_data_get() when offset exceeds a block
authorMaxime Henrion <mhenrion@haproxy.com>
Wed, 8 Jul 2026 16:37:07 +0000 (12:37 -0400)
committerWilly Tarreau <w@1wt.eu>
Fri, 10 Jul 2026 14:42:35 +0000 (16:42 +0200)
The loop advanced the block pointer only after the copy, but skipped
blocks preceding <offset> with a continue, which bypassed that advance.
As a result any read starting past the first block copied nothing.

This notably affects conditional requests: should_send_notmodified_response()
reads the stored ETag at entry->etag_offset, so a cached response whose
ETag header sits beyond the first block fails revalidation and is served
in full (200) instead of 304.

Rewrite the loop as a for() so the block pointer always advances,
regardless of whether the current block is copied or skipped.

This should be backported as far as 2.6.

src/shctx.c

index fb1611396f02e11f4906915189c6e11cb42e6ef0..cb580b14eecf50b8076334117256b2ab58d1fa35 100644 (file)
@@ -226,30 +226,22 @@ int shctx_row_data_append(struct shared_context *shctx, struct shared_block *fir
 int shctx_row_data_get(struct shared_context *shctx, struct shared_block *first,
                        unsigned char *dst, int offset, int len)
 {
-       int count = 0, size = 0, start = -1;
+       int count, size, start = -1;
        struct shared_block *block;
 
        /* can't copy more */
        if (len > first->len)
                len = first->len;
 
-       block = first;
-       count = 0;
-       /* Pass through the blocks to copy them */
-       do {
-               if (count >= first->block_count  || len <= 0)
-                       break;
-
-               count++;
-               /* continue until we are in right block
-                  corresponding to the offset */
-               if (count < offset / shctx->block_size + 1)
+       /* Walk the blocks, skipping those that precede the one holding <offset>. */
+       for (block = first, count = 0; count < first->block_count && len > 0;
+            block = LIST_ELEM(block->list.n, struct shared_block *, list), count++) {
+               if (count < offset / shctx->block_size)
                        continue;
 
-               /* on the first block, data won't possibly began at offset 0 */
+               /* the first copied block may not start at its beginning */
                if (start == -1)
-                       start = offset - (count - 1) * shctx->block_size;
-
+                       start = offset - count * shctx->block_size;
                BUG_ON(start < 0);
 
                /* size can be lower than a block when copying the last block */
@@ -260,9 +252,7 @@ int shctx_row_data_get(struct shared_context *shctx, struct shared_block *first,
                dst += size;
                len -= size;
                start = 0;
-
-               block = LIST_ELEM(block->list.n, struct shared_block*, list);
-       } while (block != first);
+       }
        return len;
 }