diff options
author | Denys Vlasenko | 2014-11-18 14:32:58 +0100 |
---|---|---|
committer | Denys Vlasenko | 2014-11-18 14:32:58 +0100 |
commit | 8a475def9e3e21f780ebcf07dd607b26ceb00ea8 (patch) | |
tree | 3f09f0533abe2126d00146448eb34f333b8c7564 /shell | |
parent | 08a5dab181fa4c28b7636c35021308e1e12e7b59 (diff) | |
download | busybox-8a475def9e3e21f780ebcf07dd607b26ceb00ea8.zip busybox-8a475def9e3e21f780ebcf07dd607b26ceb00ea8.tar.gz |
ash,hush: do not segfault on $((2**63 / -1))
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r-- | shell/math.c | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/shell/math.c b/shell/math.c index 3da1511..e7565eb 100644 --- a/shell/math.c +++ b/shell/math.c @@ -415,10 +415,29 @@ arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_ } else if (right_side_val == 0) return "divide by zero"; - else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) - rez /= right_side_val; - else if (op == TOK_REM || op == TOK_REM_ASSIGN) - rez %= right_side_val; + else if (op == TOK_DIV || op == TOK_DIV_ASSIGN + || op == TOK_REM || op == TOK_REM_ASSIGN) { + /* + * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))' + * + * MAX_NEGATIVE_INT / -1 = MAX_POSITIVE_INT+1 + * and thus is not representable. + * Some CPUs segfault trying such op. + * Others overfolw MAX_POSITIVE_INT+1 to + * MAX_NEGATIVE_INT (0x7fff+1 = 0x8000). + * Make sure to at least not SEGV here: + */ + if (right_side_val == -1 + && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */ + ) { + right_side_val = 1; + } + if (op == TOK_DIV || op == TOK_DIV_ASSIGN) + rez /= right_side_val; + else { + rez %= right_side_val; + } + } } if (is_assign_op(op)) { |