ous line was UB and prevent it from causing problems.
- At least the impact of the UB is limited to code which uses values produced from the UB.
- At least the impact of the UB is limited to code which is in the same compilation unit as the line with UB.
- Okay, but at least the impact of the UB is limited to code which runs after the line with UB.
UB is explicitly allowed to alter the behavior of other code, even including operations preceding it! "Alter" here encompasses corrupting, undoing, or altogether preventing (as if it never happened) the outcomes of that other code. To learn more and see examples of UB causing "time travel," check out this blog post.
Thanks to these two Reddit posts for suggesting better wording for these items. For the original text, see the Errata section at the end of this post.
Falsehoods about the possible outcomes of UB
- At least it won't corrupt the memory of the program.
- At least it won't corrupt the memory of the program other than where the UB-affected data was located.
- At least it won't corrupt the heap.
- At least it won't corrupt the stack.
- At least it won't corrupt the current stack frame. (My name for this is the "local variables are safely in registers" fallacy.)
- At least it won't corrupt the stack pointer.
- At least it won't corrupt the CPU flags register / any other CPU state.
- At least it won't corrupt the executable memory of the program. OS and hardware security features like W^X can make this unlikely, but self-modifying programs can be built so it's in principle possible through UB as well. Certainly there's no guarantee that UB won't do this!
- At least it won't corrupt streams like stdout or stderr.
- At least it won't overwrite any files the program already had open.
- At least it won't open new files and overwrite them.
- At least it won't completely wipe the drive.
- At least it won't damage or destroy any hardware components. Not all devices have the same level of self-protection against bad inputs written to their control registers. This is the kind of lesson one tends to learn the hard way.
- At least it won't start playing Doom if the program didn't already have the Doom source code in it. I'd be quite impressed if you made a compiler that makes programs run Doom when they encounter UB. Consider it a challenge!
Falsehoods like "but it worked fine before"
- 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."
- Recompiling as above + if you haven't rebooted the machine since the last compilation will produce a binary that still "works fine."
- Recompiling as above + with the same environment variables will produce a binary that still "works fine."
- Recompiling as above + at the same time of day and day of week as before, during a Lunar eclipse, having first sacrificed a fresh stick of RAM to the binary gods, will produce a binary that still "works fine."
Falsehoods about self-consistent behavior of UB
- Multiple runs of the program compiled as above and with the same inputs will produce the same behavior in each run.
- Those multiple runs will produce the same behavior if the program, ignoring the UB, is deterministic.
- But they will if the program is also single-threaded.
- But they will if the program also doesn't read any external data (files, network, environment variables, etc.).
Community-contributed falsehoods around UB
- Using a debugger on a UB-containing program will show program state that corresponds to the source code. This is a corrolary of falsehood #16, further explained in this post. UB can corrupt the behavior of the program both before and after the UB, so the source code you see in your editor no longer matches the actual executing program. Of course, you can still use the debugger to step through assembly instructions and view re