Skip to content

Unwinding within foreign ABIs can corrupt registers #144371

@purplesyringa

Description

@purplesyringa

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 llvm/llvm-project#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

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ABIArea: Concerning the application binary interface (ABI)A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.A-extern-fnArea: `extern` functionsA-panicArea: Panicking machineryC-bugCategory: This is a bug.I-miscompileIssue: Correct Rust code lowers to incorrect machine codeT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions