Download raw body.
ksh signed overflow
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 <ctype.h>
-#include <limits.h>
+#include <stdint.h>
#include <string.h>
#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 */
ksh signed overflow