ash: make ${v:N:M} more robust for very large M by clamping to MIN/MAX_INT
authorDenys Vlasenko <vda.linux@googlemail.com>
Wed, 10 Jan 2018 12:22:25 +0000 (13:22 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Wed, 10 Jan 2018 12:22:25 +0000 (13:22 +0100)
Before this patch, "${v:2:0x100000001}" = "${v:2:1}",
and similarly, constructs like "${v:2:9999999999}" may give wrong result
due to int overflows.

function                                             old     new   delta
substr_atoi                                            -      43     +43

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
shell/ash.c

index 83a8e77f9d661eba25cd0e46c2355003fdf7caf5..a7f330c117e1d778e195d99303f5fb5ec5002b16 100644 (file)
@@ -5780,6 +5780,26 @@ ash_arith(const char *s)
        return result;
 }
 #endif
+#if BASH_SUBSTR
+# if ENABLE_FEATURE_SH_MATH
+static int substr_atoi(const char *s)
+{
+       arith_t t = ash_arith(s);
+       if (sizeof(t) > sizeof(int)) {
+               /* clamp very large or very large negative nums for ${v:N:M}:
+                * else "${v:0:0x100000001}" would work as "${v:0:1}"
+                */
+               if (t > INT_MAX)
+                       t = INT_MAX;
+               if (t < INT_MIN)
+                       t = INT_MIN;
+       }
+       return t;
+}
+# else
+#  define substr_atoi(s) number(s)
+# endif
+#endif
 
 /*
  * expandarg flags
@@ -6816,13 +6836,10 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
 
                loc = str = stackblock() + strloc;
 
-# if !ENABLE_FEATURE_SH_MATH
-#  define ash_arith number
-# endif
                /* Read POS in ${var:POS:LEN} */
                colon = strchr(loc, ':');
                if (colon) *colon = '\0';
-               pos = ash_arith(loc);
+               pos = substr_atoi(loc);
                if (colon) *colon = ':';
 
                /* Read LEN in ${var:POS:LEN} */
@@ -6830,7 +6847,6 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
                /* *loc != '\0', guaranteed by parser */
                if (quotes) {
                        char *ptr;
-
                        /* Adjust the length by the number of escapes */
                        for (ptr = startp; ptr < (str - 1); ptr++) {
                                if ((unsigned char)*ptr == CTLESC) {
@@ -6842,19 +6858,15 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
                orig_len = len;
                if (*loc++ == ':') {
                        /* ${var::LEN} */
-                       len = ash_arith(loc);
+                       len = substr_atoi(loc);
                } else {
                        /* Skip POS in ${var:POS:LEN} */
                        len = orig_len;
-                       while (*loc && *loc != ':') {
+                       while (*loc && *loc != ':')
                                loc++;
-                       }
-                       if (*loc++ == ':') {
-                               len = ash_arith(loc);
-                       }
+                       if (*loc++ == ':')
+                               len = substr_atoi(loc);
                }
-#  undef ash_arith
-
                if (pos < 0) {
                        /* ${VAR:$((-n)):l} starts n chars from the end */
                        pos = orig_len + pos;