Re: Optimisations and undefined behaviour

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, 8 Nov 2015, Jeff Law wrote:

On 11/08/2015 12:11 PM, Florian Weimer wrote:
On 11/06/2015 01:32 PM, David Brown wrote:
How about this case:

int foo(int x) {
	if (x > 1290) {
		printf("X is wrong here %d, but we don't care\n", x);
	}
	return x*x*x;

Why not simply:
  unsigned y = x;
  return y*y*y;
? This is an example where defined behavior is so easy to get...

One very useful trick against compiler optimizations is:
  asm("":"+g"(x));
(should be "+X" but gcc is too buggy for that. Use "+gx" for floats on x86)
It tells gcc: "I might have modified the value of x here, so you can't assume anything about that value", but no code is actually generated for it.

}

The compiler can eliminate the check and the printf.

I don't think the compiler can do that because printf has an externally
visible effect, which is sequenced before the undefined behavior, so
this program transformation would not be permitted under the as-if rule.

I am not sure about this interpretation (not 100% about the opposite either). If the compiler knew that printf always returns, it would know that x > 1290 implies undefined behavior, and could thus assume that it does not happen. As long as there is a chance that printf might not return (throws an exception, calls longjmp or exit, loops infinitely, etc), the compiler cannot make that assumption (it could assume that printf does not return though, and do weird things if that assumption turns out to be wrong).

Right. This is precisely the discussion we had when looking at this class of issues in the erroneous-path optimizer. It doesn't currently try to handle overflows, but if it did, it'd probably do something like first transforming the code into:


if (x > 1290) {
   printf ("...");
   return x * x * x;
}
return x * x * x;

Note how the return statement has been duplicated into the THEN clause. That allows us to transform the undefined behaviour into


if (x > 1290) {
   printf ("...");
   __builtin_trap ();
}
return x * x * x.

The OP was hoping to get an arbitrary value, not a trap, or he could have added a call to abort() himself.

Note carefully that we don't use __builtin_unreachable, which has the undesirable effect of doing absolutely nothing. Whereas __builtin_trap immediately terminates the program, thus never allowing the undefined behaviour to actually execute (and thus prevent any bad things from happening from a security standpoint).

There are 2 irreconcilable viewpoints: security and speed. We already have an option that turns __builtin_unreachable into an equivalent of __builtin_trap. If you insist on using __builtin_trap directly in too many places, we will have to add an option: -fprogram-returns-normally that turns __builtin_trap (and std::terminate and a few others) into __builtin_unreachable.

--
Marc Glisse



[Index of Archives]     [Linux C Programming]     [Linux Kernel]     [eCos]     [Fedora Development]     [Fedora Announce]     [Autoconf]     [The DWARVES Debugging Tools]     [Yosemite Campsites]     [Yosemite News]     [Linux GCC]

  Powered by Linux