The Bug Hall of Fame

Type This First

This is the essence of Heartbleed in 15 lines. Save as heartbleed_demo.c:

#include <stdio.h>
#include <string.h>

char secret[] = "MY_SECRET_KEY_12345";
char buffer[64];

void heartbeat(int claimed_length) {
    // Copies 'claimed_length' bytes — but doesn't check
    // if the actual data is that long!
    char response[64];
    memcpy(response, buffer, claimed_length);
    printf("Response (%d bytes): ", claimed_length);
    for (int i = 0; i < claimed_length; i++)
        printf("%c", response[i] >= 32 ? response[i] : '.');
    printf("\n");
}

int main(void) {
    strcpy(buffer, "hi");            // actual payload: 2 bytes
    heartbeat(50);                   // but we claim 50 bytes!
    return 0;
}

Compile and run. You will see memory contents beyond what was sent — possibly including MY_SECRET_KEY_12345. This is exactly how Heartbleed worked.


Why This Chapter Exists

These are not theoretical bugs. These are real vulnerabilities that affected billions of devices, cost billions of dollars, and compromised millions of passwords.

Every single one is a memory safety bug. Every single one would not compile in safe Rust.

This is not about blaming C. C was designed in 1972 for a world where programs were small and programmers were few. The question for today is: should the compiler catch these mistakes, or should we rely on human discipline at scale?


Heartbleed (CVE-2014-0160)

What happened. OpenSSL's TLS heartbeat extension had a buffer over-read. A client sends a heartbeat message with a payload and a claimed length. The server echoes back claimed_length bytes — without checking that the actual payload is that long.

The root cause in C:

// Simplified from the actual OpenSSL code:
memcpy(response, payload, claimed_length);
//                        ^^^^^^^^^^^^^^
// Never verified: claimed_length <= actual_payload_size

Impact. Any server running affected OpenSSL would leak up to 64KB of process memory per request. That memory contained passwords, private keys, session tokens. An estimated 17% of the internet's secure servers were vulnerable.

How Rust prevents it. In Rust, slices carry their length. &[u8] knows how many bytes it contains. You cannot memcpy past the end — the runtime panics (bounds check) or the compiler prevents it entirely.

    Client sends:          Server does:
    payload = "hi"         memcpy(buf, payload, 500)
    claimed_len = 500      ^^^^^^^^^^^^^^^^^^^^^^^^
                           Copies 500 bytes starting at "hi"
                           Next 498 bytes: whatever's in memory
                           Private keys, passwords, sessions...

sudo Baron Samedit (CVE-2021-3156)

What happened. A heap-based buffer overflow in sudo — the program that grants root access. Present for almost 10 years before discovery.

The root cause. When parsing command-line arguments in sudoedit mode, a backslash at the end of a string caused a write past the end of a heap buffer.

// Simplified pattern:
while (*from) {
    if (from[0] == '\\' && from[1] != '\0')
        from++;  // skip backslash
    *to++ = *from++;  // write to heap buffer
}
// If the string ends with '\', from[1] reads past the null terminator
// and the loop keeps writing past the buffer boundary

Impact. Any local user could gain root access on nearly every Unix-like system. CVSSv3 score: 7.8.

How Rust prevents it. Ownership and bounds checking. A Vec<u8> in Rust will panic on out-of-bounds write, or better — the iterator-based approach would never produce an out-of-bounds index.


WannaCry / EternalBlue (CVE-2017-0144)

What happened. The EternalBlue exploit targeted a buffer overflow in Windows' SMBv1 implementation. The WannaCry ransomware used it to spread across networks.

The root cause. A buffer overflow in the Windows SMB driver (srv.sys). A specially crafted SMB transaction caused a pool buffer overflow in kernel memory.

Impact. Over 200,000 computers in 150 countries. Hospitals shut down. Factories stopped. Estimated damage: $4 billion or more.

How Rust prevents it. Safe Rust does not allow buffer overflows. Period. Slice access is bounds-checked. Vec access is bounds-checked. You would need unsafe to bypass this, and unsafe blocks are visible and auditable.


iOS Jailbreaks: Use-After-Free in WebKit

What happened. Many iOS jailbreaks (and zero-day exploits) have been based on use-after-free bugs in Safari's WebKit rendering engine.

The pattern:

Widget *w = create_widget();
destroy_widget(w);          // frees the memory
// ... more code ...
w->render();                // USE AFTER FREE
// The memory at w might now contain attacker-controlled data

Impact. Full device compromise. In the hands of nation-states, these exploits were used for surveillance. The Pegasus spyware used WebKit vulnerabilities.

How Rust prevents it. Ownership. When you free (drop) a value, the compiler will not let you use it again. The borrow checker ensures that no references outlive the data they point to.

