# Notes on C++ undefined behavior
[This post was originally a twitter thread](https://x.com/ChadNauseam/status/1922404295359549594)
You write the following C++ code. Put it into a C++ compiler, compile it, and run it. Suddenly, your bank account is drained, your passwords are changed, and your nudes get leaked. What went wrong? And can we use this to disprove Fermat's Last Theorem?
```C++
int main() {
int v;
if (v == 0) {
cout << "test" << endl;
}
return 0;
}
```
Wait a second. You didn't write any code that drains your bank account. So why would your compiler produce a binary that does that? Well guess what: there are absolutely no rules what the C++ compiler is supposed to do given the code you write.
Sure, it may be against the US legal code for the compiler authors to make it do that, but C++ compiler authors answer to a higher authority: the C++ specification. And the C++ specification says explicitly that the compiler can do whatever it wants with that code.
That's because the code you wrote exhibits undefined behavior: access of an uninitialized scalar. There are "no restrictions" on what code the compiler should generate for programs that exhibit undefined behavior. The compiler authors are untethered and you are at their mercy.
![[Pasted image 20250513232030.png]]
Now, realistically, your code will **not** compile to malware, but that's just because the compiler authors are nice people who understand that you probably need that money more than they do. But: it's still possible that undefined behavior could introduce security vulnerabilities.
This is because people assume that "something reasonable" will happen when you write code with undefined behavior, but that isn't true. Look at this code. In practice, will it print "p is true" or "p is false"?
```C++
int main()
{
bool p;
if (p)
std::puts("p is true");
if (!p)
std::puts("p is false");
}
```
Trick question. A C++ compiler could print BOTH "p is true" and "p is false"! Other versions of the same compiler or other compilers might print nothing at all. The C++ standard says they can do whatever they want, and they take advantage. But has this ever been a problem in practice?
Unsurprisingly, yes. There was a security vulnerability in Chromium where they accidentally introduced some undefined behavior in a security-critical check. This caused the compiler to make the check do nothing.
![[Pasted image 20250513235054.png]]
Essentially, they wrote (1 << 32). Turns out, shifting a 32-bit integer by 32 bits is undefined behavior, and they got got. Luckily, the bug was fixed before it was exploited (and this particular bug would likely have been difficult to exploit in the first place).
However, this slipped through the eyes of FIVE Google C++ developers. If it can happen to them, it can happen to you.
![[Pasted image 20250513235150.png]]
Let's compare this to Rust. People say that Rust guarantees memory safety in safe code, but it actually does more than that: safe Rust has no undefined behavior!
(Of course, that is conditional on the unsafe Rust only exposing sound APIs to safe code. And it's conditional on the Rust compiler not being buggy in a way that affects your program's execution. But in practice, those constraints are much easier to meet than C++'s)
BTW, I have no problem with C++. In my free time I develop a C++ game engine. I just think the language is funny. Let's do a few more for fun. I'm taking these examples from cppreference btw. What happens if you pass the largest possible signed integer to this function?
```C++
int foo(int x) {
return x + 1 > x;
}
```
You might expect it to overflow, causing it to return false, but signed integer overflow is undefined. So this code actually compiles to `return true;` on many compilers!
![[Pasted image 20250513235305.png]]
![[Pasted image 20250513235314.png]]
![[Pasted image 20250513235322.png]]
Let's try another one. What does this compile to?
```c++
int table[4] = {};
bool exists_in_table(int v)
{
for (int i = 0; i <= 4; i++)
if (table[i] == v)
return true;
return false;
}
```
Answer: it always returns true!
The compiler sees that the last iteration of the loop does an array access out of bounds, and so it goes ahead and obliterates your whole function (to always return true without doing any work).
![[Pasted image 20250513235514.png]]
Ok, these have been pretty easy so far. But did you know you might need to be a literal mathematical genius to know how the C++ specification constrains your compiler's codegen?
Andrew Wiles is an English mathematician specializing in number theory. What he's best known for is proving a conjecture first put forward by Pierre de Fermat around 1637. The conjecture became well known for it's importance and the hilarious way it was stated.
![[Pasted image 20250513235544.png]]
Fermat wrote in the margin of a book that "no three positive integers a, b, and c satisfy the equation a^n + b^n = c^n for any integer value of n greater than 2". He claimed that he had a proof, but that the margin was too small for him to write it down, so it would have to come in a future book.
He then died, and 350 years later so many people had tried and failed to prove his statement that it was assumed to be unsolvable. But Andrew Wiles attacked the problem nonstop for 6 years in total secrecy until he finally proved it true, and concluded with possibly the highest-effort troll of all time
![[Pasted image 20250513235627.png]]
![[Pasted image 20250513235640.png]]
![[Pasted image 20250513235652.png]]
The excellent cppreference has outdone themselves once again and created a program which appears to test this. Do you see any issue with this code?
```C++
bool fermat()
{
const int max_value = 1000;
// let's loop forever until we find a
// counterexample to Fermat's Last Theorem
for (int a = 1, b = 1, c = 1; true; )
{
if ((a * a * a) == (b * b * b + c * c * c))
return true;
a++;
if (a > max_value)
{
a = 1;
b++;
}
if (b > max_value)
{
b = 1;
c++;
}
if (c > max_value)
c = 1;
}
return false; // not disproved
}
int main()
{
std::cout << "Fermat's Last Theorem ";
fermat()
? std::cout << "has been disproved!\n"
: std::cout << "has not been disproved.\n";
}
```
If Fermat's last theorem is true, the loop will go on looking for counterexamples forever. If the theorem is false, the `return true` branch will eventually be taken. We all know the theorem is true, so it should hopefully run forever.
Guess what: This code will output `Fermat's Last Theorem has been disproved!` on mainstream compilers!
Here's why. If the theorem is true, this is a non-trivial infinite loop with no side effects. which is undefined behavior. That means the C++ spec allows any codegen. So the optimizer really can simplify the loop to merely `return true;`!
I couldn't believe it at first, but this is [real behavior implemented by real compilers](https://godbolt.org/z/aqdz67rP1)!
![[CleanShot 2025-05-14 at
[email protected]]]
That's all folks! If you want my honest opinion on C++, check out my article on the subject: [[the-good-and-bad-of-cpp-as-a-rust-dev]].