From: Christopher Faulet Date: Mon, 9 Feb 2026 10:15:02 +0000 (+0100) Subject: MEDIUM: sample: Get chunks with a size dependent on input data when necessary X-Git-Tag: v3.4-dev5~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dfc40854138b4d4a1e8a6094f5a0f1cca3cc1f39;p=thirdparty%2Fhaproxy.git MEDIUM: sample: Get chunks with a size dependent on input data when necessary The function used to duplicate a sample was update to support large buffers. In addition several converters were also reviewed to deal with large buffers. For instance, base64 encoding and decoding must use chunks of the same size than the sample. For some of them, a retry is performed to enlarge the chunk is possible. TODO: Review reg_sub, concat and add_item to get larger chunk if necessary --- diff --git a/src/http_conv.c b/src/http_conv.c index 30056036c..a46432dc0 100644 --- a/src/http_conv.c +++ b/src/http_conv.c @@ -263,7 +263,10 @@ 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(); + struct buffer *str = get_trash_chunk_sz(smp->data.u.str.data); + + if (!str) + return 0; memcpy(str->area, smp->data.u.str.area, smp->data.u.str.data); smp->data.u.str.area = str->area; smp->data.u.str.size = str->size; @@ -334,10 +337,13 @@ static int sample_conv_url_enc(const struct arg *args, struct sample *smp, void *private) { enum encode_type enc_type; - struct buffer *trash = get_trash_chunk(); + struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); long *encode_map; char *ret; + if (!trash) + return 0; + enc_type = ENC_QUERY; enc_type = args->data.sint; diff --git a/src/sample.c b/src/sample.c index a35d997f7..18f7f1c26 100644 --- a/src/sample.c +++ b/src/sample.c @@ -876,7 +876,9 @@ int smp_dup(struct sample *smp) __fallthrough; case SMP_T_STR: - trash = get_trash_chunk(); + trash = get_trash_chunk_sz(smp->data.u.str.data+1); + if (!trash) + return 0; trash->data = smp->data.type == SMP_T_STR ? smp->data.u.str.data : smp->data.u.meth.str.data; if (trash->data > trash->size - 1) @@ -890,7 +892,9 @@ int smp_dup(struct sample *smp) break; case SMP_T_BIN: - trash = get_trash_chunk(); + trash = get_trash_chunk_sz(smp->data.u.str.data); + if (!trash) + return 0; trash->data = smp->data.u.str.data; if (trash->data > trash->size) trash->data = trash->size; @@ -1857,9 +1861,11 @@ static int smp_check_debug(struct arg *args, struct sample_conv *conv, static int sample_conv_base642bin(const struct arg *arg_p, struct sample *smp, void *private) { - struct buffer *trash = get_trash_chunk(); + struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); int bin_len; + if (!trash) + return 0; trash->data = 0; bin_len = base64dec(smp->data.u.str.area, smp->data.u.str.data, trash->area, trash->size); @@ -1875,9 +1881,11 @@ static int sample_conv_base642bin(const struct arg *arg_p, struct sample *smp, v static int sample_conv_base64url2bin(const struct arg *arg_p, struct sample *smp, void *private) { - struct buffer *trash = get_trash_chunk(); + struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); int bin_len; + if (!trash) + return 0; trash->data = 0; bin_len = base64urldec(smp->data.u.str.area, smp->data.u.str.data, trash->area, trash->size); @@ -1893,9 +1901,11 @@ 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(); + struct buffer *trash = get_trash_chunk_sz((smp->data.u.str.data+2) * 4 / 3); int b64_len; + if (!trash) + return 0; trash->data = 0; b64_len = a2base64(smp->data.u.str.area, smp->data.u.str.data, trash->area, trash->size); @@ -1911,9 +1921,11 @@ 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(); + struct buffer *trash = get_trash_chunk_sz((smp->data.u.str.data+2) * 4 / 3); int b64_len; + if (!trash) + return 0; trash->data = 0; b64_len = a2base64url(smp->data.u.str.area, smp->data.u.str.data, trash->area, trash->size); @@ -2012,15 +2024,18 @@ static int sample_conv_2dec_check(struct arg *args, struct sample_conv *conv, */ static int sample_conv_2dec(const struct arg *args, struct sample *smp, void *private, int be) { - struct buffer *trash = get_trash_chunk(); + struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); const int last = args[2].data.sint ? smp->data.u.str.data - args[1].data.sint + 1 : smp->data.u.str.data; - int max_size = trash->size - 2; + int max_size; int i; int start; int ptr = 0; unsigned long long number; char *pos; + if (!trash) + return 0; + max_size = trash->size - 2; trash->data = 0; while (ptr < last && trash->data <= max_size) { @@ -2102,7 +2117,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(); + struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); 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; @@ -2110,6 +2125,8 @@ static int sample_conv_be2hex(const struct arg *args, struct sample *smp, void * int ptr = 0; unsigned char c; + if (!trash) + return 0; trash->data = 0; if (args[0].data.str.data == 0 && args[2].data.sint == 0) chunk_size = smp->data.u.str.data; @@ -2130,6 +2147,15 @@ 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; @@ -2140,10 +2166,12 @@ 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(); + struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data*2); unsigned char c; int ptr = 0; + if (!trash) + return 0; trash->data = 0; while (ptr < smp->data.u.str.data && trash->data <= trash->size - 2) { c = smp->data.u.str.area[ptr++]; @@ -2158,16 +2186,26 @@ 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(); + struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); unsigned char c; int ptr = 0; int bit = 0; + if (!trash) + return 0; trash->data = 0; while (ptr < smp->data.u.str.data && trash->data <= trash->size - 8) { 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; @@ -2787,8 +2825,11 @@ static int sample_conv_json(const struct arg *arg_p, struct sample *smp, void *p } /* Check length */ - if (temp->data + len > temp->size) - return 0; + if (temp->data + len > temp->size) { + temp = get_larger_trash_chunk(temp); + if (!temp) + return 0; + } /* Copy string. */ memcpy(temp->area + temp->data, str, len); @@ -3177,11 +3218,14 @@ static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void char *start, *end; struct my_regex *reg = arg_p[0].data.reg; regmatch_t pmatch[MAX_MATCH]; - struct buffer *trash = get_trash_chunk(); + struct buffer *trash = get_trash_chunk_sz(smp->data.u.str.data); + struct buffer *tmp; struct buffer *output; int flag, max; int found; + if (!trash) + return 0; start = smp->data.u.str.area; end = start + smp->data.u.str.data; @@ -3199,6 +3243,13 @@ 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) { @@ -3217,6 +3268,13 @@ static int sample_conv_regsub(const struct arg *arg_p, struct sample *smp, void output->data = exp_replace(output->area, output->size, start, arg_p[1].data.str.area, pmatch); + /* 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) { @@ -3233,6 +3291,13 @@ 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. @@ -3591,9 +3656,18 @@ static int sample_conv_concat(const struct arg *arg_p, struct sample *smp, void { struct buffer *trash; struct sample tmp; + struct ist var = IST_NULL; int max; - trash = alloc_trash_chunk(); + + smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); + if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) && + (sample_casts[tmp.data.type][SMP_T_STR] == c_none || + sample_casts[tmp.data.type][SMP_T_STR](&tmp))) { + 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); if (!trash) return 0; @@ -3615,24 +3689,15 @@ static int sample_conv_concat(const struct arg *arg_p, struct sample *smp, void trash->area[trash->data] = 0; } - /* append second string (variable) if it's found and we can turn it - * into a string. - */ - smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); - if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) && - (sample_casts[tmp.data.type][SMP_T_STR] == c_none || - sample_casts[tmp.data.type][SMP_T_STR](&tmp))) { - - max = tmp.data.u.str.data; - if (max > trash->size - 1 - trash->data) - max = trash->size - 1 - trash->data; + /* append second string (variable) if it's found */ + max = istlen(var); + if (max > trash->size - 1 - trash->data) + max = trash->size - 1 - trash->data; - if (max) { - memcpy(trash->area + trash->data, tmp.data.u.str.area, - max); - trash->data += max; - trash->area[trash->data] = 0; - } + if (max) { + memcpy(trash->area + trash->data, istptr(var), max); + trash->data += max; + trash->area[trash->data] = 0; } /* append third string */ @@ -3675,10 +3740,18 @@ static int sample_conv_add_item(const struct arg *arg_p, struct sample *smp, voi { struct buffer *tmpbuf; struct sample tmp; + struct ist var = IST_NULL; size_t max; - int var_available; - tmpbuf = alloc_trash_chunk(); + /* Check if variable is found and we can turn into a string. */ + smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); + if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) && + (sample_casts[tmp.data.type][SMP_T_STR] == c_none || + sample_casts[tmp.data.type][SMP_T_STR](&tmp))) { + 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); if (!tmpbuf) return 0; @@ -3689,19 +3762,10 @@ static int sample_conv_add_item(const struct arg *arg_p, struct sample *smp, voi memcpy(tmpbuf->area, smp->data.u.str.area, tmpbuf->data); tmpbuf->area[tmpbuf->data] = 0; - /* Check if variable is found and we can turn into a string. */ - var_available = 0; - smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); - if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) && - (sample_casts[tmp.data.type][SMP_T_STR] == c_none || - sample_casts[tmp.data.type][SMP_T_STR](&tmp))) - var_available = 1; - /* Append delimiter only if input is not empty and either * the variable or the suffix are not empty */ - if (smp->data.u.str.data && ((var_available && tmp.data.u.str.data) || - arg_p[2].data.str.data)) { + if (smp->data.u.str.data && ((istlen(var) && tmp.data.u.str.data) || arg_p[2].data.str.data)) { max = arg_p[0].data.str.data; if (max > tmpbuf->size - 1 - tmpbuf->data) max = tmpbuf->size - 1 - tmpbuf->data; @@ -3714,13 +3778,13 @@ static int sample_conv_add_item(const struct arg *arg_p, struct sample *smp, voi } /* Append variable contents if variable is found and turned into string. */ - if (var_available) { - max = tmp.data.u.str.data; + if (istlen(var)) { + max = istlen(var); if (max > tmpbuf->size - 1 - tmpbuf->data) max = tmpbuf->size - 1 - tmpbuf->data; if (max) { - memcpy(tmpbuf->area + tmpbuf->data, tmp.data.u.str.area, max); + memcpy(tmpbuf->area + tmpbuf->data, istptr(var), max); tmpbuf->data += max; tmpbuf->area[tmpbuf->data] = 0; } @@ -4462,9 +4526,14 @@ static int sample_conv_json_query(const struct arg *args, struct sample *smp, vo case MJSON_TOK_STRING: { int len; + retry: 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; } @@ -4478,6 +4547,11 @@ static int sample_conv_json_query(const struct arg *args, struct sample *smp, vo case MJSON_TOK_ARRAY: { // 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; @@ -4607,7 +4681,7 @@ static int sample_conv_jwt_verify(const struct arg *args, struct sample *smp, vo * smp_dup) which would end up erasing the contents of the 'smp' input * buffer. */ - input = alloc_trash_chunk(); + input = alloc_trash_chunk_sz(smp->data.u.str.data); if (!input) return 0; alg = alloc_trash_chunk(); @@ -4660,7 +4734,7 @@ static int sample_conv_jwt_verify_cert(const struct arg *args, struct sample *sm * smp_dup) which would end up erasing the contents of the 'smp' input * buffer. */ - input = alloc_trash_chunk(); + input = alloc_trash_chunk_sz(smp->data.u.str.data); if (!input) return 0; alg = alloc_trash_chunk(); @@ -4710,7 +4784,7 @@ static int sample_conv_jwt_member_query(const struct arg *args, struct sample *s { struct jwt_item items[JWT_ELT_MAX] = { { 0 } }; unsigned int item_num = member + 1; /* We don't need to tokenize the full token */ - struct buffer *decoded_header = get_trash_chunk(); + struct buffer *decoded_header; int retval = 0; int ret; @@ -4719,6 +4793,10 @@ static int sample_conv_jwt_member_query(const struct arg *args, struct sample *s if (item_num < member + 1) goto end; + decoded_header = get_trash_chunk_sz(items[member].length); + if (!decoded_header) + goto end; + ret = base64urldec(items[member].start, items[member].length, decoded_header->area, decoded_header->size); if (ret == -1)