How to filter the expression akin to `int c = 1/0*0` in C language?

Viewed 114

OS: Ubuntu 18.04

GCC: 7.5.0

I'm writing an expression generator to test my simple debugger, and want to filter the expressions with division by zero behavior. However i encounter a troubling problem.

As for the definite division by zero behavior akin to int c = 1/0, it will raise a signal so i can handle these cases by signal(). Nevertheless, in the case akin to int c = 1/0*0, cis equal 0 , and the program will never trap into the signal handler.

The test code as below.

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>


void ss(int sig){
  printf("division by zero.\n");
  exit(0);
}

int main(){
   int c;
   signal(SIGFPE, ss);
   c = (1u/0u)*0u;
   printf("%d\n", c);
}

Here is the result.

gcc -o divide_0 divide_0.c
divide_0.c: In function ‘main’:
divide_0.c:15:11: warning: division by zero [-Wdiv-by-zero]
    c = (1u/0u)*0u;
           ^
0

How can i capture the warning in this case?

4 Answers

Forcing the compiler to execute a division by zero is actually hard:

  1. First, as you have noted, the compiler may evaluate 1/0 at compile-time. To prevent it from knowing the divisor is zero, we can use a volatile object, as in volatile int zero = 0; c = 1/zero;. The volatile qualifier tells the compiler the object may be changed by means unknown to it, so it cannot assume it is zero and must get the value when the expression is being evaluated.

  2. Multiplying by zero in (1u/0u)*0u is counterproductive, even after we change the divisor to zero. The compiler can reason that any defined result of (1u/zero)*0u is zero, and therefore it is allowed to use zero as the union of the defined results (zero) and the undefined results (whatever the compiler likes), so it can replace (1u/zero)*0u with zero, 0. That is, it must still evaluate zero because it is volatile, but then it is free to just produce zero as the result without doing the division.

  3. The compiler does not have to use a division instruction to evaluate a division operator. I tested with Apple Clang 11, and it was evaluating 1u/zero with instructions equivalent to zero == 1 ? 1 : 0;. In other words, it just did a compare and a set, not a division, presumably because compare and set is faster. When I changed this to 13u/zero, then the compiler used a divide instruction. I expect your best bet for getting a divide instruction would be to use volatile int unknown = 0; unknown = unknown/unknown;. Even then, a compiler would be allowed by the C standard to perform the division using any instructions it wanted, not a divide. But I presume compilers will generally generate a divide instruction in this case.

Then this code will execute a division at run-time. Whether that causes a signal and what happens with that signal depends on your computing platform.

Division by zero is not guaranteed to generate a signal.

Whenever your expression evaluator performs a division, it needs to check if the divisor is 0, and if so perform an appropriate action.

I assume that the compiler somehow just removes the division. It is allowed to do that. So instead try this code:

int main(int argc, char **argv){
   int c;
   int d = argc - 1;
   signal(SIGFPE, ss);
   c = 1u/d;
   printf("%d\n", c);
}

Call it without arguments. The trick here is that the compiler cannot know how many arguments you will give the program, so it cannot optimize it away. Well, not as easily anyway.

The compiler itself complains about this since it can tell that your code is faulty and unsafe. However, you can try to use -Wno-div-by-zero argument for gcc at your own risk.

Related