From 40760a8523e7a0a7a02d960b3888fc37dff14c05 Mon Sep 17 00:00:00 2001 From: Jacek Maksymowicz Date: Mon, 9 Sep 2024 14:03:35 +0200 Subject: [PATCH 1/2] stdlib: Improve strtoll, strtoull Add setting of errno on underflow, overflow and wrong base. Unify common code between strtoll and strtoull. Make code MISRA compliant. JIRA: RTOS-869 --- stdlib/strtoull.c | 174 ++++++++++++++++++---------------------------- 1 file changed, 69 insertions(+), 105 deletions(-) diff --git a/stdlib/strtoull.c b/stdlib/strtoull.c index 1ab79204..aca7283e 100644 --- a/stdlib/strtoull.c +++ b/stdlib/strtoull.c @@ -17,6 +17,8 @@ #include #include #include +#include + intmax_t strtoimax(const char *nptr, char **endptr, int base) { @@ -30,62 +32,82 @@ uintmax_t strtoumax(const char *nptr, char **endptr, int base) } -unsigned long long int strtoull(const char *nptr, char **endptr, int base) +static unsigned long long int strtoll_common(const char *nptr, char **endptr, int base, int isUnsigned) { const char *s; - unsigned long long acc; + unsigned long long int acc, cutoff; char c; - unsigned long long cutoff; int neg, any, cutlim; s = nptr; do { c = *s++; - } while (isspace((unsigned char)c)); + } while (isspace(c)); if (c == '-') { neg = 1; c = *s++; - } else { + } + else { neg = 0; - if (c == '+') + if (c == '+') { c = *s++; + } } any = 0; - acc = 0; + acc = 0uLL; - if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { + if (((base == 0) || (base == 16)) && (c == '0') && ((s[0] == 'x') || (s[0] == 'X')) && isxdigit(s[1])) { c = s[1]; s += 2; base = 16; - any = 1; } - if (base == 0) - base = c == '0' ? 8 : 10; + if (base == 0) { + base = (c == '0') ? 8 : 10; + } + + if ((base < 2) || (base > 36)) { + errno = EINVAL; + return 0uLL; + } + if (isUnsigned != 0) { + cutoff = ULLONG_MAX; + } + else { + cutoff = (neg != 0) ? -LLONG_MIN : LLONG_MAX; + } - if (base < 2 || base > 36) - return acc; + cutlim = (int)(cutoff % base); + cutoff /= base; - cutoff = ULONG_LONG_MAX / base; - cutlim = ULONG_LONG_MAX % base; - for ( ; ; c = *s++) { - if (c >= '0' && c <= '9') + for (;; c = *s++) { + if (c >= '0' && c <= '9') { c -= '0'; - else if (c >= 'A' && c <= 'Z') + } + else if (c >= 'A' && c <= 'Z') { c -= 'A' - 10; - else if (c >= 'a' && c <= 'z') + } + else if (c >= 'a' && c <= 'z') { c -= 'a' - 10; - else + } + else { break; + } - if (c >= base) + if (c >= base) { break; + } - if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + if (any < 0) { + continue; + } + + if ((acc > cutoff) || ((acc == cutoff) && (c > cutlim))) { any = -1; + } else { any = 1; acc *= base; @@ -93,100 +115,42 @@ unsigned long long int strtoull(const char *nptr, char **endptr, int base) } } - if (any < 0) { - acc = ULONG_LONG_MAX; - } else if (neg) - acc = -acc; - - if (endptr != NULL) - *endptr = (char *)(any ? s - 1 : nptr); - - return (acc); -} - - -long long int strtoll(const char *nptr, char **endptr, int base) -{ - const char *s; - long long int acc, cutoff; - int c; - int neg, any, cutlim; - - s = nptr; - do { - c = (unsigned char) *s++; - } while (isspace(c)); - - if (c == '-') { - neg = 1; - c = *s++; - } else { - neg = 0; - if (c == '+') - c = *s++; + if (any == 0) { + errno = EINVAL; } - - if ((base == 0 || base == 16) && c == '0' && (*s == 'x' || *s == 'X')) { - c = s[1]; - s += 2; - base = 16; - } - - if (base == 0) - base = c == '0' ? 8 : 10; - - cutoff = neg ? LONG_LONG_MIN : LONG_LONG_MAX; - cutlim = (int)(cutoff % base); - cutoff /= base; - if (neg) { - if (cutlim > 0) { - cutlim -= base; - cutoff += 1; + else if (any < 0) { + errno = ERANGE; + if (isUnsigned != 0) { + acc = ULLONG_MAX; + } + else { + acc = (neg != 0) ? -LLONG_MIN : LLONG_MAX; } - cutlim = -cutlim; + } + else if (neg != 0) { + acc = -acc; } - for (acc = 0, any = 0;; c = (unsigned char) *s++) { - if (isdigit(c)) - c -= '0'; - else if (isalpha(c)) - c -= isupper(c) ? 'A' - 10 : 'a' - 10; - else - break; + if (endptr != NULL) { + *endptr = (char *)((any != 0) ? (s - 1) : nptr); + } - if (c >= base) - break; + return acc; +} - if (any < 0) - continue; - if (neg) { - if (acc < cutoff || (acc == cutoff && c > cutlim)) { - any = -1; - acc = LONG_LONG_MIN; - } else { - any = 1; - acc *= base; - acc -= c; - } - } else { - if (acc > cutoff || (acc == cutoff && c > cutlim)) { - any = -1; - acc = LONG_LONG_MAX; - } else { - any = 1; - acc *= base; - acc += c; - } - } - } +unsigned long long int strtoull(const char *nptr, char **endptr, int base) +{ + return strtoll_common(nptr, endptr, base, 1); +} - if (endptr != 0) - *endptr = (char *)(any ? s - 1 : nptr); - return (acc); +long long int strtoll(const char *nptr, char **endptr, int base) +{ + return (long long int)strtoll_common(nptr, endptr, base, 0); } + long long int atoll(const char *str) { return strtoll(str, NULL, 10); From e2d34aebd7d701405ddb54f6f54985dc2f163338 Mon Sep 17 00:00:00 2001 From: Jacek Maksymowicz Date: Mon, 9 Sep 2024 14:20:08 +0200 Subject: [PATCH 2/2] stdlib: Fix strtol, strtoul Correct return value on signed long int overflow/underflow. Correct handling of "0x" prefix not followed by a hex digit. JIRA: RTOS-869 --- stdlib/strtoul.c | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/stdlib/strtoul.c b/stdlib/strtoul.c index d988d148..407859ce 100644 --- a/stdlib/strtoul.c +++ b/stdlib/strtoul.c @@ -19,7 +19,7 @@ #include -unsigned long int strtoul(const char *nptr, char **endptr, int base) +static unsigned long int strtoul_common(const char *nptr, char **endptr, int base, int isUnsigned) { unsigned long int cutoff, result = 0; int cutlim, t, width = 0, negative = 0; @@ -37,11 +37,12 @@ unsigned long int strtoul(const char *nptr, char **endptr, int base) sptr++; } - if ((base == 16 || base == 0) && sptr[0] == '0' && (sptr[1] | 0x20) == 'x') { + if ((base == 16 || base == 0) && sptr[0] == '0' && (sptr[1] | 0x20) == 'x' && isxdigit(sptr[2])) { base = 16; sptr += 2; } - else if (base == 0 && sptr[0] == '0') { + + if (base == 0 && sptr[0] == '0') { base = 8; } else if (base == 0) { @@ -53,8 +54,15 @@ unsigned long int strtoul(const char *nptr, char **endptr, int base) return 0; } - cutoff = (unsigned long int)(ULONG_MAX) / (unsigned long int)base; - cutlim = (unsigned long int)(ULONG_MAX) - (cutoff * (unsigned long int)base); + if (isUnsigned != 0) { + cutoff = ULONG_MAX; + } + else { + cutoff = (negative != 0) ? -LONG_MIN : LONG_MAX; + } + + cutlim = (int)(cutoff % base); + cutoff /= base; while (isalnum(*sptr) != 0) { t = *sptr - '0'; @@ -78,7 +86,12 @@ unsigned long int strtoul(const char *nptr, char **endptr, int base) if (width < 0) { errno = ERANGE; - result = ULONG_MAX; + if (isUnsigned != 0) { + result = ULONG_MAX; + } + else { + result = (negative != 0) ? LONG_MIN : LONG_MAX; + } } else if (width == 0) { errno = EINVAL; @@ -95,9 +108,15 @@ unsigned long int strtoul(const char *nptr, char **endptr, int base) } +unsigned long int strtoul(const char *nptr, char **endptr, int base) +{ + return strtoul_common(nptr, endptr, base, 1); +} + + long int strtol(const char *nptr, char **endptr, int base) { - return (long int)strtoul(nptr, endptr, base); + return (long int)strtoul_common(nptr, endptr, base, 0); }