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 <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2018-01-10 13:22:25 +01:00
parent 2c876774a9
commit baa41c7855

View File

@ -5780,6 +5780,26 @@ ash_arith(const char *s)
return result; return result;
} }
#endif #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 * expandarg flags
@ -6816,13 +6836,10 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
loc = str = stackblock() + strloc; loc = str = stackblock() + strloc;
# if !ENABLE_FEATURE_SH_MATH
# define ash_arith number
# endif
/* Read POS in ${var:POS:LEN} */ /* Read POS in ${var:POS:LEN} */
colon = strchr(loc, ':'); colon = strchr(loc, ':');
if (colon) *colon = '\0'; if (colon) *colon = '\0';
pos = ash_arith(loc); pos = substr_atoi(loc);
if (colon) *colon = ':'; if (colon) *colon = ':';
/* Read LEN in ${var:POS:LEN} */ /* Read LEN in ${var:POS:LEN} */
@ -6830,7 +6847,6 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
/* *loc != '\0', guaranteed by parser */ /* *loc != '\0', guaranteed by parser */
if (quotes) { if (quotes) {
char *ptr; char *ptr;
/* Adjust the length by the number of escapes */ /* Adjust the length by the number of escapes */
for (ptr = startp; ptr < (str - 1); ptr++) { for (ptr = startp; ptr < (str - 1); ptr++) {
if ((unsigned char)*ptr == CTLESC) { if ((unsigned char)*ptr == CTLESC) {
@ -6842,19 +6858,15 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
orig_len = len; orig_len = len;
if (*loc++ == ':') { if (*loc++ == ':') {
/* ${var::LEN} */ /* ${var::LEN} */
len = ash_arith(loc); len = substr_atoi(loc);
} else { } else {
/* Skip POS in ${var:POS:LEN} */ /* Skip POS in ${var:POS:LEN} */
len = orig_len; len = orig_len;
while (*loc && *loc != ':') { while (*loc && *loc != ':')
loc++; loc++;
} if (*loc++ == ':')
if (*loc++ == ':') { len = substr_atoi(loc);
len = ash_arith(loc);
}
} }
# undef ash_arith
if (pos < 0) { if (pos < 0) {
/* ${VAR:$((-n)):l} starts n chars from the end */ /* ${VAR:$((-n)):l} starts n chars from the end */
pos = orig_len + pos; pos = orig_len + pos;