From baa41c785551ae0580526298aa6fadf4534fc8c0 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Wed, 10 Jan 2018 13:22:25 +0100 Subject: ash: make ${v:N:M} more robust for very large M by clamping to MIN/MAX_INT 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 --- shell/ash.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) (limited to 'shell/ash.c') diff --git a/shell/ash.c b/shell/ash.c index 83a8e77f9..a7f330c11 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -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; -- cgit v1.2.3