]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: chunk: Review chunks usage to not retrieve a large buffer by error
authorChristopher Faulet <cfaulet@haproxy.com>
Mon, 6 Jul 2026 14:34:24 +0000 (16:34 +0200)
committerChristopher Faulet <cfaulet@haproxy.com>
Wed, 8 Jul 2026 06:52:06 +0000 (08:52 +0200)
All calls to get_trash_chunk_sz() and alloc_trash_chunk_sz() were reviewed
to be sure we never retrieve a large chunk when it is not expected. Some
calls were not upgrade, but several calls now rely on get_best_trash_chunk()
and alloc_best_trash_chunk() functions.

The idea of the fix is to get a large buffer only when the original buffer
is already a large buffer and the expected size of data exceeds the size of
a regular buffer. This should prevent unexpected memory usage.

This commit relies on the previous one:

  MINOR: chunk: Add function to get a large/regular chunk depending on a buffer

Both must be backported to 3.4.

src/http_conv.c
src/http_fetch.c
src/sample.c

index c0a2f95360ab9f5c45dadf66172f729cd89432ce..5449f7adc590e71c417577341ae9735b99e7d703 100644 (file)
@@ -263,7 +263,7 @@ static int sample_conv_url_dec(const struct arg *args, struct sample *smp, void
          * before decoding.
         */
        if (smp->flags & SMP_F_CONST || smp->data.u.str.size <= smp->data.u.str.data) {
-               struct buffer *str = get_trash_chunk_sz(smp->data.u.str.data + 1);
+               struct buffer *str = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.data+1);
 
                if (!str)
                        return 0;
index 718819c7f1d9f92715d78ca243d06afd63a32182..01222c294e5e6930387ca8b95e97f0755841987b 100644 (file)
@@ -679,8 +679,8 @@ 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_trash_chunk_sz(htx->data);
-                                       if (!chunk_istcat(chk, body))
+                                       chk = get_best_trash_chunk(&chn->buf, htx->data);
+                                       if (!chk || !chunk_istcat(chk, body))
                                                return 0;
                                }
                                if (!chunk_istcat(chk, htx_get_blk_value(htx, blk)))
@@ -2122,8 +2122,8 @@ static int smp_fetch_body_param(const struct arg *args, struct sample *smp, cons
                                if (isttest(body)) {
                                        /* More than one DATA block we must use a trash */
                                        if (!chk) {
-                                               chk = get_trash_chunk_sz(htx->data);
-                                               if (!chunk_istcat(chk, body))
+                                               chk = get_best_trash_chunk(&chn->buf, htx->data);
+                                               if (!chk || !chunk_istcat(chk, body))
                                                        break;
                                        }
                                        if (!chunk_istcat(chk, htx_get_blk_value(htx, blk)))
index 1aaec60d98e630536d39751533cda40c84c99df9..c2d0472d7754f057136b43fb02f31a43e1a72233 100644 (file)
@@ -903,7 +903,7 @@ int smp_dup(struct sample *smp)
 
        case SMP_T_STR:
                buf = (smp->data.type == SMP_T_STR ? &smp->data.u.str : &smp->data.u.meth.str);
-               trash = get_trash_chunk_sz(buf->data+1);
+               trash = get_best_trash_chunk(buf, buf->data+1);
                if (!trash)
                        return 0;
                trash->data = buf->data;
@@ -1933,7 +1933,7 @@ static int sample_conv_base64url2bin(const struct arg *arg_p, struct sample *smp
 
 static int sample_conv_bin2base64(const struct arg *arg_p, struct sample *smp, void *private)
 {
-       struct buffer *trash = get_trash_chunk_sz((smp->data.u.str.data+2) * 4 / 3);
+       struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, (smp->data.u.str.data+2) * 4 / 3);
        int b64_len;
 
        if (!trash)
@@ -1953,7 +1953,7 @@ static int sample_conv_bin2base64(const struct arg *arg_p, struct sample *smp, v
 
 static int sample_conv_bin2base64url(const struct arg *arg_p, struct sample *smp, void *private)
 {
-       struct buffer *trash = get_trash_chunk_sz((smp->data.u.str.data+2) * 4 / 3);
+       struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, (smp->data.u.str.data+2) * 4 / 3);
        int b64_len;
 
        if (!trash)
@@ -2149,7 +2149,7 @@ static int sample_conv_be2hex_check(struct arg *args, struct sample_conv *conv,
  */
 static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *private)
 {
-       struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data * 2);
+       struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.size);
        int chunk_size = args[1].data.sint;
        const int last = args[2].data.sint ? smp->data.u.str.data - chunk_size + 1 : smp->data.u.str.data;
        int i;
@@ -2181,15 +2181,6 @@ static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *
                        trash->area[trash->data++] = hextab[(c >> 4) & 0xF];
                        trash->area[trash->data++] = hextab[c & 0xF];
                }
-
-               if (trash->data >= max_size) {
-                       struct buffer *trash2 = get_larger_trash_chunk(trash);
-
-                       if (!trash2)
-                               break;
-                       max_size += trash2->size - trash->size;
-                       trash = trash2;
-               }
        }
 
        smp->data.u.str = *trash;
@@ -2200,7 +2191,7 @@ static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void *
 
 static int sample_conv_bin2hex(const struct arg *arg_p, struct sample *smp, void *private)
 {
-       struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data*2);
+       struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.data*2);
        unsigned char c;
        int ptr = 0;
 
