From d638a3ea46d9f124c01861da93bcbd024f66e972 Mon Sep 17 00:00:00 2001 From: Peter Bex Date: Thu, 31 Mar 2016 21:40:29 +0200 Subject: [PATCH] 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... --- NEWS | 2 ++ runtime.c | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 3933464..78e41d1 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 8e89ea3..f11789a 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; -- 2.1.4