rust-lang / rust-lang/rust

Miscompile from LLVM assuming that globals don't get merged

Open
#162,078 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-linkers A-LLVM C-bug I-miscompile I-unsound needs-triage P-medium T-compiler T-opsem
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

This issue is the exact inverse of #161973. Instead of compiling code that assumes things living at the same address have the same contents (which there is no guarantee for), this issue demonstrates the optimizer making that assumption instead.

As such, the code below is obviously sound (the unsafe is only to get it to weaponize the already UB noundef undef). It outputs (modulo exact address)

thread 'main' (1) panicked at /app/example.rs:6:5:
assertion `left == right` failed
  left: 106946049351680
 right: 106946049351680

https://godbolt.org/z/z5redezGj

use std::mem::MaybeUninit;

fn get_first_if_half(p: &[u32]) -> u32 {
    let half = const { &[MaybeUninit::uninit(), MaybeUninit::new(0)] };
    
    // Simply observe that the addresses are equal, without making
    // assumptions about what that implies
    assert_eq!(p.as_ptr().addr(), half.as_ptr().addr());

    // LLVM will happily use that to inline the `undef` from `half` here.
    // But uninit globals can get merged into init globals by the linker!
    p[0]
}

pub fn main() {
    // Linker will merge this with `half` from the other function
    let full = std::hint::black_box(const { &[0, 0] });

    // Below is just exploitation to make it miscompile. 
    // LLVM is returning `undef` for the `u32`, which is already UB

    if get_first_if_half(full) != get_first_if_half(full) {
        // SAFETY: We loaded the first item in `full` twice; 
        // 0 != 0 is impossible.
        unsafe { std::hint::unreachable_unchecked() }
    }
}

@rustbot label A-llvm A-linkers I-unsound I-miscompile T-opsem 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

No repository file or test is named. Begin with the Rust reproducer and its Godbolt link, then investigate the interaction between LLVM's assumptions and linker merging of globals. The report proposes no specific change or acceptance test, so completion criteria require maintainer agreement.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.