Rust's "Memory Safe" Promise Had a Hole For 11 Years. Nobody Noticed.

Bottom line: Rust issue #25860, a lifetime-related soundness hole in the compiler, has been open since April 2015, which makes it more than eleven years old.

It lets safe code, with no `unsafe` block, turn a short-lived reference into a `'static` one and from there reach use-after-free and arbitrary type confusion.

It has never been reported as exploited in the wild, because it takes deliberately contrived code to trigger.

That doesn't make the hole fake: it means Rust's safety guarantee is a strong empirical property, not yet a proven one, and you should plan around the difference.

The Fix Is Not Coming This Quarter

Nobody "found" this bug last week. It's been public for eleven years, with a reproduction short enough to fit in a tweet.

The recent noise on Hacker News is about it getting attention again, and about how little the surrounding picture has changed.

I want to be careful about what I'm claiming here, because this topic attracts two kinds of bad takes. One says Rust is a lie. The other says the bug doesn't matter because nobody writes this code.

Both are wrong in ways worth taking apart.

Everything below comes from the public issue tracker, the Rust project's own writing, and code you can compile yourself. I'm not citing private conversations, because I don't have any to cite.

What the Hole Actually Is

The bug is about implied bounds on higher-ranked lifetimes. The compiler, when checking a function type like `for<'a, 'b> fn(&'a &'b ())`, is supposed to know that `'b` outlives `'a`.

That implied bound is dropped when the function pointer type is converted to another type, and variance rules then let you launder the lifetime.

Here's the canonical reproduction. This compiles on stable, with zero `unsafe`:

```rust static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T { let f: fn(&'static &'a (), &'a T) -> &'static T = foo; f(UNIT, x)

} ```

Read the signature of `bad`. It takes a reference that lives for `'a` and returns a reference that lives forever. The borrow checker is supposed to reject exactly this.

It doesn't, because the proof obligation that would catch it was dropped on the floor two steps earlier.

From there, the path to real memory corruption is well worn.

Return a `&'static` to something on the stack, let the frame die, and you have a dangling pointer produced by code the compiler certified as safe.

