3 ms·
> the whole point of the standards specifying UB is precisely to let implementations define the behavior themselves. This used to be the case. Signed integer
by loup-vaillant 9y ago
> the whole point of the standards specifying UB is precisely to let implementations define the behavior themselves.
This used to be the case. Signed integer overflow for instance is undefined because some CPUs go bananas when you try that. Other platform performed 2's complement just fine, and we used to be able to rely on this.
No longer.
See, the standard doesn't say "implementation defined". It doesn't say "undefined on platforms that go bananas, implementation defined otherwise". It says "undefined" period.
Signed integer overflow is undefined on all platforms, even your modern x86-64 CPU. Compiler writers interpreted it as a licence to assume it never happens, to help optimisations. For instance:
int x = whatever;
x += small_increment;
if (x < 0) { // check for overflow
abort(); // security shut down
}
proceed(x); // overflow didn't happen, we're safe!
Here's what the compiler thinks:
int x = whatever;
x += small_increment;
if (x < 0) { // only true if signed overflow -> false
abort(); // dead code
}
proceed(x);
Then the compiler simply deletes your security check:
int x = whatever;
x += small_increment;
proceed(x);
Don't listen to Chandler Carruth, nasal demons are real. Some undefined behaviours can encrypt your whole hard drive, assuming they're exploitable by malicious inputs.
- userbinator 9y agoThis is the sort of emotionally-charged fearmongering around UB that really makes any discussion pointless. That example is wrong. Integers can be signed. If a compiler cannot prove x >= 0, then it simply cannot remove that code. Now, if you used unsigned int x = whatever; ... if(x < 0) There would be an obvious case for removing that if.
- teraflop 9y agoA very simple test case demonstrates that GCC can remove tests in the presence of signed overflow, even in ways that change a program's behavior. $ cat undefined.c #include <limits.h> #include <stdio.h> #include <stdlib.h> int main() { int x = INT_MAX; if (x+1 > x) { printf("%d > %d\n", x+1, x); } else { printf("overflow!\n"); } } $ gcc --version gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0 Copyright (C) 2017 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gcc undefined.c && ./a.out overflow! $ gcc -O3 undefined.c && ./a.out -2147483648 > 2147483647
- userbinator 9y agoYes, that example is well-known but different; here, the compiler is assuming that x + 1 will always be greater than x, which is entirely something else than the parent's assertion of assuming that x + small_increment will always be positive.
- loup-vaillant 9y agoThe difference doesn't matter, you would know better if you weren't clinging so hard to your beliefs. Here's the "difference": $ cat undefined.c #include <limits.h> #include <stdio.h> #include <stdlib.h> int main() { int x = INT_MAX; if (x+1 < 0) { printf("%d < 0\n", x+1); } else { printf("overflow!\n"); } } $ gcc --version gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609 Copyright (C) 2015 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gcc undefined.c && ./a.out -2147483648 < 0 $ gcc -O3 undefined.c && ./a.out overflow! The security check is gone all the same.
- yorwba 9y agoYour "small_increment" needs to be at least large enough to turn the smallest negative int into a non-negative number, and that makes it unrepresentable as a signed int itself. There are cases where the compiler removes misguided overflow checks, since "perform UB, then check whether it happened" doesn't actually work, but your example is not such a case.
- loup-vaillant 9y ago> "perform UB, then check whether it happened" doesn't actually work This raises an interesting point, because in many cases, assuming 2's complement and wrapping around, checking for overflow after the fact, as opposed to preventing it from happening in the first place, is actually easier. (And actually works if you use the `-fwrap` flag.) The right thing should be easier to do than the wrong thing. It's a shame this is not the case here.
- loup-vaillant 9y agoI was of course assuming that `whatever` was positive, and the compiler knew it. See this sibling thread: https://news.ycombinator.com/item?id=16664546 https://news.ycombinator.com/item?id=16664546