summaryrefslogtreecommitdiff
path: root/openbsd-compat/fmt_scaled.c
diff options
context:
space:
mode:
authorAlistair Delva <adelva@google.com>2020-08-20 16:14:23 -0700
committerAlistair Delva <adelva@google.com>2020-08-20 16:53:18 -0700
commitd9da10d147d633fdb6ec65e17ff4b8447419d83e (patch)
tree8f93e8fdc2907f141e0924910bfec26669819f0b /openbsd-compat/fmt_scaled.c
parent22246b08952d746a7cc5a292570636cf4277598f (diff)
parentecb2c02d994b3e21994f31a70ff911667c262f1f (diff)
Merge upstream-master into master
Commit ecb2c02d994b3e21994f31a70ff911667c262f1f upstream This nearly (but not quite) corresponds to V_8_3_P1; subsequent cherry-picks will correct this. Bug: 162492243 Change-Id: I3c079d86435b7c25aefff4538dc89a3002b1e25b
Diffstat (limited to 'openbsd-compat/fmt_scaled.c')
-rw-r--r--openbsd-compat/fmt_scaled.c51
1 files changed, 40 insertions, 11 deletions
diff --git a/openbsd-compat/fmt_scaled.c b/openbsd-compat/fmt_scaled.c
index edd682a4..2f76ef93 100644
--- a/openbsd-compat/fmt_scaled.c
+++ b/openbsd-compat/fmt_scaled.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: fmt_scaled.c,v 1.9 2007/03/20 03:42:52 tedu Exp $ */
+/* $OpenBSD: fmt_scaled.c,v 1.17 2018/05/14 04:39:04 djm Exp $ */
/*
* Copyright (c) 2001, 2002, 2003 Ian F. Darwin. All rights reserved.
@@ -69,7 +69,7 @@ static long long scale_factors[] = {
#define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */
-/** Convert the given input string "scaled" into numeric in "result".
+/* Convert the given input string "scaled" into numeric in "result".
* Return 0 on success, -1 and errno set on error.
*/
int
@@ -81,7 +81,7 @@ scan_scaled(char *scaled, long long *result)
long long scale_fact = 1, whole = 0, fpart = 0;
/* Skip leading whitespace */
- while (isascii(*p) && isspace(*p))
+ while (isascii((unsigned char)*p) && isspace((unsigned char)*p))
++p;
/* Then at most one leading + or - */
@@ -108,7 +108,8 @@ scan_scaled(char *scaled, long long *result)
* (but note that E for Exa might look like e to some!).
* Advance 'p' to end, to get scale factor.
*/
- for (; isascii(*p) && (isdigit(*p) || *p=='.'); ++p) {
+ for (; isascii((unsigned char)*p) &&
+ (isdigit((unsigned char)*p) || *p=='.'); ++p) {
if (*p == '.') {
if (fract_digits > 0) { /* oops, more than one '.' */
errno = EINVAL;
@@ -124,14 +125,30 @@ scan_scaled(char *scaled, long long *result)
/* ignore extra fractional digits */
continue;
fract_digits++; /* for later scaling */
+ if (fpart > LLONG_MAX / 10) {
+ errno = ERANGE;
+ return -1;
+ }
fpart *= 10;
+ if (i > LLONG_MAX - fpart) {
+ errno = ERANGE;
+ return -1;
+ }
fpart += i;
} else { /* normal digit */
if (++ndigits >= MAX_DIGITS) {
errno = ERANGE;
return -1;
}
+ if (whole > LLONG_MAX / 10) {
+ errno = ERANGE;
+ return -1;
+ }
whole *= 10;
+ if (i > LLONG_MAX - whole) {
+ errno = ERANGE;
+ return -1;
+ }
whole += i;
}
}
@@ -150,21 +167,28 @@ scan_scaled(char *scaled, long long *result)
/* Validate scale factor, and scale whole and fraction by it. */
for (i = 0; i < SCALE_LENGTH; i++) {
- /** Are we there yet? */
+ /* Are we there yet? */
if (*p == scale_chars[i] ||
- *p == tolower(scale_chars[i])) {
+ *p == tolower((unsigned char)scale_chars[i])) {
/* If it ends with alphanumerics after the scale char, bad. */
- if (isalnum(*(p+1))) {
+ if (isalnum((unsigned char)*(p+1))) {
errno = EINVAL;
return -1;
}
scale_fact = scale_factors[i];
+ /* check for overflow and underflow after scaling */
+ if (whole > LLONG_MAX / scale_fact ||
+ whole < LLONG_MIN / scale_fact) {
+ errno = ERANGE;
+ return -1;
+ }
+
/* scale whole part */
whole *= scale_fact;
- /* truncate fpart so it does't overflow.
+ /* truncate fpart so it doesn't overflow.
* then scale fractional part.
*/
while (fpart >= LLONG_MAX / scale_fact) {
@@ -181,7 +205,9 @@ scan_scaled(char *scaled, long long *result)
return 0;
}
}
- errno = ERANGE;
+
+ /* Invalid unit or character */
+ errno = EINVAL;
return -1;
}
@@ -196,7 +222,7 @@ fmt_scaled(long long number, char *result)
unsigned int i;
unit_type unit = NONE;
- abval = (number < 0LL) ? -number : number; /* no long long_abs yet */
+ abval = llabs(number);
/* Not every negative long long has a positive representation.
* Also check for numbers that are just too darned big to format
@@ -220,12 +246,15 @@ fmt_scaled(long long number, char *result)
fract = (10 * fract + 512) / 1024;
/* if the result would be >= 10, round main number */
- if (fract == 10) {
+ if (fract >= 10) {
if (number >= 0)
number++;
else
number--;
fract = 0;
+ } else if (fract < 0) {
+ /* shouldn't happen */
+ fract = 0;
}
if (number == 0)