Why Nobody Noticed (and Why That's Complicated)

The honest answer is that people did notice. The issue is labeled, triaged, and has a long thread. What nobody did was fix it, and the reason is more interesting than negligence.

The bug isn't a typo in one function. It lives in how the current trait solver and region checker represent implied bounds.

A correct fix means changing foundations, and a foundations change risks breaking real crates in ways the language team promised not to.

Rust's stability guarantee is a hard constraint, not a suggestion.

So the project's long-term answer has been structural. The new trait solver effort and the formal modeling work around it are the path to closing this class of bug properly.

That's a multi-year program, not a patch release.

I'll say plainly what this means: the soundness hole survived because the correct fix is expensive, and the exploit is unnatural. Those two facts reinforced each other for a decade.

The Strongest Counterargument

Here's the position I'd give the most weight to, and it's one many compiler people hold: this is not a safety problem in practice. Ordinary Rust never produces this pattern.

You can write Rust full-time for ten years and never construct a `for<'a>` function pointer with a lifetime-mismatched `'static` coercion on purpose.

That's true. The bug requires adversarial intent, or at least a very odd accident.

I'm not aware of any report of fuzzers stumbling into it, and real-world memory-safety CVEs in Rust codebases overwhelmingly come from `unsafe` blocks and FFI boundaries, not from the type checker.

The other side of that argument is a distinction I find worth keeping. "Unlikely in practice" and "sound" are different claims.

A language that markets a guarantee, and gets deployed on that basis, owes its users the precise version of that guarantee.

And the precise version is: safe Rust is memory safe, assuming the compiler has no soundness bugs, and here is a known one.

What the Evidence Says

Let me put some numbers around the hand-waving, using only things that are checkable.

FactStatus
Issue #25860 openedApril 2015
Rust 1.0 releasedMay 2015
Reproduction needs `unsafe`No
Public proof-of-concept crate exploiting itYes (`cve-rs`)
Known in-the-wild exploitationNone I'm aware of

The `cve-rs` crate deserves a mention.

It's a deliberately provocative project that packages this class of bug into a library, letting you cause segfaults and transmute between arbitrary types from safe code.

It exists as a protest and a demonstration, not an attack tool, and it makes the argument "nobody can realistically write this" harder to sustain. Somebody did write it, and it's a dependency away.

The tracker also holds a long list of other soundness issues, labeled `I-unsound`. Most are far more obscure than this one, and most don't have a fix scheduled either.

Any compiler of this complexity has such a list, and C++ compilers have worse ones, with no formal claim to refute.

That last point is where the skeptic in me gets honest. I spend a lot of time pointing out that AI tooling gets sold on guarantees it can't back up. Rust doesn't do that.

The project documents these issues openly, labels them, and refuses to hide them. That's the correct behavior, and it's rarer than it should be.

The Part Where My Skepticism Cuts Both Ways

There's a modern wrinkle that I think people are underweighting.

For eleven years, "requires deliberately contrived code" was a decent defense because the people writing code were humans who wanted their programs to work.

That assumption is weaker now. Code is increasingly generated by models that pattern-match from the whole internet, including pathological examples.

And if you're running untrusted code in a sandbox that relies on Rust's type system as the security boundary, then "no one would write this" isn't a defense at all. It's the attacker's opening move.

That's the practical threat model: not your CRUD service, but any system that treats safe Rust as a security boundary against hostile input. Think plugin hosts, WASM-adjacent runtimes, online judges, and code playgrounds.

If your isolation story is "it's safe Rust, so it's contained," you are relying on a property the project itself says isn't fully proven.

This matters more as Rust moves into places where the stakes are high. Meta's WhatsApp, for instance, rewrote its media handler in Rust, replacing a large C++ codebase. That's a sound engineering call.

Memory-unsafe parsers handling hostile media files are exactly where the old language bleeds.

The type-checker hole doesn't change that calculus, because the bar is "far better than C++," not "mathematically perfect." But it's worth knowing which bar you're actually clearing.

What This Means for You

If you ship Rust, the practical takeaways are short:

1. Don't treat safe Rust as a sandbox for hostile code. Use OS-level isolation, seccomp, VMs, or WASM engines with their own hardening.

Defense in depth isn't optional when the outer layer is a language guarantee.

2. Audit your `unsafe` first. The soundness bugs that actually bite in production live there, in FFI, and in dependencies. This compiler bug is a footnote beside a single bad `unsafe impl Send`.

3. Pin toolchains and read release notes. When soundness fixes land, they sometimes break code that relied on the hole. That's the system working as intended.

4. Run Miri on your unsafe code. It won't catch this specific bug, but it catches the much more common class of mistakes you're far more likely to make.

5. Be precise in what you promise. "Memory safe unless the compiler has a soundness bug" is a longer sentence, and it's the true one.

None of this is a reason to go back to C. The relevant comparison isn't Rust versus perfection.

It's Rust versus the thing it replaces, where the equivalent bug class shows up weekly rather than once a decade, and where nobody has even a theoretical fix to wait for.

Eleven Years In

The thing I keep coming back to is the shape of the failure. A one-screen program, accepted by the compiler, violates the language's central promise.

It's been sitting in public view since before the language hit 1.0. It hasn't hurt anyone that we know of.

I find that reassuring and unsettling in equal measure. Reassuring, because it says the engineering is holding in the places that matter.

Unsettling, because it says guarantees can have holes that stay open not because nobody cares but because the fix is hard, and because the exploit is awkward enough to look like a curiosity.

Rust will probably close this hole properly, through the new solver, within a few years.

When it does, the language will be more trustworthy than it is today, and the bug will have done its job as a very public to-do item.

Until then, the guarantee is real, strong, and not absolute. Which of those three words do you think your team is actually relying on?

Story Sources

Hacker Newsgithub.com