deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (native)

Decade-old Rust unsoundness lets safe code smuggle non-'static references past the type checker

A rust-lang/rust issue shows the compiler has accepted lifetime-bound violations for up to a decade, letting safe code pass non-'static references to code that assumes 'static, with use-after-free consequences.

Decade-old Rust unsoundness lets safe code smuggle non-'static references past the type checker

A bug report filed in the rust-lang/rust repository on October 13, 2026 demonstrates that the Rust compiler has been accepting programs that violate its own lifetime rules for a very long time — in one variant, since Rust 1.4 — and that the resulting type violations can produce dangling references and use-after-free behavior without a single line of unsafe code.

The issue was opened by GitHub user theemathas and quickly triaged as P-high by the project's issue bot following discussion on the Rust Zulip. It was retitled to describe the root cause: a struct's declared lifetime requirements can be bypassed through covariant field assignment.

How the exploit works

The original proof of concept defines a generic struct with a T: 'static + Send bound and a Drop implementation. The Drop code takes the value out of the struct and pushes it into a global, mutex-protected collection of boxed 'static trait objects — which is only sound because the type system promises that T lives for the remainder of the program.

The caller then breaks that promise. A local String is created, and a reference to it is assigned into the struct's field. Because shared references are covariant in their lifetime, the assignment is accepted even though the value being written has a much shorter lifetime than the bound on the type parameter demands. When the struct is dropped at the end of the block, the reference to the dead local is stored away as if it were 'static.

The demonstration then allocates a fresh string that happens to reuse the same stack slot, and successfully downcasts the smuggled reference back to a &'static String, printing the new string's contents. Under AddressSanitizer, the same program aborts with a stack-use-after-scope report, confirming that the reference genuinely outlives its referent. According to the reporter, the code compiles on current nightly and on stable releases at least as far back as 1.85.0.

A family of failures, not a single bug

The comment thread shows this is not an isolated quirk. Compiler contributor lcnr posted a variant that needs no global state at all: a struct holding a mutable reference to a longer-lived reference alongside a shorter-lived one, whose Drop implementation writes the short one through the long one, leaving a dangling reference that is printed after the inner scope ends.

theemathas contributed perhaps the clearest version: Drop spawns a thread that sleeps, then dereferences the supposedly 'static reference while the original stack frame is long gone.

Commenter maxdexh went further, reporting a variant that segfaults or prints freed memory as far back as Rust 1.4, released in 2015 — the origin of the roughly eleven-year figure — and arguing that the underlying problem is not specific to field assignment at all. In their reading, the compiler's unification logic generally fails to re-check the lifetime bounds of a type when two types are made equal, and they supplied an example using ordinary type unification across array elements plus a field swap to produce the same violation.

theemathas also noted that their own version has compiled without complaint since Rust 1.18.0 in 2017.

Why it matters

Rust's core selling point is that memory safety is enforced by the compiler, so that unsafe behavior requires an explicit unsafe block that can be audited. An unsoundness like this breaks that contract: any program depending on it can exhibit undefined behavior written entirely in safe Rust.

The practical exposure is uneven, though. Code that merely uses lifetimes normally is unlikely to trip over this; the danger concentrates where someone writes code — especially unsafe code — that relies on a 'static or outlives bound as a hard invariant. A library exposing such an API can be handed a dangling reference by an ordinary, well-meaning caller, and the resulting bug will surface inside the library rather than at the assignment that caused it.

For maintainers of generic code with lifetime bounds and custom Drop implementations, the issue is a prompt to audit those patterns and to run test suites under sanitizers, which catch this class of error reliably. For the Rust project, it is a reminder that even the most heavily tested type checkers can carry a blind spot for a decade. At the time of the report, no fix had landed.

  • #rust
  • #compiler
  • #type-system
  • #memory-safety
  • #security

Related posts