AI NewsDev toolsAnnouncement

Rust borrow checker lets a swap bypass lifetimes, 11 years undetected

A short Rust program using only safe code and no explicit unsafe blocks can swap the inner fields of two values with different lifetime bounds, violating a core soundness promise the Rust compiler has carried since 2015 and which Miri now flags as a dangling reference.

AI News

Editorial2 min read

LinkedInX
GitHub issue preview card for rust-lang/rust issue 164250

Image: GitHub

Why it mattersA team shipping safe Rust in production can treat the bug as low exposure, because a real exploit needs an intentionally malicious Drop implementation that nobody writes by accident, and still note that Miri is now the fastest check against the pattern.

A short Rust program that uses only safe code and declares no unsafe blocks can bypass the lifetime rules the borrow checker is supposed to enforce, through a swap of the first field of two values that happen to have different lifetime bounds. The issue, rust-lang/rust#164250, was labelled P-high and I-unsound by the Rust Types Team and reached 740 points on Hacker News within a day of being filed.

The reproducer builds and runs on current stable rustc 1.99.0 and nightly rustc 1.101.0, printing [[]]. Compile the same program under nightly Miri and the undefined-behaviour checker reports a dangling reference, a use-after-free, inside the closure that was captured with the long-lived reference. The real reference was swapped out and dropped while the closure was still able to reach it.

What the program does

Rust ships a safety promise: programs written without unsafe cannot reach freed memory or produce other kinds of memory unsafety. The reproducer in the issue holds that promise under test. It defines a generic Q<T, F> struct whose destructor calls the closure field F with the captured value T. It builds a two-element array where one element captures a &'static reference and another captures a shorter-lived &'a reference, then calls std::mem::swap on the first fields of the two array elements through a mutable destructuring pattern.

The borrow checker should reject this. The short-lived reference ends up assigned where a 'static reference belongs, and the destructor then uses it after the shorter borrow has ended. The compiler accepts the program, which is the bug.

What the Types Team is saying

The P-high label and a note from @lcnr on the issue explain the priority. The bug would have been P-critical regardless of whether anyone encountered it in the wild, were it not already 11 years old. A bug nobody has hit in 11 years cannot be easy to hit, the comment adds, and reaching real memory unsafety from it needs an intentionally malicious Drop implementation. The fix sits in the compiler, not the type system: the written rules were correct, so the compiler needs to reject the program the rules reject on paper. GrantMoyer's top-rated comment on Hacker News makes the same point.

The issue sits in the T-types unsound-issues project under the Rust organisation, marked open/unblocked. The borrow checker rewrite goals under Project Zero already cover the surrounding work.

Where the news lands for a team shipping Rust

A production codebase that does not swap fields of structs with different-lifetime closure captures is not reachable through this bug by accident. A team can still shorten its own exposure by running Miri over the Rust tests it already has, which is what caught the undefined behaviour here. The reproducer and its Godbolt link, rust.godbolt.org/z/fn455rzKW, both sit in the first issue comment.

Source

Issue 164250 in rust-lang/rust on GitHub and the Hacker News discussion.

SourceGitHub

This item was written by an AI system from the linked source. Reveneau is responsible for what it publishes.

Share
LinkedInX
Start a project