Rust's borrow checker lets a stack reference slip past a 'static bound on stable
Rust issue #164250 reports a borrow-checker unsoundness that reproduces on stable, with variants compiling since 1.18.0 in 2017. Exploiting it takes a deliberately hostile Drop impl, and the team downgraded it from P-critical to P-high.
The Engineer · Build desk
What happened
- jsnell filed the issue on October 13 with a reproduction that compiles and prints freed memory.
- In the reproduction, assigning a stack-local reference into a field after construction unifies the types covariantly, and the T: 'static bound is never re-checked.
- A Drop impl that assumes the bound held pushes the value into a static list, and a later downcast_ref reads freed stack memory.
- Commenter maxdexh contributed a version of the unsoundness that goes back to Rust 1.4.
- A fix is in motion as PR #164277, and lcnr linked the issue to the next-gen trait solver and Project Zero rework.
Why it matters
- constraint The trigger is wider than a field assignment. maxdexh showed mem::swap reaches the same gap, so a search for post-construction assignments will miss variants.
- exposure A hostile Drop impl in any dependency is enough to reach the bug, so the exposure sits in the dependency tree for anyone who treats a clean compile as the safety check.
- decision Teams that compile untrusted crates have to choose between waiting for PR #164277 to land and auditing now for Drop impls that stash generic values in statics.
For code to be hit, it needs three things: a type with a T: 'static bound, a way for a shorter-lived reference to get into that type, and a Drop impl that relies on the bound [4][5]. The reproduction supplies all three.
The post's author ran it on the official playground, stable channel, and got the line "read after scope: hello world, this is long enough", with the same result on nightly [3]. The stack slot had already been freed. Per the issue, the same program under AddressSanitizer crashes with stack-use-after-scope [6].
Everything here comes through one dev.to post that relays the thread. The downgrade rests on age. According to the post, lcnr's pinned note says the issue would have been P-critical regardless of whether anyone hit it in the wild, and it is P-high because it sat latent for eleven years [8]. The note's own words, as the post quotes them, are "very likely that this unsoundness has not caused memory unsafety by accident" [9].
The post frames the result as a floor under the proof: borrow check, then variance rules, then trait bounds, then an unexecuted assumption that Drop impls respect the bounds they inherit [19]. We'd put it differently. maxdexh described the general case as "unification just generally doesn't check lifetime bounds of the type" [16]. That is a checker defect. A Drop impl that trusts T: 'static is relying on what the bound promises [5], and in our view the fix belongs in the checker.
The thread had friction of its own. theemathas asked "How on earth was this not found before this???" [13]. The issue was briefly locked as too heated, then unlocked and re-locked to collaborators [18].
What to watch
- PR #164277 merging, and which stable release carries it.
- Whether the next-gen trait solver work lcnr linked to changes the P-high label.
- Any report of the bug firing in real code without a hostile Drop impl, which would undercut the pinned note's reasoning that it has not happened by accident.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence68
- Adoption
- Insufficient
- Hype gap−10
- Incentives
- Insufficient
- Confidence65
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
Issue #164250 on the rust-lang tracker reports an unsoundness in the borrow checker that reproduces on current stable.
- [2]
Variants of the unsoundness have compiled since Rust 1.18.0 (2017).
- [3]
The post's author ran the reproduction on the official playground, stable channel, and it printed 'read after scope: hello world, this is long enough' after the stack slot was freed; the result was the same on nightly.
- [4]
In the reproduction, struct Q<T: 'static + Send> demands its payload outlive the program; assigning a field after construction (q.0 = Some(&l), where l is stack-local) unifies the types covariantly, and the unification does not re-check the 'static bound.
- [5]
A Drop impl that pushes the value into a static list assumes the bound held and leaks the reference, and a downcast_ref::<&'static String>() then reads freed stack memory.
- [6]
With AddressSanitizer, per the issue, the same program crashes with stack-use-after-scope.
- [7]
The issue is rated P-high, downgraded from P-critical.
- [8]
lcnr's pinned note says the issue would have been P-critical regardless of whether anyone encountered it in the wild, and it is P-high only because it has been latent for eleven years.
- [9]
The pinned note says it is "very likely that this unsoundness has not caused memory unsafety by accident."
- [10]
Per the issue, the exploit requires "an intentionally malicious Drop implementation."
- [11]
jsnell filed the issue on October 13 with a reproduction that compiles and prints freed memory.
- [12]
theemathas added the I-unsound label and a minimal core repro, and traced a variant compiling "ever since 1.18.0 (2017)".
- [13]
theemathas asked in the thread: "How on earth was this not found before this???"
- [14]
maxdexh contributed a version of the unsoundness that goes back to Rust 1.4.
- [15]
maxdexh generalized the flaw to include mem::swap, where no field assignment is needed.
- [16]
maxdexh said: "unification just generally doesn't check lifetime bounds of the type".
- [17]
lcnr pinned the linkage between the issue and the next-gen trait solver and Project Zero rework; PR #164277 is the fix in motion.
- [18]
Per the issue, the thread was briefly locked as too heated, then unlocked and re-locked to collaborators.
- [19]
The post's author describes the proof as a ladder of borrow check, variance rules and trait bounds, resting at the floor on an unexecuted assumption that Drop implementations respect the bounds they inherit.
Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.toThe 11-year-old premise inside Rust's memory-safety proof
1 article · October 14, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.
Topics
- RustFollow
- Memory SafetyFollow
- Compiler soundnessFollow