~ 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