Unwinding within foreign ABIs can corrupt registers
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
Run this on Linux in release mode (https://play.rust-lang.org/?version=stable&mode=release&edition=2024&gist=5fba6c5632078d3225298bbd29188b7e):
fn main() {
let _dropper = Dropper(core::hint::black_box(1));
panic_within_win64_abi();
}
struct Dropper(i32);
impl Drop for Dropper {
fn drop(&mut self) {
println!("Dropper({})", { self.0 });
}
}
#[inline(never)]
extern "win64-unwind" fn panic_within_win64_abi() {
panic!();
}
I expected to see this happen: Dropper(1)
Instead, this happened: Dropper(-1078274960)
What's happening here is:
- The ABI for
drop_in_placeforDropperis chosen to receiveself.0inedi, so the number1is preemptively stored inediat the beginning ofmain. - Since
panic_within_win64_abiisextern "win64-unwind"and the x86-64 Windows CC hasrdias a callee-saved register, LLVM assumes thatediwon't be corrupted and doesn't spilledito stack, assuming thatdrop_in_placewill receive1inedi. - This would be correct, if not for the fact that
panic_within_win64_abipanics, which activates the native unwinding machinery, which has tons of internal functions and runs tons of landing pads, none of which preserverdisince that's not mandated by the native CC, soedieventually gets corrupted.
This is an LLVM bug. LLVM should not assume that any registers except the ones specified by the native CC survive unwinding, even from within another CC. I've reported it months ago in https://github.com/llvm/llvm-project/issues/112943 but got no response. Thought I'd report it here as well, since it can easily be triggered by safe code and someone might want to take a look.
Meta
rustc --version --verbose:
rustc 1.90.0-nightly (a00149764 2025-07-14)
binary: rustc
commit-hash: a001497644bc229f1abcc5b2528733386591647f
commit-date: 2025-07-14
host: x86_64-unknown-linux-gnu
release: 1.90.0-nightly
LLVM version: 20.1.8
@rustbot label +A-ABI +A-extern-fn +A-LLVM +A-panic +I-miscompile +T-compiler
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the linked Rust Playground reproducer in Linux release mode and compare the observed Dropper value with the expected value. Investigate the interaction between the extern "win64-unwind" ABI, native unwinding, and LLVM's register preservation assumptions; done means the reproducer no longer corrupts Dropper(1).
Written by the indexing model from the issue text.
Assessment
- Tech stack
- linux, rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100