@@ -2220,7 +2211,7 @@ static int sample_conv_bin2hex(const struct arg *arg_p, struct sample *smp, void
 
 static int sample_conv_bin2base2(const struct arg *arg_p, struct sample *smp, void *private)
 {
-       struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data);
+       struct buffer *trash = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.data*8);
        unsigned char c;
        int ptr = 0;
        int bit = 0;
@@ -2232,14 +2223,6 @@ static int sample_conv_bin2base2(const struct arg *arg_p, struct sample *smp, vo
                c = smp->data.u.str.area[ptr++];
                 for (bit = 7; bit >= 0; bit--)
                         trash->area[trash->data++] = c & (1 << bit) ? '1' : '0';
-
-               if (trash->data >= trash->size - 8) {
-                       struct buffer *trash2 = get_larger_trash_chunk(trash);
-
-                       if (!trash2)
-                               break;
-                       trash = trash2;
-               }
        }
        smp->data.u.str = *trash;
        smp->data.type = SMP_T_STR;
@@ -2320,7 +2303,7 @@ static int sample_conv_reverse(const struct arg *arg_p, struct sample *smp, void
        int input_len = smp->data.u.str.data;
        int i;
 
-       trash = get_trash_chunk_sz(input_len + 1);
+       trash = get_best_trash_chunk(&smp->data.u.str, input_len+1);
        if (!trash)
                return 0;
 
@@ -2360,7 +2343,7 @@ static int sample_conv_reverse_dom(const struct arg *arg_p, struct sample *smp,
        if (input[0] == '.')
                return 0;
 
-       trash = get_trash_chunk_sz(input_len + 1);
+       trash = get_best_trash_chunk(&smp->data.u.str, input_len+1);
        if (!trash)
                return 0;
 
@@ -2855,7 +2838,9 @@ static int sample_conv_json(const struct arg *arg_p, struct sample *smp, void *p
 
        input_type = arg_p->data.sint;
 
-       temp = get_trash_chunk();
+       temp = get_best_trash_chunk(&smp->data.u.str, smp->data.u.str.size);
+       if (!temp)
+               return 0;
        temp->data = 0;
 
        p = smp->data.u.str.area;
@@ -2940,11 +2925,8 @@ static int sample_conv_json(const struct arg *arg_p, struct sample *smp, void *p
                }
 
                /* Check length */
-               if (temp->data + len > temp->size) {
-                       temp = get_larger_trash_chunk(temp);
-                       if (!temp)
-                               return 0;
-               }
+               if (temp->data + len > temp->size)
+                       return 0;
 
                /* Copy string. */
                memcpy(temp->area + temp->data, str, len);
@@ -3334,7 +3316,6 @@ static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void
        struct my_regex *reg = arg_p[0].data.reg;
        regmatch_t pmatch[MAX_MATCH];
        struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data);
-       struct buffer *tmp;
        struct buffer *output;
        int flag, max;
        int found;
@@ -3358,13 +3339,6 @@ static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void
                if (!found)
                        pmatch[0].rm_so = end - start;
 
-               /* If too many data to copy try to get a larger chunk */
-               if (pmatch[0].rm_so > b_room(trash)) {
-                       tmp = get_larger_trash_chunk(trash);
-                       if (tmp)
-                               trash = tmp;
-               }
-
                /* copy the heading non-matching part (which may also be the tail if nothing matches) */
                max = trash->size - trash->data;
                if (max && pmatch[0].rm_so > 0) {
@@ -3388,13 +3362,6 @@ static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void
                }
                output->data = max;
 
-               /* If too many data to copy try to get a larger chunk */
-               if (output->data > b_room(trash)) {
-                       tmp = get_larger_trash_chunk(trash);
-                       if (tmp)
-                               trash = tmp;
-               }
-
                /* replace the matching part */
                max = trash->size - trash->data;
                if (max) {
@@ -3411,13 +3378,6 @@ static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void
                if (start >= end)
                        break;
 
-               /* If too many data to copy try to get a larger chunk */
-               if (!b_room(trash)) {
-                       tmp = get_larger_trash_chunk(trash);
-                       if (tmp)
-                               trash = tmp;
-               }
-
                /* We have a special case for matches of length 0 (eg: "x*y*").
                 * These ones are considered to match in front of a character,
                 * so we have to copy that character and skip to the next one.
@@ -3787,7 +3747,8 @@ static int sample_conv_concat(const struct arg *arg_p, struct sample *smp, void
                var = ist2(b_orig(&tmp.data.u.str), b_data(&tmp.data.u.str));
        }
 
-       trash = alloc_trash_chunk_sz(smp->data.u.str.data + arg_p[0].data.str.data + istlen(var) + arg_p[2].data.str.data);
+       trash = alloc_best_trash_chunk(&smp->data.u.str,
+                                      smp->data.u.str.data + arg_p[0].data.str.data + istlen(var) + arg_p[2].data.str.data);
        if (!trash)
                return 0;
 
@@ -3871,7 +3832,8 @@ static int sample_conv_add_item(const struct arg *arg_p, struct sample *smp, voi
                var = ist2(b_orig(&tmp.data.u.str), b_data(&tmp.data.u.str));
        }
 
-       tmpbuf = alloc_trash_chunk_sz(smp->data.u.str.data + arg_p[0].data.str.data + istlen(var) + arg_p[2].data.str.data);
+       tmpbuf = alloc_best_trash_chunk(&smp->data.u.str,
+                                       smp->data.u.str.data + arg_p[0].data.str.data + istlen(var) + arg_p[2].data.str.data);
        if (!tmpbuf)
                return 0;
 
@@ -4603,7 +4565,6 @@ static int sample_check_json_query(struct arg *arg, struct sample_conv *conv,
  */
 static int sample_conv_json_query(const struct arg *args, struct sample *smp, void *private)
 {
-       struct buffer *trash = get_trash_chunk();
        const char *token; /* holds the temporary string from mjson_find */
        int token_size;    /* holds the length of <token> */
 
@@ -4623,6 +4584,7 @@ static int sample_conv_json_query(const struct arg *args, struct sample *smp, vo
 
                                return 1;
                        } else {
+                               struct buffer *trash = get_trash_chunk();
                                double double_val;
 
                                if (mjson_get_number(smp->data.u.str.area, smp->data.u.str.data, args[0].data.str.area, &double_val) == 0)
@@ -4645,16 +4607,14 @@ static int sample_conv_json_query(const struct arg *args, struct sample *smp, vo
 
                        return 1;
                case MJSON_TOK_STRING: {
+                       struct buffer *trash = get_trash_chunk_sz(token_size);
                        int len;
 
-               retry:
+                       if (!trash)
+                               return 0;
                        len = mjson_get_string(smp->data.u.str.area, smp->data.u.str.data, args[0].data.str.area, trash->area, trash->size);
 
                        if (len == -1) {
-                               trash = get_larger_trash_chunk(trash);
-                               if (trash)
-                                       goto retry;
-
                                /* invalid string */
                                return 0;
                        }
@@ -4666,13 +4626,12 @@ static int sample_conv_json_query(const struct arg *args, struct sample *smp, vo
                        return 1;
                }
                case MJSON_TOK_ARRAY: {
+                      struct buffer *trash = get_trash_chunk_sz(token_size);
+
+                       if (!trash)
+                               return 0;
                        // We copy the complete array, including square brackets into the return buffer
                        // result looks like: ["manage-account","manage-account-links","view-profile"]
-                      if (token_size > trash->size) {
-                              trash = get_larger_trash_chunk(trash);
-                              if (!trash)
-                                      return 0;
-                      }
                        trash->data = b_putblk(trash, token, token_size);
                        smp->data.u.str = *trash;
                        smp->data.type = SMP_T_STR;