#![allow(unused)]
fn main() {
let w = create_widget();
drop(w);
// w.render();  // COMPILE ERROR: use of moved value `w`
}

The Chromium Data

Google published the numbers for Chromium (Chrome's open-source base):

+---------------------------------------------+
| Chromium Security Bugs by Category           |
|                                              |
|  Memory safety:     ~70%  <-- THIS           |
|  Logic errors:      ~15%                     |
|  Other:             ~15%                     |
+---------------------------------------------+

Seventy percent. Not 7%. Seven-zero. The dominant source of security vulnerabilities in one of the most-used programs on Earth is memory safety.

Google's response: new Chromium components are increasingly written in Rust and memory-safe C++.


The Android Data

Android's security team reported a similar pattern:

Year    Memory Safety Bugs (%)   Rust Code (%)
2019           76%                   0%
2020           72%                   ~1%
2021           65%                   ~5%
2022           55%                  ~10%
2023           40%                  ~15%

As the percentage of new code written in Rust increased, the percentage of memory safety bugs decreased — even though the total codebase grew. The existing C/C++ code was not rewritten; new code was simply written in Rust.

Fun Fact

As of 2024, there have been ZERO memory safety vulnerabilities discovered in Android's Rust code. Not "few." Zero.


Linux Kernel: Rust for New Drivers

In 2022, Rust support was merged into the Linux kernel. The philosophy:

  • Existing C code stays as C. It works. It is well-tested.
  • New drivers and modules can be written in Rust.
  • The goal is preventing new bugs, not rewriting old code.

Linus Torvalds approved this not because C is bad, but because humans make mistakes, and the kernel cannot afford those mistakes.


The Bug Pattern Table

+---------------------+------------------+-----------------------------+
| Bug Type            | Frequency in     | Rust Prevention             |
|                     | C Codebases      |                             |
+---------------------+------------------+-----------------------------+
| Buffer overflow     | Very common      | Bounds checking on all      |
|   (read or write)   |                  | slice/array access          |
+---------------------+------------------+-----------------------------+
| Use-after-free      | Common           | Ownership: compiler tracks  |
|                     |                  | when values are dropped     |
+---------------------+------------------+-----------------------------+
| Double free         | Common           | Ownership: only one owner,  |
|                     |                  | dropped exactly once        |
+---------------------+------------------+-----------------------------+
| Null pointer deref  | Very common      | No null: Option<T> forces   |
|                     |                  | explicit handling            |
+---------------------+------------------+-----------------------------+
| Data race           | Common in        | Send/Sync: compiler refuses |
|                     | concurrent code  | unsafe sharing              |
+---------------------+------------------+-----------------------------+
| Uninitialized read  | Common           | All variables must be       |
|                     |                  | initialized before use      |
+---------------------+------------------+-----------------------------+
| Format string       | Occasional       | No format strings; macros   |
|                     |                  | are type-checked             |
+---------------------+------------------+-----------------------------+
| Integer overflow    | Common           | Panics in debug, wrapping   |
|                     |                  | explicit in release          |
+---------------------+------------------+-----------------------------+

Not Blaming C

C is one of the most important languages ever created. It powers operating systems, databases, embedded systems, and the infrastructure of the internet. Billions of devices run C code.

The bugs in this chapter are not C's fault. They are human mistakes. The question is not whether humans make mistakes — they do, always, inevitably — but whether the toolchain should catch those mistakes before they reach production.

What do you think happens?

If a large company rewrites all their C code in Rust, are they now bug-free? What kinds of bugs does Rust NOT prevent? (Hint: think about logic errors, deadlocks, and incorrect algorithms.)

Rust prevents memory safety bugs. It does not prevent wrong business logic, algorithmic errors, deadlocks, resource leaks from forgetting to close files, or plain old bad design.

But eliminating 70% of security vulnerabilities is a powerful argument.


Timeline of Awareness

2014  Heartbleed shocks the world
2017  WannaCry causes $4B+ damage
2019  Microsoft: 70% of CVEs are memory safety
2020  Google: 70% of Chromium bugs are memory safety
2021  Baron Samedit: sudo exploitable for 10 years
2022  Rust merged into Linux kernel
2023  Android: zero memory-safety bugs in Rust code
2024  White House recommends memory-safe languages

The industry is moving. Not because C is bad, but because the stakes are too high for human discipline alone.


Task

  1. Look up CVE-2014-0160 (Heartbleed) on any CVE database. Read the description.
  2. Find the actual OpenSSL patch that fixed it. The key change is a bounds check — identify the exact line.
  3. Write a 10-line Rust equivalent of the heartbeat function. Try to make it read out of bounds. Observe the panic.
  4. Look up CVE-2021-3156. Can you identify the buffer overflow pattern?
  5. Bonus: Search for "memory safety vulnerabilities" + your favorite open-source project. How many of the CVEs are buffer overflows or use-after-free?