Question
Can you find a real example of "time travel" caused by undefined behaviour?
I am curious. Does anyone know a non-hypothetical C or C++ counterexample to the myth that "the impact of the undefined behaviour is limited to code which runs after the line with undefined behaviour"? In other words I want to see an example of undefined behaviour "travelling back in time" and causing an externally visible effect. By non-hypothetical I mean an example of a real, production-grade compiler (preferably a recent-ish version too) violating this assumption.
I want to see an example where something is printed to stdout (or stderr) that would not be printed if the myth were true or where something is not printed that would be or where something is printed, but the text is wrong. Why the insistence on printing to stdout? I have seen some examples that use writing to a volatile variable as an "externally observable" side effect. I am not very convinced by those examples because I do not see how I would be able to actually observe those writes in the common case of a regular process in user-space (i.e not the OS kernel, device driver or embedded).
Edit: Yakk - Adam Nevraumont pointed out that writing to stdout is done via library calls that the compiler can not reason about. It occurred to me that there exists another form of interprocess communication that I think the compiler might be able to reason about - atomic writes to memory shared with another process. Unfortunately it sounds like interprocess communication by shared-memory is not codified in the normative parts of the C++ standard
The printing has to be done by code that is executed before the line responsible for undefined behaviour. I am not interested in examples of wrong output being printed after the line with undefined behaviour has already been executed. Note: since it does not make sense to talk about execution of a program whose behaviour is undefined, please insert "if the behaviour were not undefined" where appropriate when interpreting this paragraph.
Edit: I found a better way to word this: I am asking for an example that would demonstrate a difference between the real undefined behaviour "all bets are off as soon as the program gets an input for which the behaviour of the program is undefined" and a fictional version of undefined behaviour "the program executes normally until it hits the line that causes undefined behaviour and after that all bets are off". I want to see an example of the code that would have executed normally if the myth were true doing something strange instead. Code that goes "after" the bad line misbehaving in some way can be explained by nasal daemons under both the correct and the incorrect mental model of undefined behaviour and is thus not a counterexample.
Here is the closest I was able to find:
void bar (void);
int a;
void foo3 (unsigned y, unsigned z)
{
bar();
a = y%z;
}
According to the blog linked above some (unknown) version of clang compiles this to:
foo3:
pushl %ebp
movl %esp, %ebp
pushl %esi
subl $4, %esp
movl 8(%ebp), %eax
xorl %edx, %edx
divl 12(%ebp) <-- might crash the program
movl %edx, %esi
call bar <-- might be side effecting
movl %esi, a
addl $4, %esp
popl %esi
popl %ebp
ret
I was able to replicate a similar assembly output in clang 3.3 on godbolt, but not on more recent versions of clang.
I believe this example to be a compiler bug. Clang appears to assume that bar() is guaranteed to return, but C has exit()
and longjump()
- the code after the call to bar() may be dead code. The behaviour of a program is undefined only if the execution state that does a bad thing is reachable.
Rules:
- Include a godbolt link or an alternative means of specifying the CPU architecture, OS, compiler version, standard library version and other technical details required to enable others to reproduce your results
- If your example does not use stdout or stderr despite the above request, please explain how I can observe the "externally observable" effect your example uses to demonstrate "time travel" without using a virtual machine, running your code on bare metal, attaching a debugger or using a disassembler.
- Flush the output buffers. I do not find missing output caused by data being stuck in a buffer when the program crashes convincing
- No hypothetical examples. I am not asking about what the C standard allows the compiler to do or what it could plausibly do. I would like to see a real example of a compiler actually doing this
- No compiler bugs
- Only C or C++