7 ms·
I 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
by nmilo 4y ago
I 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 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 https://www.ralfj.de/blog/2020/07/15/unused-data.html
[2]: https://kristerw.blogspot.com/2017/09/why-undefined-behavior-may-call-never.html https://kristerw.blogspot.com/2017/09/why-undefined-behavior...
- AstralStorm 4y agoUB can easily wipe "the drive" on my microcontroller by sending a wrong SPI command to the flash chip, or by messing with write unprotected memory mapped built in flash. Welcome to kernel code. I believe one could make it play DOOM by clever malice in this case.
- nmilo 4y agoYeah, I never said it couldn't, I said the more helpful thing to say is that UB can cause what looks like dead code to not be dead. If that dead code includes, an rm -rf, or, say, a function to enable/disable arbitrary pins on your chip, then yes, that dead code might be called, and with undefined parameters too. But saying UB will wipe your drive is just misleading and almost wrong.
- account42 4y agoWe need a doomcc that will replace all undefined behavior with doom. A more fun version of ubsan if you will.
- nayuki 4y agoThanks for inadvertently realizing my prediction. > When a C or C++ program triggers undefined behavior, anything is allowed to happen in the program execution. And by anything, I really mean anything: The program can crash with an error message, it can silently corrupt data, it can morph into a colorful video game, or it can even give the right result. -- https://www.nayuki.io/page/undefined-behavior-in-c-and-cplusplus-programs https://www.nayuki.io/page/undefined-behavior-in-c-and-cplus...
- nayuki 4y ago>> Falsehood #28. At least it won't completely wipe the drive. > This is just unhelpful for anyone trying to learn more about UB. [...] 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 The example is contrived, yes. But even if the source code doesn't literally contain "rm -rf /", if the program contains a buffer overflow vulnerability, then someone can craft an input to inject arbitrary code/commands into the program execution and still effect a deletion anyway. So yes, UB really does mean anything can happen.