From 1a77667409dec9fcb9a9c9764973e671827dccf7 Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Mon, 7 Jul 2025 19:08:26 -0400 Subject: [PATCH] Fix UBSan error with overflowing exponent operator (#1727) --- src/opmath.cpp | 15 ++++++--------- test/asm/exponent.asm | 11 +++++++++++ test/asm/overflow.asm | 6 ++++++ test/asm/overflow.err | 4 ++-- test/asm/overflow.out | 2 ++ 5 files changed, 27 insertions(+), 11 deletions(-) create mode 100644 test/asm/exponent.asm diff --git a/src/opmath.cpp b/src/opmath.cpp index d9df2fc5..061d17b2 100644 --- a/src/opmath.cpp +++ b/src/opmath.cpp @@ -21,20 +21,17 @@ int32_t op_modulo(int32_t dividend, int32_t divisor) { } int32_t op_exponent(int32_t base, uint32_t power) { - int32_t result = 1; + uint32_t result = 1; - for (;;) { + for (uint32_t ubase = base; power; power /= 2) { if (power % 2) { - result *= base; + result = static_cast(result) * ubase; } - power /= 2; - if (!power) { - break; - } - base *= base; + ubase = static_cast(ubase) * ubase; } - return result; + // Avoid arithmetic overflow runtime error + return result <= INT32_MAX ? result : -static_cast(~result) - 1; } int32_t op_shift_left(int32_t value, int32_t amount) { diff --git a/test/asm/exponent.asm b/test/asm/exponent.asm new file mode 100644 index 00000000..c3590c63 --- /dev/null +++ b/test/asm/exponent.asm @@ -0,0 +1,11 @@ +def n = 30 +for x, -n, n + for p, n + def v1 = x ** p + def v2 = 1 + for i, p + def v2 *= x + endr + assert v1 == v2, "{d:x}**{d:p} = {d:v1} or {d:v2}?" + endr +endr diff --git a/test/asm/overflow.asm b/test/asm/overflow.asm index 209671b5..c92fe4b1 100644 --- a/test/asm/overflow.asm +++ b/test/asm/overflow.asm @@ -34,6 +34,12 @@ def x = x << 1 dl -1 << 1 print_x +def x = 2 ** 31 + print_x + +def x = 5 ** 29 + print_x + def x = 4294967295 def x = 4294967296 diff --git a/test/asm/overflow.err b/test/asm/overflow.err index a8ea1830..ca0c0501 100644 --- a/test/asm/overflow.err +++ b/test/asm/overflow.err @@ -2,7 +2,7 @@ warning: overflow.asm(23): [-Wdiv] Division of -2147483648 by -1 yields -2147483648 warning: overflow.asm(24): [-Wdiv] Division of -2147483648 by -1 yields -2147483648 -warning: overflow.asm(38): [-Wlarge-constant] +warning: overflow.asm(44): [-Wlarge-constant] Integer constant is too large -warning: overflow.asm(41): [-Wlarge-constant] +warning: overflow.asm(47): [-Wlarge-constant] Graphics constant is too long, only first 8 pixels considered diff --git a/test/asm/overflow.out b/test/asm/overflow.out index 96f0efa3..cd5c4c4c 100644 --- a/test/asm/overflow.out +++ b/test/asm/overflow.out @@ -4,3 +4,5 @@ $80000000 $80000000 $0 $FFFFFFFE +$80000000 +$6B90BE55