> 14. Okay, but if the line with UB is unreachable (dead) code, then it's as if the UB wasn't there. This one came as a surprise to me. Is this really (unconditionally) true? And if so, why? Seems trivial to me to ensure that UB that will never under any circumstance execute will not affect the functioning of an otherwise correct program.
Falsehoods programmers believe about undefined behavior
111–120 of 233 posts
Re: Falsehoods programmers believe about undefined behavior
#112Earlier quoted context omitted.
That's irrelevant. A C compiler targetting x86 has no obligation at all to handle signed integer overflow in the same way the processor natively does. Because it is not an assembler - it is compiling against the theoretical C virtual machine. This is for the same sort of reason that unsigned overflow does have a well defined meaning in C, and will always do exactly that even for targets that do not natively do that (…
> A C compiler targetting x86 has no obligation It kinda has an implied obligation to not screw with its users. And doing the same optimizations to fold bloated Cpp templates properly messes with my C code.
Re: Falsehoods programmers believe about undefined behavior
#113The truth is that practice diverges from theory, and, in practice, it is actually useful to talk about UB as a thing that happens and not as some mystical aura the compiler can sense or not. Leading to, in practice, almost all of these points being, if not flat-out wrong, at least unhelpful or misleading to the average programmer. For example:
> 14. Okay, but if the line with UB is unreachable (dead) code, then it's as if the UB wasn't there.[1]
The linked post ([1]) doesn't even showcase this phenomenon, it shows something completely different, where ignoring a 3 transmuted into a bool causes more UB. The problem was that you already had UB to begin with when transmuting a 3 into a bool. I want to see a godbolt output where the claimed:
if (false) {
run_ub();
}
actually causes problems.> 28. At least it won't completely wipe the drive.[2]
This is just unhelpful for anyone trying to learn more about UB. UB can call parts of your code that would otherwise be dead, which is a useful piece of information to know. And if you literally write "rm -rf /" in these dead sections, then yeah, I guess UB is technically wiping your drive, but stating it like that just gets in the way of anyone trying to learn more about UB and just perpetrates this unhelpful theory that UB can literally do anything it wants, such as...
> 30. At least it won't start playing Doom if the program didn't already have the Doom source code in it.
Really?
> 31-32. If a UB-containing program "worked fine" previously, recompiling the program without any code changes will still produce a binary that "works fine." Recompiling without code changes and with the same compiler and flags will produce a binary that still "works fine." Recompiling as above + on the same machine will produce a binary that still "works fine."
In practice, compilers are generally deterministic. I see no reason this should be the case. Don't link me a sadistic compiler that randomizes code output.
Finally,
> The moment your program contains UB, all bets are off.
is blatantly untrue. All guarantees are off, but I can bet what most programs with UB do, and it is still useful to be able to reason about what programs with UB do. For example, I can tell you what:
int main(int argc, char \*argv) {
int *p = NULL;
*p = 4;
}
does on O0 on my machine, and most linux machines, despite the fact that it is untrue for "Any kind of reasonable or unreasonable behavior happening with any consistency or any guarantee of any sort."You guys are engineers right? Software engineers? Why don't you do what all engineers are supposed to do and that is use experience and intuition to know when theory applies in practice, and when it doesn't?
[1]: https://www.ralfj.de/blog/2020/07/15/unused-data.html
[2]: https://kristerw.blogspot.com/2017/09/why-undefined-behavior...
Re: Falsehoods programmers believe about undefined behavior
#114It's surprising to see so much debate about when or where UB "happens". Unfortunately this article doesn't refer to the best tool to root out UB - UBSan. If you write C or C++, you should have a UBSan+ASan build that is configured to terminate with an error when it encounters violations. You'll save yourself a lot of hair-pulling later. Then you don't have to wonder about the nebulous topics like what "line" the UB "…
So the only way to run them is too use an emulator, and that means I cannot sanitize drivers.
Re: Falsehoods programmers believe about undefined behavior
#115> Undefined behavior: Anything is allowed to happen, and you might no longer have a computer left after it all happens. and > The moment your program contains UB, all bets are off. Does this include that a compiler, when it can prove that a program it compiles contains UB, erase your disk as part of the compilation process? i.e. without you ever running the compiled program.
> Does this include that a compiler, when it can prove that a program it compiles contains UB, erase your disk as part of the compilation process? i.e. without you ever running the compiled program. No, but technically compiler would not violate specification by compiling program that on running will scan your disk for secret data, publish it online, apply ransomware to your disk and deliberately destroy your SSD.
void main(){};
is allowed to row hammer your RAM and inspect and modify the states of other programs, but is it allowed e.g. to delete your file system through a system call?Re: Falsehoods programmers believe about undefined behavior
#116Earlier quoted context omitted.
Should loop-invariant code motion be an allowed optimization only when the compiler can statically prove that a for loop's increment won't overflow?
If it would result in different behavior in the event of an overflow (which I think is generally not the case), then no. A typical for loop (using <) can easily been seen to not overflow, so this is a rare case anyway. Loops using signed comparison with <= where the compiler can't rule overflow out should result in a compiler or linter warning.
I am not an expert on compiler optimization, but having reasoned through a few cases where a compiler is _unable_ to correctly make an obvious-seeming optimization, I tend to think that compiler writers have good reasons for the things they want to be able to assume.
Re: Falsehoods programmers believe about undefined behavior
#117I am always amazed by articles like these. This guy thoroughly covers this topic. I cannot say I have ever written anything that ran into or caused undefined behavior. And I have programmed for a long time. Is this just an interesting topic or do people actively push the compiler to see what it will do? Maybe I am a boring/simple programmer. :)
> I cannot say I have ever written anything that ran into or caused undefined behavior. And I have programmed for a long time. If during that long time you were writing C or C++, it's very likely that you did but you may not have attributed the bugs you were fixing to Undefined Behavior. You may have considered them either to be your bugs or "compiler bugs that you worked around."
Back when I worked on VAX/Alpha (I am old) I absolutely had to get down and read assembly/binary to figure out the compiler was doing what I thought. And in some cases had to work around compiler bugs.
But for most of the more recent things I have written I just have not run into these types of errors. I am really boring or I write code that is boring ha.
It is fascinating that people study this stuff and interesting to read about, I just do not run into it.
Re: Falsehoods programmers believe about undefined behavior
#118Earlier quoted context omitted.
The compiler isn't required to know if code is unreachable. For example, `if (fermats_last_theorem_is_wrong()) {*p == 1} ` will imply to a modern compiler that p is not NULL in the scope where it is defined, even if we now know that this code will never be reached (assuming that function is correctly written). Or, here is a better example: for(int i=0; i > n;) { } *p=1; Here the compiler will assume that the loop ter…
The compiler is required to successfully translate a program that contains unreachable UB, and such a program is conforming if undefined behavior is never reached. This is explicitly spelled out in https://www.open-std.org/jtc1/sc22/wg14/docs/rr/dr_109.html > Furthermore, if every possible execution of a given program would result in undefined behavior, the given program is not strictly conforming. A conforming imple…
To hammer it in, the consider the following:
if(p) *p = 0;
*p is undefined if p is NULL. But as long as that statement is never reached when p is NULL that is not a problem. That is trivially true in this case, but the same applies even for more complex relations between the conditional and the undefinedness of the statement.
Or put another way, in
int * p = 0;
if(fermats_last_theorem_is_wrong()) {
p = malloc(sizeof(int));
*p = 0;
}
if(p) {
puts("fermats last theorem is wrong\n");
}
if(fermats_last_theorem_is_wrong()) {
*p == 1;
}
if(p) {
puts("fermats last theorem is wrong\n");
}
the compiler is not allowed to remove the if(p) checks and unconditionally print that fermats last theorem is wrong unless it can prove that fermats_last_theorem_is_wrong() is true.In fact, something having undefined behavior and the compiler being allowed to assume that it is unreachable are effectively the same thing. Which is why the spec for std::unreachable() and the compiler-specific intrinsics that preceded it is that it invokes undefined behavior and not more complex wording.
Re: Falsehoods programmers believe about undefined behavior
#119Earlier quoted context omitted.
No, no, no. UB is a global property of your program. "All bets are off" even at compile time. "Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended." https://blog.regehr.org/archives/213
UB is typically a property of the execution of a program. A program may bump into UB for one set of inputs, but not for an other set.
Re: Falsehoods programmers believe about undefined behavior
#120I thought this was going to be an actually useful post about the details of UB, when it happens, and what it generally looks like, but instead it's just more of the same often repeated "if you write a program with UB your compiler will literally summon dragons and wipe your hard drive and kill you in your sleep." The truth is that practice diverges from theory, and, in practice, it is actually useful to talk about UB…
Welcome to kernel code. I believe one could make it play DOOM by clever malice in this case.