summaryrefslogtreecommitdiffhomepage
path: root/shell
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2014-11-18 14:32:58 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2014-11-18 14:32:58 +0100
commit8a475def9e3e21f780ebcf07dd607b26ceb00ea8 (patch)
tree3f09f0533abe2126d00146448eb34f333b8c7564 /shell
parent08a5dab181fa4c28b7636c35021308e1e12e7b59 (diff)
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.c27
1 files changed, 23 insertions, 4 deletions
diff --git a/shell/math.c b/shell/math.c
index 3da151137..e7565ebf2 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)) {