4

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?

Arthur
  • 83
  • 1
  • 4
  • 2
    You can't. The compiler is allowed to do anything when it sees undefined behavior, so in this case it didn't create a division instruction, and there is nothing to catch. – interjay May 07 '21 at 16:58
  • 1
    Also note that generating a signal on division by zero, even when it does work, is rather specific to x86. On some other machines, e.g. ARM, the hardware doesn't generate exceptions at all in such cases, and so no signal will ever be produced. You really do have to do the check yourself in software if you want it to be reliable. – Nate Eldredge May 07 '21 at 17:05
  • 1
    @AbhishekChoubey "Anything multiplied to 0 becomes 0, be it infinity or undefined value." --> Hmm, Infinity × 0 more like [NAN](https://stackoverflow.com/questions/37841240/why-is-infinity-%C3%97-0-nan) – chux - Reinstate Monica May 07 '21 at 17:36
  • @AbhishekChoubey "Division takes precedence over multiplication", in C, `*,/,%` are of same precedence. – chux - Reinstate Monica May 07 '21 at 17:43

4 Answers4

4

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.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • How about `result = 1/(volatile int)0 * (volatile int)0;`? – chux - Reinstate Monica May 07 '21 at 17:46
  • 4
    @chux-ReinstateMonica: Top-level qualifiers in casts are ignored; values cannot be qualified, just objects. C 6.5.4 5, emphasis added: “Preceding an expression by a parenthesized type name converts the value of the expression to the **unqualified version** of the named type.” – Eric Postpischil May 07 '21 at 17:49
  • Thanks. Maybe a _compound literal_ which I think would be an object here: `#define VZ ((volatile int){0}) ... int result = 1/VZ*VZ;` – chux - Reinstate Monica May 07 '21 at 17:54
  • I was meant to judge that, whether a constant expression(generated by my – Arthur May 08 '21 at 01:32
  • I was meant to judge that, whether a constant expression(generated randomly) has a division by zero behavior through an abnormal phenomenon. But on my computing platform, the behavior of the expressions like 1/0*0 (or, 2/(1/2)*0) excuted at run-time is normal .In other words, i cannot judge them in a valid way. – Arthur May 08 '21 at 01:50
2

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.

dbush
  • 205,898
  • 23
  • 218
  • 273
1

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.

klutt
  • 30,332
  • 17
  • 55
  • 95
  • 1
    Apple Clang 11 does not generate a divide instruction for this. For `1u/d`, it generates `xor %esi, %esi; cmpl $2, %ebx; sete %sil`. (The compare incorporates `d = argc - 1`, as `%ebx` contains `argc`.) – Eric Postpischil May 07 '21 at 17:57
  • @EricPostpischil I compiled it with clang and it did print "division by zero." – klutt May 07 '21 at 18:07
  • How is the fact that one compiler in one circumstance produced a division instruction going to ensure to OP that their code functions as desired? – Eric Postpischil May 07 '21 at 18:12
  • 1
    @EricPostpischil I'm not claiming that this ensures anything. – klutt May 07 '21 at 18:14
-2

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.

Jack T. Spades
  • 986
  • 6
  • 9