From: "Ted Unangst" Subject: ksh signed overflow To: tech@openbsd.org Date: Sun, 22 Jun 2025 23:36:33 -0400 dalias pointed out that bash does signed arithmetic, and turns out ksh does the same. We should probably resort to unsigned arith to avoid trouble. posix requires "as if" signed behavior, but we don't want to make the compiler angry. There's existing code to avoid some error conditions, but it uses LONG_MIN and wasn't fixed when the code was changed to use int64 everywhere. So the set of fixes is: Use correct INT64_MIN constant. Check for negation of INT64_MIN. Limit shift to valid range. Perform misc. ops like addition and multiplication with unsigned. I think this is right? expr would need a similar fixup as well. Index: expr.c =================================================================== RCS file: /home/cvs/src/bin/ksh/expr.c,v diff -u -p -r1.34 expr.c --- expr.c 20 Feb 2019 23:59:17 -0000 1.34 +++ expr.c 23 Jun 2025 03:25:30 -0000 @@ -8,7 +8,7 @@ */ #include -#include +#include #include #include "sh.h" @@ -295,7 +295,7 @@ evalexpr(Expr_state *es, enum prec prec) vl->val.i = ~vl->val.i; else if (op == O_LNOT) vl->val.i = !vl->val.i; - else if (op == O_MINUS) + else if (op == O_MINUS && vl->val.i != INT64_MIN) vl->val.i = -vl->val.i; /* op == O_PLUS is a no-op */ } else if (op == OPEN_PAREN) { @@ -343,37 +343,47 @@ evalexpr(Expr_state *es, enum prec prec) switch ((int) op) { case O_TIMES: case O_TIMESASN: - res = vl->val.i * vr->val.i; + res = vl->val.u * vr->val.u; break; case O_DIV: case O_DIVASN: - if (vl->val.i == LONG_MIN && vr->val.i == -1) + if (vl->val.i == INT64_MIN && vr->val.i == -1) res = LONG_MIN; else res = vl->val.i / vr->val.i; break; case O_MOD: case O_MODASN: - if (vl->val.i == LONG_MIN && vr->val.i == -1) + if (vl->val.i == INT64_MIN && vr->val.i == -1) res = 0; else res = vl->val.i % vr->val.i; break; case O_PLUS: case O_PLUSASN: - res = vl->val.i + vr->val.i; + res = vl->val.u + vr->val.u; break; case O_MINUS: case O_MINUSASN: - res = vl->val.i - vr->val.i; + res = vl->val.u - vr->val.u; break; case O_LSHIFT: case O_LSHIFTASN: - res = vl->val.i << vr->val.i; + { + uint64_t amt = vr->val.u; + if (amt > 64) + amt = 64; + res = vl->val.i << amt; + } break; case O_RSHIFT: case O_RSHIFTASN: - res = vl->val.i >> vr->val.i; + { + uint64_t amt = vr->val.u; + if (amt > 64) + amt = 64; + res = vl->val.i >> amt; + } break; case O_LT: res = vl->val.i < vr->val.i; @@ -539,7 +549,7 @@ do_ppmm(Expr_state *es, enum token op, s assign_check(es, op, vasn); vl = intvar(es, vasn); - oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--; + oval = op == O_PLUSPLUS ? vl->val.u++ : vl->val.u--; if (vasn->flag & INTEGER) setint_v(vasn, vl, es->arith); else Index: table.h =================================================================== RCS file: /home/cvs/src/bin/ksh/table.h,v diff -u -p -r1.15 table.h --- table.h 18 Jun 2018 17:03:58 -0000 1.15 +++ table.h 23 Jun 2025 03:10:56 -0000 @@ -20,6 +20,7 @@ struct tbl { /* table item */ union { char *s; /* string */ int64_t i; /* integer */ + uint64_t u; /* unsigned integer */ int (*f)(char **); /* int function */ struct op *t; /* "function" tree */ } val; /* value */