Search code examples
cinteger-overflowgcc-warning

Overreaching gcc sanitizer


I have this little snippet here:

static int32_t s_pow(int32_t base, int32_t exponent)
{
   int32_t result = 1;
   while (exponent != 0) {
      if ((exponent % 2) == 1) {
         result *= base;
      }
      exponent /= 2;
      base *= base; /* <- this line */
   }
   return result;
}

Small and neat and just the one I need. But GCC 7.5.0 (and other newer versions that support the checks) complained:

gcc -I./ -Wall -Wsign-compare -Wextra -Wshadow -fsanitize=undefined\
 -fno-sanitize-recover=all -fno-sanitize=float-divide-by-zero\
 -Wdeclaration-after-statement -Wbad-function-cast -Wcast-align -Wstrict-prototypes\ 
 -Wpointer-arith -Wsystem-headers -O3 -funroll-loops -fomit-frame-pointer -flto -m64\
 test.o -o test

test.c:25:12: runtime error: signed integer overflow: 65536 * 65536 cannot be represented in type 'int'

Yes, GCC, that is correct, 65536 * 65536 is a bit much for a 31 bit data type even in a 64 bit environment, admitted. But the input comes from a small table and is carefully chosen in such a way that the result result cannot exceed 2^20 and therefor none of the intermediate results can. I cannot change the sanitizing itself only my code. Yes, I checked all possible results.

Any ideas? Or is it my code?


Solution

  • As I see runtime error: signed integer overflow is happened in runtime. It means that you send an input leading to UB. You say that result cannot exceed 2^20. As I guess you mean that you have a case where you make base *= base action after you already got a result, and don't care about base anymore.

    If I understand you right, you can just change code to something like this I guess? It will give you a fix for s_pow(60000, 1) call for example, and won't affect performance, due optimization is quite easy for compiler here.

    static int32_t s_pow(int32_t base, int32_t exponent) {
       int32_t result = 1;
       while (exponent != 0) {
          if ((exponent % 2) == 1) {
             result *= base;
          }
          exponent /= 2;
          if (exponent == 0) {
            break;
          }
          base *= base; /* <- this line */
       }
       return result;
    }
    

    Basically it is important to understand what you want here, if you want to remove annoying warning, here it is a way. If warning still appears, probably you send too big numbers.