Skip to content

program miscompiles to division by zero with checked_div().unwrap_unchecked() #164431

Closed

Description

@466774321-code

I tried this code:

static mut a: u64 = 0u64;
static mut b: bool = false;
static mut c: *mut u8 = 0 as *mut u8;
static mut d: bool = false;
static mut e: *mut u8 = (&raw mut a).cast::<u8>();
static mut f: bool = false;
unsafe fn h0() {
    let mut h = false;
    let mut i = false;
    let mut j = 8997201173137027981;
    let mut k = 0u32;
    let mut l = 0u64;
    let mut st = 0;
loop {
    match st {
        0 => {
            st = 1;
        }
        1 => {
            l = j ^ l;
            st = 3;
        }
        3 => {
            h = f;
            i = (0 >= (l as i64));
            k = k.wrapping_add(4050710252);
            if h { st = 6; } else { st = 5; }
        }
        5 => {
            f = true;
            if k == 4050710252 { st = 1; } else { st = 8; }
        }
        6 => {
            unsafe { e = (&raw mut e).cast(); }
            unsafe { g(0); }
            st = 9;
        }
        9 => {
            if i { st = 13; } else { st = 14; }
        }
        10 => {
            i = ((j as i64) < -2451427860696281925i64);
            j = a;
            if h { st = 9; } else { st = 12; }
        }
        13 => {
            if i { st = 22; } else { st = 23; }
        }
        14 => {
            if h { st = 27; } else { st = 28; }
        }
        22 => {
            st = 10;
        }
        27 => {
            j = (unsafe { (l as i64).checked_div((j as i64)).unwrap_unchecked() } as u64);
            st = 48;
        }
        _ => break,
    }
}
}
unsafe fn g(mut m: u64) {
    let mut n = false;
    let mut o = 0u8;
    let mut t = 93;
loop {
    match t {
        4 => {
            if n { t = 18; } else { t = 17; }
        }
        17 => {
            unsafe { std::ptr::write(e.cast::<*mut u8>(), 0 as *mut u8); }
            m = e as usize as u64;
            c = (&raw mut o).cast();
            t = 93;
        }
        93 => {
            d = d | b;
            let (_, u) = m.overflowing_mul(m);
            b = u;
            n = d & b;
            b = c.is_null();
            if n | b { t = 4; } else { t = 6; a = 1; }
        }
        _ => break,
    }
}
}
fn main() {
    unsafe { h0(); }
}```

Save the code as `case.rs`, then run:

```text
rustc +nightly --edition=2024 -C opt-level=0 -Awarnings case.rs -o case_o0 && ./case_o0
rustc +nightly --edition=2024 -C opt-level=2 -Awarnings case.rs -o case_o2 && ./case_o2

I expected both commands to exit successfully.

Instead, the optimized executable terminates with SIGFPE (exit code 136). On x86-64, gdb stops at a generated div instruction with a zero divisor.

The current reduction is 92 numbered lines / 2141 bytes. The file contains 91 newline characters because it does not end with an extra newline. The md5 is e08323f83dd8b2ee86a758bba4278b6d.

Observed with the latest nightly tested:

rustc 1.101.0-nightly (7df81381d 2026-10-14)
binary: rustc
commit-hash: 7df81381d027710708209f36b98d11b1c88f8d2e
commit-date: 2026-10-14
host: x86_64-unknown-linux-gnu
release: 1.101.0-nightly
LLVM version: 23.1.3

Results, six runs per configuration:

       -O0       -O1       -O2       -O3       -Os       -Oz
case   0/6       6/6       6/6       6/6       6/6       6/6

Checks

  • Miri with Stacked Borrows: clean.
  • Miri with Tree Borrows: clean.
  • Adding #[inline(never)] to g makes the optimized crash disappear.
  • Replacing the checked_div(...).unwrap_unchecked() expression with wrapping_div makes the optimized crash disappear.
  • A non-PIE build did not reproduce in 10 runs; the default PIE build reproduces.

