We've all heard the ghost stories about undefined C programs that delete hard disks or set the computer on fire.
However, usually, C programs with undefined behavior either crash or seem to be working.<p>So, has anyone ever experienced behavior that went beyond that?
I was unable to find the quote, but someone once said that "We always focus on the negative effects of undefined behavior, but literally anything could happen. It doesn't have to be bad! Let's replace fear with hope."
In the late 80's, two of my fellow C programmers and I thought that we might want to embed a copyright string into all of our executable programs that comprised a system that we were building. These were 16-bit MS-DOS programs. It seemed like an innocuous idea.<p>Each C program in our system included a header file with a few globals that looked something like this:<p><pre><code> char *dave="Copyright ... blah, blah";
char *bob="More magic and mirth from the mavericks in MicroDev";
char *jim="CAPS Sucks!";
</code></pre>
The system we were replacing was written in a proprietary programming language called CAPS that was a royal pain to work with.<p>A decade or so later, after we had moved on to other projects and other companies, one of the old users called me. They were using the system to board a customer and in the printed paperwork that the system generated the words "CAPS Sucks!" showed up! They caught the error before sending the paperwork to the new customer, manually correcting it.<p>I talked with a new programmer overseeing the system. They eventually found the bug in some code they'd added that caused a pointer to go astray which ended up pointing to constants in early portions of memory ( early because those three strings were in the first #include file in the set.) Why it skipped over the first two strings, I'll never know for sure.
Another undefined behaviour error:<p>We had a macro BAG which looked like:<p><pre><code> #define BAG(x) **x
</code></pre>
and people would write things like:<p><pre><code> BAG(x) = f(y);
</code></pre>
Now, we had a moving garbage collector, which could cause the value of <i>x to change, so it was important that this was executed "as if".<p><pre><code> temp = f(y); **x = temp;
</code></pre>
For many years, this was exactly what gcc always did, but this wasn't required by C. An update to gcc caused, when optimisation was high enough and gcc felt like it, to instead first dereferencing x, and then calculating f(y), then assigning f(y) to the old value of </i><i>x.<p>But, this would only cause a problem when calling f(y) caused a GC which would change </i>x, and then a random memory location was written to.
The problem was subtle -- some part of a program was writing to a memory location it shouldn't be, making random memory locations 0.<p>We had a custom moving GC. Fixing the bug involved changing the GC so, we used mmap to map an anonymous buffer storing the GCed objects into many memory locations, and each time a memory block was allocated:<p>1) Choose a random copy of the memory.<p>2) Mark all the other blocks as neither readable or writable.<p>3) Make all references to GC memory we were currently tracking point to that block.<p>This meant anyone who was keeping a pointer to GCed memory we had lost track of would point to a now protected block of memory, and as soon as they tried to read or write through it would cause a segfault.<p>This made the program run slower.. so much slower the startup, which used to be about 3 seconds was now about 6 hours. However, it found the bug (and a whole bunch of other GC bugs as well).
Early in my programming “career”, when I was about 15/16, I started writing network programs in C. I was fascinated by the idea that one program could send and receive data with another far far away. One particular story was a great lesson for me (I eventually ended up naming my company after it), and also pretty amusing. It’s not about undefined behaviour, but about not properly understanding C strings. If you’re curious, the story is here: <a href="https://martinrue.com/zzuy-a-lesson-in-perseverance" rel="nofollow">https://martinrue.com/zzuy-a-lesson-in-perseverance</a>
I guess you weren't exaggerating: <a href="https://www.quora.com/Can-a-code-be-written-to-blow-up-a-computer" rel="nofollow">https://www.quora.com/Can-a-code-be-written-to-blow-up-a-com...</a>
Mine is a simple off by one byte write causing a failure in a separate thread in proprietary barely related code on deinitialization of the library.
It took years to find. Not even ASAN, valgrind and debug allocators helped.
What did was a full audit including assembly.<p>They had their own memory allocator and internal threads, the off by one corrupted an atomic flag... Since this is data dependent on a relatively cold path the issue did not appear that often. And less predictably because it was ARM.<p>Note the multiple stacked undefined behaviors.
> We've all heard the ghost stories about undefined C programs that delete hard disks<p>Exactly that one. Someone at some job had written a Linux kernel driver - for a subsystem which was definitely not related to any disk IO. This driver had a bug and was subject to a race condition which it lead it in some cases to write to memory which it didn't own. That caused hard drive corruptions on some devices.<p>I also had another one in an embedded software project: A "filesystem" on the flash memory got corrupted for multiple reasons: The responsible code was missing synchronization - if two threads would be writing at the same time the result was basically undefined. And it also missed checks for dynamic memory allocation failures, and would then to undefined things.<p>Debugging all those issues had not been particularly fun, and made me a firmer believer in safer programming languages.
Only crashes, as far as I know.<p>I met a bug from 2010 in Openresty where if you set ngx.header["Cache-Control"] = nil then sometime later maybe nginx will segfault.<p>nginx uses a pointer to char and length for strings, like any sane C program. For headers that are always present on the response, setting the header value to nil from Lua is implemented as setting it to {NULL, 0} (which is different from the empty string, as far as the lua side is concerned). Sometime later, nginx will call strlcasestrn passing this header value to check whether it contains "private". This involves adding the length of the string to the start pointer to calculate the upper bound. So that's NULL + 0, which is whatever the compiler feels like it is at the time.
I recall once reading a bug-report thread of how null-check elisions caused a vulnerability in a big piece of software.
I've tried to find it often, because it would be nice to reference in response to your kind of question.<p>I'm afraid that more than an hour of searching hasn't turned anything up. I think it was a Firefox bug, but given my fruitless search, that seems suspect.<p>Here's to hoping someone else happens to know what I am talking about and shares a link.
I was reading some data off of the network and storing it in a byte array. Then it was being loaded into some SIMD registers (ARM NEON) to do mathy stuff. Ended up in type punning hell when the code ran fine on some machines but not others. IIRC it worked on machines running Linux but not on bare metal.
I had one for shift by negative number. It was in some error correction code. A different team had wrote this code and tested on a PC but the actual hardware used an ARM processor and both implement it differently. We did catch it eventually using a linter tool.