ash,hush: do not segfault on $((2**63 / -1))
[oweals/busybox.git] / shell / math.c
index 3da151137ad4c593b875ec8d4d6f1068595eddad..e7565ebf2980a6f9f249977e6a34541582ceab89 100644 (file)
@@ -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)) {