merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

commit 8b9954e2236f05114310154302125698935ee4dd
parent ca44fed55ec43230afc1cc1adeb17d8202b10991
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sat, 10 Oct 2026 23:14:14 +0200

enforce caps on array lengths

Diffstat:
Msrc/util/order_choice_parse.c | 20++++++++++++++++++++
Msrc/util/order_parse.c | 22++++++++++++++++++++++
Msrc/util/product_parse.c | 23+++++++++++++++++++----
3 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/src/util/order_choice_parse.c b/src/util/order_choice_parse.c @@ -31,6 +31,15 @@ /** + * Maximum number of entries we accept in the @e choices array of a v1 + * order. The array is grown to its JSON length before any entry is + * parsed, so the length must be bounded independently of the maximum + * upload size. + */ +#define MAX_CHOICES 1024 + + +/** * Parse JSON order choice input. * * @param[in] root JSON object containing choice input @@ -250,6 +259,17 @@ parse_order_choices ( GNUNET_break_op (0); return GNUNET_SYSERR; } + if (MAX_CHOICES < json_array_size (root)) + { + /* 'choices' is untrusted input; reject rather than abort in + GNUNET_xmalloc_() (which asserts above 40 MiB). */ + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Too many choices in order (%u, at most %u allowed)\n", + (unsigned int) json_array_size (root), + (unsigned int) MAX_CHOICES); + return GNUNET_SYSERR; + } *choices = NULL; *choices_len = 0; GNUNET_array_grow (*choices, diff --git a/src/util/order_parse.c b/src/util/order_parse.c @@ -31,6 +31,17 @@ /** + * Maximum number of entries we accept in the @e products array of an + * order. The array is allocated from its JSON length before any entry + * is parsed, and phase_merge_inventory() of POST /private/orders puts + * @e products_len + 1 words on the stack, so the length must be bounded + * independently of the maximum upload size. Same limit as the + * MAX_PRODUCTS that bounds the inventory_products of the same request. + */ +#define MAX_PRODUCTS 1024 + + +/** * Parse v0-specific fields of @a input JSON into @a order. * * @param[in] input the JSON order terms @@ -265,6 +276,17 @@ TALER_MERCHANT_order_parse (json_t *input) if (NULL != products) { order->products_len = json_array_size (products); + if (MAX_PRODUCTS < order->products_len) + { + /* 'products' is untrusted input; reject rather than abort in + GNUNET_xmalloc_() (which asserts above 40 MiB). */ + GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Too many products in order (%u, at most %u allowed)\n", + (unsigned int) order->products_len, + (unsigned int) MAX_PRODUCTS); + goto cleanup; + } if (0 != order->products_len) { size_t i; diff --git a/src/util/product_parse.c b/src/util/product_parse.c @@ -24,6 +24,16 @@ #include <gnunet/gnunet_json_lib.h> #include <taler/taler_json_lib.h> + +/** + * Maximum number of entries we accept in the @e prices array of a + * product. The array is allocated from its JSON length before any + * entry is parsed, so the length must be bounded independently of the + * maximum upload size. + */ +#define MAX_PRICES 1024 + + /** * Parse the given unit quantity string @a s and store the result in @a q. * @@ -253,12 +263,17 @@ TALER_MERCHANT_parse_product_sold (const json_t *pj, size_t i; json_t *price_i; - if (len >= UINT_MAX) + if (MAX_PRICES < len) { - /* 'prices' is untrusted network input; reject rather than abort. - (The zero-length case is excluded by the guard above, so we - never call GNUNET_new_array(0, ...).) */ + /* 'prices' is untrusted network input; reject rather than abort + in GNUNET_xmalloc_() (which asserts above 40 MiB). (The + zero-length case is excluded by the guard above, so we never + call GNUNET_new_array(0, ...).) */ GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Too many prices for product (%u, at most %u allowed)\n", + (unsigned int) len, + (unsigned int) MAX_PRICES); return GNUNET_SYSERR; } r->prices_length = (unsigned int) len;