From c6836f494a9d45759c803e6fecee67e62c39016e Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 6 May 2026 09:34:41 +0900 Subject: [PATCH] fp_uscale.c: fix uninitialized *fp when exponent is malformed mrb_read_float jumped past the *fp assignment via `goto done` when the exponent had no digits (e.g., "5e", "5e+"). It returned TRUE without setting *fp, leaving the caller (mrb_str_to_dbl etc.) to return whatever was on the stack. MSan flagged this; on most runs the uninitialized read happens to yield 0.0, so the bug is silently incorrect rather than crashing. Refactor the finalization (compute res from d, final_p, sign, etc.) to run once after the optional-exponent block. The malformed-exponent case now falls through using the mantissa-only `final_p = trunc - dp`, producing the same result strtod gives for the same input ("5e" -> 5.0 with endp at 'e'). Float("5e") still raises because mrb_str_len_to_dbl rejects trailing characters under badcheck. Reported by OSS-Fuzz (MSan). Co-authored-by: Claude --- src/fp_uscale.c | 40 +++++++++++----------------------------- 1 file changed, 11 insertions(+), 29 deletions(-) diff --git a/src/fp_uscale.c b/src/fp_uscale.c index b01de8fc2..0b92859e9 100644 --- a/src/fp_uscale.c +++ b/src/fp_uscale.c @@ -1466,7 +1466,10 @@ mrb_read_float(const char *str, char **endp, double *fp) return FALSE; } - /* exponent */ + /* exponent (optional). On malformed exponent ("5e", "5e+"), keep `a` + pointing at the 'e' so the caller's *endp reflects where parsing + stopped, and fall through using the mantissa-only result. */ + int final_p = trunc - dp; if ((*p | 32) == 'e') { int e = 0; int exp_sign = 1; @@ -1474,37 +1477,17 @@ mrb_read_float(const char *str, char **endp, double *fp) if (*p == '-') { exp_sign = -1; p++; } else if (*p == '+') p++; - if (!ISDIGIT(*p)) goto done; - - while (ISDIGIT(*p)) { - if (e < 10000) e = e * 10 + (*p - '0'); - p++; + if (ISDIGIT(*p)) { + while (ISDIGIT(*p)) { + if (e < 10000) e = e * 10 + (*p - '0'); + p++; + } + final_p += e * exp_sign; + a = p; } - - { - int final_p = e * exp_sign + trunc - dp; - double res; - if (d == 0) { - res = 0.0; - } - else if (final_p > 308) { - res = HUGE_VAL; - } - else if (final_p < -342 - nd) { - res = 0.0; - } - else { - res = parse_decimal(d, final_p); - } - if (sign < 0) res = -res; - *fp = res; - } - a = p; - goto done; } { - int final_p = trunc - dp; double res; if (d == 0) { res = 0.0; @@ -1522,7 +1505,6 @@ mrb_read_float(const char *str, char **endp, double *fp) *fp = res; } -done: if (endp) *endp = (char*)a; return TRUE; }