]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
sync fmt_scaled.c with OpenBSD
authorDamien Miller <djm@mindrot.org>
Sat, 11 Mar 2017 23:48:14 +0000 (10:48 +1100)
committerDamien Miller <djm@mindrot.org>
Sat, 11 Mar 2017 23:48:14 +0000 (10:48 +1100)
revision 1.13
date: 2017/03/11 23:37:23;  author: djm;  state: Exp;  lines: +14 -1;  commitid: jnFKyHkB3CEiEZ2R;
fix signed integer overflow in scan_scaled. Found by Nicolas Iooss
using AFL against ssh_config. ok deraadt@ millert@
----------------------------
revision 1.12
date: 2013/11/29 19:00:51;  author: deraadt;  state: Exp;  lines: +6 -5;
fairly simple unsigned char casts for ctype
ok krw
----------------------------
revision 1.11
date: 2012/11/12 14:07:20;  author: halex;  state: Exp;  lines: +4 -2;
make scan_scaled set errno to EINVAL rather than ERANGE if it encounters
an invalid multiplier, like the man page says it should

"looks sensible" deraadt@, ok ian@
----------------------------
revision 1.10
date: 2009/06/20 15:00:04;  author: martynas;  state: Exp;  lines: +4 -4;
use llabs instead of the home-grown version;  and some comment changes
ok ian@, millert@
----------------------------

openbsd-compat/fmt_scaled.c

index edd682a49823208ba7144d9ed699175cb7aae443..e5533b2de90ce09ba95e3930caa2fcc202efe846 100644 (file)
@@ -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.13 2017/03/11 23:37:23 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,6 +125,10 @@ 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;
                        fpart += i;
                } else {                                /* normal digit */
@@ -131,6 +136,10 @@ scan_scaled(char *scaled, long long *result)
                                errno = ERANGE;
                                return -1;
                        }
+                       if (whole >= LLONG_MAX / 10) {
+                               errno = ERANGE;
+                               return -1;
+                       }
                        whole *= 10;
                        whole += i;
                }
@@ -150,17 +159,22 @@ 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];
 
+                       if (whole >= LLONG_MAX / scale_fact) {
+                               errno = ERANGE;
+                               return -1;
+                       }
+
                        /* scale whole part */
                        whole *= scale_fact;
 
@@ -181,7 +195,9 @@ scan_scaled(char *scaled, long long *result)
                        return 0;
                }
        }
-       errno = ERANGE;
+
+       /* Invalid unit or character */
+       errno = EINVAL;
        return -1;
 }
 
@@ -196,7 +212,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