rust-lang / rust-lang/rust

Unwinding within foreign ABIs can corrupt registers

Open
#144,371 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-ABI A-extern-fn A-LLVM A-panic C-bug I-miscompile T-compiler
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_place for Dropper is chosen to receive self.0 in edi, so the number 1 is preemptively stored in edi at the beginning of main.
  • Since panic_within_win64_abi is extern "win64-unwind" and the x86-64 Windows CC has rdi as a callee-saved register, LLVM assumes that edi won't be corrupted and doesn't spill edi to stack, assuming that drop_in_place will receive 1 in edi.
  • This would be correct, if not for the fact that panic_within_win64_abi panics, which activates the native unwinding machinery, which has tons of internal functions and runs tons of landing pads, none of which preserve rdi since that's not mandated by the native CC, so edi eventually 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.