From c38ec91d229cbe56167f8af865c05b3d92db705f Mon Sep 17 00:00:00 2001 From: Fabian Giesen Date: Wed, 7 Jul 2021 01:27:15 -0700 Subject: [PATCH] stb_divide: Fix integer overflow issues We've established the signs of values before so we can carefully jiggle the expressions to be guaranteed overflow-free; the tests for <0 here were meant to check if the value was "still negative", i.e. if the sum did not underflow below INT_MIN, and we can rewrite that using algebra to be overflow-free. We need an extra case in the Euclidean dvision for INT_MIN / INT_MIN which is a bit annoying but trivial; finally, for two's complement platforms, note that abs(x) = (x > 0) ? x : -x does not work (since for x=INT_MIN, -x still gives INT_MIN), but the equivalent formulation for _negative_ absolute value (x < 0) ? x : -x is always in range and overflow-free, so rewrite the relevant expressions using that negative absolute value instead. Fixes issue #741. --- stb_divide.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/stb_divide.h b/stb_divide.h index f8b1f3e..c8a1d92 100644 --- a/stb_divide.h +++ b/stb_divide.h @@ -166,15 +166,15 @@ int stb_div_floor(int v1, int v2) return v1/v2; #else if (v1 >= 0 && v2 < 0) { - if ((-v1)+v2+1 < 0) // check if increasing v1's magnitude overflows - return -stb__div(-v1+v2+1,v2); // nope, so just compute it + if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows + return -stb__div((v2+1)-v1,v2); // nope, so just compute it else return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0); } if (v1 < 0 && v2 >= 0) { if (v1 != INT_MIN) { - if (v1-v2+1 < 0) // check if increasing v1's magnitude overflows - return -stb__div(v1-v2+1,-v2); // nope, so just compute it + if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows + return -stb__div((v1+1)-v2,-v2); // nope, so just compute it else return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0); } else // it must be possible to compute -(v1+v2) without overflowing @@ -209,8 +209,10 @@ int stb_div_eucl(int v1, int v2) else // if v1 is INT_MIN, we have to move away from overflow place if (v2 >= 0) q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2); - else + else if (v2 != INT_MIN) q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2); + else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow + q = 1, r = 0; #endif if (r >= 0) return q; @@ -228,13 +230,13 @@ int stb_mod_trunc(int v1, int v2) if (r >= 0) return r; else - return r + (v2 > 0 ? v2 : -v2); + return r - (v2 < 0 ? v2 : -v2); } else { // modulus result should always be negative int r = stb__mod(v1,v2); if (r <= 0) return r; else - return r - (v2 > 0 ? v2 : -v2); + return r + (v2 < 0 ? v2 : -v2); } #endif } @@ -267,7 +269,7 @@ int stb_mod_eucl(int v1, int v2) if (r >= 0) return r; else - return r + (v2 > 0 ? v2 : -v2); // abs() + return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow] } #ifdef STB_DIVIDE_TEST