commit 03c6aab2ec52b1ff413eb305c233a69e07df961c
parent 74292cba96c89561d4b6f7fa7076143153efde93
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 12 Jul 2026 09:42:58 +0200
bound VLA stack arrays
Diffstat:
4 files changed, 66 insertions(+), 26 deletions(-)
diff --git a/src/lib/merchant_api_get-private-kyc.c b/src/lib/merchant_api_get-private-kyc.c
@@ -183,6 +183,13 @@ parse_kyc (struct TALER_MERCHANT_GetPrivateKycHandle *kyc,
}
num_limits += json_array_size (jlimits);
num_kycauths += json_array_size (jkycauths);
+ if ( (num_limits > MAX_KYC) ||
+ (num_kycauths > MAX_KYC) )
+ {
+ /* Bound the stack VLAs declared below by an untrusted response. */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
}
{
diff --git a/src/lib/merchant_api_get-private-statistics-amount-SLUG.c b/src/lib/merchant_api_get-private-statistics-amount-SLUG.c
@@ -162,6 +162,12 @@ parse_intervals_and_buckets_amt (
}
total_amounts += json_array_size (amounts_arr);
}
+ if (total_amounts > MAX_STATISTICS * (size_t) MAX_STATISTICS)
+ {
+ /* Bound the stack VLA declared below by an untrusted response. */
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
}
{
struct TALER_Amount glob_amt_arr[GNUNET_NZL (total_amounts)];
diff --git a/src/lib/merchant_api_post-orders-ORDER_ID-pay.c b/src/lib/merchant_api_post-orders-ORDER_ID-pay.c
@@ -38,6 +38,13 @@
#include <donau/donau_json_lib.h>
/**
+ * Maximum number of exchange base URLs we accept in an (untrusted)
+ * response before considering it malformed. Bounds the stack VLA used
+ * to parse the array.
+ */
+#define MAX_EXCHANGE_BASE_URLS 1024
+
+/**
* Handle for a POST /orders/$ORDER_ID/pay operation.
*/
struct TALER_MERCHANT_PostOrdersPayHandle
@@ -414,36 +421,47 @@ handle_pay_finished (void *cls,
}
{
size_t alen = json_array_size (ebus);
- const char *ebua[GNUNET_NZL (alen)];
- size_t idx;
- json_t *jebu;
- bool ok = true;
- GNUNET_assert (alen <= UINT_MAX);
- json_array_foreach (ebus, idx, jebu)
+ if (alen > MAX_EXCHANGE_BASE_URLS)
+ {
+ /* Bound the stack VLA below by an untrusted response. */
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint = "too many exchange_base_urls in response";
+ break;
+ }
{
- ebua[idx] = json_string_value (jebu);
- if (NULL == ebua[idx])
+ const char *ebua[GNUNET_NZL (alen)];
+ size_t idx;
+ json_t *jebu;
+ bool ok = true;
+
+ json_array_foreach (ebus, idx, jebu)
{
- GNUNET_break_op (0);
- pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
- pr.hr.http_status = 0;
- pr.hr.hint
- = "non-string value in exchange_base_urls in response";
- ok = false;
- break;
+ ebua[idx] = json_string_value (jebu);
+ if (NULL == ebua[idx])
+ {
+ GNUNET_break_op (0);
+ pr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
+ pr.hr.http_status = 0;
+ pr.hr.hint
+ = "non-string value in exchange_base_urls in response";
+ ok = false;
+ break;
+ }
}
+ if (! ok)
+ break;
+ pr.details.unavailable_for_legal_reasons.num_exchanges
+ = (unsigned int) alen;
+ pr.details.unavailable_for_legal_reasons.exchanges
+ = ebua;
+ poph->cb (poph->cb_cls,
+ &pr);
+ TALER_MERCHANT_post_orders_pay_cancel (poph);
+ return;
}
- if (! ok)
- break;
- pr.details.unavailable_for_legal_reasons.num_exchanges
- = (unsigned int) alen;
- pr.details.unavailable_for_legal_reasons.exchanges
- = ebua;
- poph->cb (poph->cb_cls,
- &pr);
- TALER_MERCHANT_post_orders_pay_cancel (poph);
- return;
}
}
break;
diff --git a/src/lib/merchant_api_post-private-orders.c b/src/lib/merchant_api_post-private-orders.c
@@ -33,6 +33,14 @@
/**
+ * Maximum number of exchange rejections we allow in a response before
+ * we consider the (untrusted) reply malformed. Bounds the stack VLA
+ * used to parse the array.
+ */
+#define MAX_EXCHANGE_REJECTIONS 1024
+
+
+/**
* Handle for a POST /private/orders operation.
*/
struct TALER_MERCHANT_PostPrivateOrdersHandle
@@ -284,7 +292,8 @@ handle_post_orders_finished (void *cls,
{
unsigned int rej_len = (unsigned int) json_array_size (jer);
- if (json_array_size (jer) == (size_t) rej_len)
+ if ( (json_array_size (jer) == (size_t) rej_len) &&
+ (rej_len <= MAX_EXCHANGE_REJECTIONS) )
{
struct TALER_MERCHANT_ExchangeRejectionDetail rejs[
GNUNET_NZL (rej_len)];