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 <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-05-06 09:34:41 +09:00
parent 3cc60d31c6
commit c6836f494a
+11 -29
View File
@@ -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;
}