|
|
|
|
@@ -0,0 +1,85 @@
|
|
|
|
|
From 06675db4bf234ed17e14305f1d59259d2fe78b06 Mon Sep 17 00:00:00 2001
|
|
|
|
|
From: Willy Tarreau <w@1wt.eu>
|
|
|
|
|
Date: Mon, 29 Sep 2025 18:34:11 +0200
|
|
|
|
|
Subject: [PATCH] BUG/CRITICAL: mjson: fix possible DoS when parsing numbers
|
|
|
|
|
|
|
|
|
|
Mjson comes with its own strtod() implementation for portability
|
|
|
|
|
reasons and probably also because many generic strtod() versions as
|
|
|
|
|
provided by operating systems do not focus on resource preservation
|
|
|
|
|
and may call malloc(), which is not welcome in a parser.
|
|
|
|
|
|
|
|
|
|
The strtod() implementation used here apparently originally comes from
|
|
|
|
|
https://gist.github.com/mattn/1890186 and seems to have purposely
|
|
|
|
|
omitted a few parts that were considered as not needed in this context
|
|
|
|
|
(e.g. skipping white spaces, or setting errno). But when subject to the
|
|
|
|
|
relevant test cases of the designated file above, the current function
|
|
|
|
|
provides the same results.
|
|
|
|
|
|
|
|
|
|
The aforementioned implementation uses pow() to calculate exponents,
|
|
|
|
|
but mjson authors visibly preferred not to introduce a libm dependency
|
|
|
|
|
and replaced it with an iterative loop in O(exp) time. The problem is
|
|
|
|
|
that the exponent is not bounded and that this loop can take a huge
|
|
|
|
|
amount of time. There's even an issue already opened on mjson about
|
|
|
|
|
this: https://github.com/cesanta/mjson/issues/59. In the case of
|
|
|
|
|
haproxy, fortunately, the watchdog will quickly stop a runaway process
|
|
|
|
|
but this remains a possible denial of service.
|
|
|
|
|
|
|
|
|
|
A first approach would consist in reintroducing pow() like in the
|
|
|
|
|
original implementation, but if haproxy is built without Lua nor
|
|
|
|
|
51Degrees, -lm is not used so this will not work everywhere.
|
|
|
|
|
|
|
|
|
|
Anyway here we're dealing with integer exponents, so an easy alternate
|
|
|
|
|
approach consists in simply using shifts and squares, to compute the
|
|
|
|
|
exponent in O(log(exp)) time. Not only it doesn't introduce any new
|
|
|
|
|
dependency, but it turns out to be even faster than the generic pow()
|
|
|
|
|
(85k req/s per core vs 83.5k on the same machine).
|
|
|
|
|
|
|
|
|
|
This must be backported as far as 2.4, where mjson was introduced.
|
|
|
|
|
|
|
|
|
|
Many thanks to Oula Kivalo for reporting this issue.
|
|
|
|
|
|
|
|
|
|
CVE-2025-11230 was assigned to this issue.
|
|
|
|
|
---
|
|
|
|
|
src/mjson.c | 14 ++++++++++++--
|
|
|
|
|
1 file changed, 12 insertions(+), 2 deletions(-)
|
|
|
|
|
|
|
|
|
|
diff --git a/src/mjson.c b/src/mjson.c
|
|
|
|
|
index 73b7a5797f..2a4106bbd8 100644
|
|
|
|
|
--- a/src/mjson.c
|
|
|
|
|
+++ b/src/mjson.c
|
|
|
|
|
@@ -767,11 +767,13 @@ static double mystrtod(const char *str, char **end) {
|
|
|
|
|
|
|
|
|
|
/* exponential part */
|
|
|
|
|
if ((*p == 'E') || (*p == 'e')) {
|
|
|
|
|
+ double exp, f;
|
|
|
|
|
int i, e = 0, neg = 0;
|
|
|
|
|
p++;
|
|
|
|
|
if (*p == '-') p++, neg++;
|
|
|
|
|
if (*p == '+') p++;
|
|
|
|
|
while (is_digit(*p)) e = e * 10 + *p++ - '0';
|
|
|
|
|
+ i = e;
|
|
|
|
|
if (neg) e = -e;
|
|
|
|
|
#if 0
|
|
|
|
|
if (d == 2.2250738585072011 && e == -308) {
|
|
|
|
|
@@ -785,8 +787,16 @@ static double mystrtod(const char *str, char **end) {
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
- for (i = 0; i < e; i++) d *= 10;
|
|
|
|
|
- for (i = 0; i < -e; i++) d /= 10;
|
|
|
|
|
+ /* calculate f = 10^i */
|
|
|
|
|
+ exp = 10;
|
|
|
|
|
+ f = 1;
|
|
|
|
|
+ while (i > 0) {
|
|
|
|
|
+ if (i & 1) f *= exp;
|
|
|
|
|
+ exp *= exp;
|
|
|
|
|
+ i >>= 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (e > 0) d *= f;
|
|
|
|
|
+ else if (e < 0) d /= f;
|
|
|
|
|
a = p;
|
|
|
|
|
} else if (p > str && !is_digit(*(p - 1))) {
|
|
|
|
|
a = str;
|
|
|
|
|
--
|
|
|
|
|
2.51.0
|
|
|
|
|
|