The failure appears to involve cross-function inlining, the lowering of checked_div(...).unwrap_unchecked(), llvm.assume/undef on the None path, address-dependent pointer state, and later LLVM optimization. I have not isolated the exact LLVM pass transformation that produces the zero divisor.

Meta

rustc +nightly --version --verbose:

rustc 1.101.0-nightly (7df81381d 2026-10-14)
binary: rustc
commit-hash: 7df81381d027710708209f36b98d11b1c88f8d2e
commit-date: 2026-10-14
host: x86_64-unknown-linux-gnu
release: 1.101.0-nightly
LLVM version: 23.1.3

Activity

  1. added
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 16, 2026
  2. maxdexh commented on Oct 16, 2026

    @maxdexh
    Member

    Reproduces with mir-opt-level=0

  3. added
    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.
    I-miscompileIssue: Correct Rust code lowers to incorrect machine code
    and removed
    needs-triageThis issue may need triage. Remove when done. See docs forge.rust-lang.org/release/issue-triaging
    on Oct 16, 2026
  4. added
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Oct 16, 2026
  5. maxdexh commented on Oct 16, 2026

    @maxdexh
    Member

    Godbolt link: https://godbolt.org/z/ab6j6ea8a

    This also reproduces on 1.90, before LLVM 23

  6. added
    E-needs-bisectionCall for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc
    on Oct 16, 2026
  7. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    E-needs-mcveCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example
    on Oct 16, 2026
  8. apiraino commented on Oct 16, 2026

    @apiraino
    Contributor

    Even on 1.87.0. IIUC: https://godbolt.org/z/9x1Tejbv4
    Assigning priority (discussion on Zulip).

    @rustbot label -I-prioritize +P-medium

  9. added and removed
    I-prioritizeIssue needs a team member to assess the impact. Will be replaced by P-{low,medium,high,critical}
    on Oct 16, 2026
  10. jieyouxu commented on Oct 16, 2026

    @jieyouxu
    Member

    Would be good if someone could try to see if they can minimize this further. While the example is self-contained it's still quite large.

  11. RobertObkircher commented on Oct 16, 2026

    @RobertObkircher
  12. ds84182 commented on Oct 16, 2026

    @ds84182

    force inlining g fixes the miscompilation so LLVM thinks some global variables aren't modified

  13. GBra-4-669 commented on Oct 17, 2026

    @GBra-4-669

    I think this is already fixed upstream.

    The report's nightly (7df81381d, LLVM 23.1.3) just misses the LLVM bump in #164254. On nightly-2026-10-16 (LLVM 23.1.4) both case.rs and the reducer exit 0 at -O1/-O2/-O3; on nightly-2026-10-15 case.rs SIGFPEs 3/3 and the reducer exits 101. So this is probably closable as llvm-fixed-upstream, like #164137.

    The checked_div is only where the wrong value becomes fatal. The cause looks like the GlobalsAA bug from #164137 (a global whose own address is stored into itself isn't treated as address-taken): llvm/llvm-project#213232, fixed by llvm/llvm-project#213631.

    Reducer, Miri-clean (Stacked and Tree Borrows), wrong at -O1+ on 1.87.0 and 1.99.0 (godbolt). This should be usable as a regression test:

    static mut E: *mut u8 = 0 as *mut u8;
    
    #[inline(never)]
    unsafe fn g() -> usize {
        unsafe {
            let p = E;
            std::ptr::write(p.cast::<*mut u8>(), std::ptr::null_mut());
            E as usize
        }
    }
    
    fn main() {
        unsafe {
            E = (&raw mut E).cast::<u8>();
            assert_eq!(g(), 0);
        }
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-bugCategory: This is a bug.E-needs-bisectionCall for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustcE-needs-mcveCall for participation: This issue has a repro, but needs a Minimal Complete and Verifiable ExampleI-miscompileIssue: Correct Rust code lowers to incorrect machine codeP-mediumMedium priorityT-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