~ chicken-core (chicken-5) 76a84c3c444631b62a875cfda682ea3ee322a447
commit 76a84c3c444631b62a875cfda682ea3ee322a447
Author: Peter Bex <peter@more-magic.net>
AuthorDate: Thu Mar 31 21:40:29 2016 +0200
Commit: Evan Hanson <evhan@foldling.org>
CommitDate: Sun Apr 3 17:18:40 2016 +1200
Fix invalid base handling of string->number
Before, we'd segfault when given absurd base arguments like 60,
depending on how the libc implemented this undefined behaviour. Now we
detect it early and bail out.
The handling of invalid values is slightly improved as well; we might
get a return value of 0 from strtod and strtol(l) on error: HUGE_VAL /
(LONG_)LONG_{MAX,MIN} are only returned on overflow/underflow, and the
errno might have a different value from ERANGE as pointed out by the
glibc manual page, even though the spec is silent about other errnos.
At least if the string couldn't be converted eptr will be mutated to
point to str. But otherwise, all bets are off.
The spec for strtod() even says:
"If the result underflows (7.12.1), the functions return a value whose
magnitude is no greater than the smallest normalized positive number in
the return type; whether errno acquires the value ERANGE is
implementation-defined"
Attempting to write C code: not even once...
Fixes #1272.
Signed-off-by: Evan Hanson <evhan@foldling.org>
diff --git a/NEWS b/NEWS
index 39334649..78e41d1c 100644
--- a/NEWS
+++ b/NEWS
@@ -49,6 +49,8 @@
with srfi-1 in the argument order of comparison procedures.
- Unit "lolevel": locative-ref has been fixed for locatives of u32
and s32vectors (thanks to Joerg Wittenberger for pointing this out).
+ - string->number now signals exceptions if passed a bad base instead
+ of segfaulting (#1272; reported by "Tilpner" on IRC).
- Tools
- A debugger is now available, known as "feathers", which allows
diff --git a/runtime.c b/runtime.c
index 8e89ea37..f11789ae 100644
--- a/runtime.c
+++ b/runtime.c
@@ -7695,6 +7695,9 @@ C_a_i_string_to_number(C_word **a, int c, C_word str, C_word radix0)
if(radix0 & C_FIXNUM_BIT) radix = C_unfix(radix0);
else barf(C_BAD_ARGUMENT_TYPE_BAD_BASE_ERROR, "string->number", radix0);
+ if (radix < 2 || radix > 36) /* Makes no sense and isn't supported */
+ barf(C_BAD_ARGUMENT_TYPE_BAD_BASE_ERROR, "string->number", radix0);
+
if(C_immediatep(str) || C_header_bits(str) != C_STRING_TYPE)
barf(C_BAD_ARGUMENT_TYPE_ERROR, "string->number", str);
@@ -7883,14 +7886,14 @@ C_regparm C_word C_fcall convert_string_to_number(C_char *str, int radix, C_word
errno = 0;
n = C_strtow(str, &eptr, radix);
- if(((n == C_LONG_MAX || n == C_LONG_MIN) && errno == ERANGE) || *eptr != '\0') {
+ if(((n == C_LONG_MAX || n == C_LONG_MIN) && errno != 0) || *eptr != '\0') {
if(radix != 10)
return from_n_nary(str, radix, flo) ? 2 : 0;
errno = 0;
fn = C_strtod(str, &eptr2);
- if(fn == HUGE_VAL && errno == ERANGE) return 0;
+ if((fn == HUGE_VAL && errno != 0) || fn == -HUGE_VAL) return 0;
else if(eptr2 == str) return 0;
else if(*eptr2 == '\0' || (eptr != eptr2 && !C_strncmp(eptr2, ".0", C_strlen(eptr2)))) {
*flo = fn;
Trap