rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.72k stars 12.64k forks source link

Miscompilation: wrong branch taken on x86_64 #112767

Closed cbeuw closed 1 year ago

cbeuw commented 1 year ago

Fuzzer generated code then minimised

use std::ptr;

pub fn dump_var(
    f: usize,
    var0: usize,
    val0: i32,
    var1: usize,
    val1: i32,
    var2: usize,
    val2: i32,
    var3: usize,
    val3: i32,
) {
    println!("fn{f}:_{var0} = {val0}, _{var1} = {val1}, _{var2} = {val2}, _{var3} = {val3}");
}

unsafe fn fn2(mut _2: *mut i32) {
    println!("entering fn2");
    let mut np: *mut i128 = ptr::null_mut();
    let mut npp = ptr::addr_of_mut!(np);
    let mut byte: u8 = 0;
    let byte_ptr = ptr::addr_of_mut!(byte);
    let mut discr: i128 = 0;
    let mut _16 = ptr::addr_of_mut!(discr);
    *_16 = 19769033156256055278156766228563896803_i128;
    fn3(npp, byte_ptr, [0; 4], _2, 0, *_16);
    loop {
        npp = ptr::addr_of_mut!(_16);
        match discr {
            3 => {
                discr = 0;
                fn3(npp, byte_ptr, [0; 4], _2, *_16, *_16);
            }
            _ => return,
        }
    }
}

pub unsafe fn fn3(
    mut _5: *mut *mut i128,
    byte_ptr: *mut u8,
    mut _7: [i64; 4],
    mut _8: *mut i32,
    mut _9: i128,
    mut big_num: i128,
) {
    println!("entering fn3");
    (*_5) = ptr::addr_of_mut!(big_num);
    *byte_ptr = 0;
    fn4(big_num, *_8, _5, _9, *_5);
    _7 = [0; 4];
}

pub unsafe fn fn4(
    big_num: i128,
    mut _7: i32,
    mut _8: *mut *mut i128,
    mut _12: i128,
    mut _13: *mut i128,
) {
    println!("entering fn4");
    (*_13) = 0; // This cannot change big_num as it was passed by value
    match big_num {
        19769033156256055278156766228563896803 => {
            println!("right branch");
            (*_8) = ptr::addr_of_mut!(_12);
            fn5(_7)
        }
        _ => {
            println!("wrong branch");
            return;
        }
    }
}

pub fn fn5(mut _1: i32) {
    println!("entering fn5");
    dump_var(5, 1, _1, 0, 0, 0, 0, 0, 0);
}

pub fn main() {
    let mut _6: i32 = 0;
    let _16 = ptr::addr_of_mut!(_6);
    unsafe { fn2(_16) }
}

Miri reports no UB under either aliasing model, and it should print

entering fn2
entering fn3
entering fn4
right branch
entering fn5
fn5:_1 = 0, _0 = 0, _0 = 0, _0 = 0

With -Zmir-opt-level=0 -Copt-level>=2, it takes the wrong branch in fn4 and doesn't call fn5.

$ rustc -Zmir-opt-level=0 -Copt-level=2 repro.rs && ./repro
entering fn2
entering fn3
entering fn4
wrong branch

Only reproducible on x86_64, not aarch64

rustc 1.72.0-nightly (3b2073f07 2023-06-17)
binary: rustc
commit-hash: 3b2073f0762cff4d3d625bb10017e0ce4e7abe50
commit-date: 2023-06-17
host: x86_64-unknown-linux-gnu
release: 1.72.0-nightly
LLVM version: 16.0.5

cc @nikic

cbeuw commented 1 year ago

There is a pointer to a stack allocation that outlasts its lifetime: (*_8) = ptr::addr_of_mut!(_12);

But this isn't https://github.com/llvm/llvm-project/issues/51838, as -Cllvm-args=-protect-from-escaped-allocas=true doesn't prevent the miscompilation, and this dangling pointer is never dereferenced, even in not taken branches

nikic commented 1 year ago

Likely SDAG bug.

BISECT: NOT running pass (1376) X86 DAG->DAG Instruction Selection on function (_ZN4test3fn317h707ba6185b238b46E)
nikic commented 1 year ago

If I'm reading the assembly right, this should be a reduction of the issue: https://github.com/llvm/llvm-project/issues/63430

apiraino commented 1 year ago

WG-prioritization assigning priority (Zulip discussion).

@rustbot label -I-prioritize +P-high +T-compiler

nikic commented 1 year ago

Fixed upstream by https://github.com/llvm/llvm-project/commit/81ec494c363d4934e692e8b35e0b3fbbc3de1c2b.

nikic commented 1 year ago

Fixed by https://github.com/rust-lang/rust/pull/114048

Godbolt: https://rustc.godbolt.org/z/6eTqszPWx