Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscompilation on aarch64-apple-darwin (likely a calling convention bug) #112548

Open
cbeuw opened this issue Jun 12, 2023 · 5 comments
Open

Miscompilation on aarch64-apple-darwin (likely a calling convention bug) #112548

cbeuw opened this issue Jun 12, 2023 · 5 comments
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness O-AArch64 Armv8-A or later processors in AArch64 mode O-macos Operating system: macOS P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@cbeuw
Copy link
Contributor

cbeuw commented Jun 12, 2023

Fuzzer generated code:

#![feature(const_hash)]
extern crate core;
use core::ptr;

use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

static mut H: DefaultHasher = DefaultHasher::new();

pub fn fn10() {
    let mut _11 = 0;
    let mut _29 = 0;
    let p = core::ptr::addr_of_mut!(_29);
    unsafe { fn14(_11, p) };
}

pub unsafe fn fn14(mut _2: isize, mut _3: *mut isize) {
    let r = core::ptr::addr_of_mut!(_2);
    *r = fn15(r, _3);
    fn18();
}
pub unsafe fn fn15(mut _2: *mut isize, mut _3: *mut isize) -> isize {
    let mut _4: *mut isize = ptr::null_mut();
    let mut _7: *mut *mut isize = ptr::null_mut();
    let mut _13: *mut isize = ptr::null_mut();
    let mut _16: *mut isize = ptr::null_mut();

    let mut _9: ((bool, i128), (f32, i128), [u128; 8], u32) = ((false, 0), (0., 0), [0; 8], 0);
    _4 = core::ptr::addr_of_mut!((*_3));
    fn16();
    'bb1: loop {
        let ret = *_4;
        _7 = core::ptr::addr_of_mut!(_2);
        _9.3 = 3178558635_u32 % 2879763616_u32;
        _9.0 = (false, 1);
        'bb3: loop {
            (*_7) = core::ptr::addr_of_mut!((*_2));
            _16 = core::ptr::addr_of_mut!((*_2));
            match _9.0 .1 {
                0 => continue 'bb1,
                1 => 'bb5: loop {
                    _4 = _2;
                    _9.2 = [279331277471898888451578203748788445758_u128; 8];
                    _7 = core::ptr::addr_of_mut!(_13);
                    loop {
                        _9.1 = (0., 2);
                        (*_16) = f64::NEG_INFINITY as isize;
                        match _9.1 .1 {
                            0 => continue 'bb5,
                            2 => {
                                _16 = core::ptr::addr_of_mut!((*_4));
                                match _9.3 {
                                    0 => continue 'bb5,
                                    _ => return ret,
                                }
                            }
                            _ => continue 'bb3,
                        }
                    }
                },
                _ => continue 'bb3,
            }
        }
    }
}

pub fn fn16() {
    let mut _23 = [1849_i16; 7];
    _23[2] = 13917_i16;
    let _32 = _23;
    unsafe {
        0.hash(&mut H);
        _32.hash(&mut H);
    }
}

pub fn fn18() {
    let mut _8: [i16; 8] = [0; 8];
    let _5 = -10_i128;
    let _9 = [(-1414_i16); 7];
    unsafe {
        _9[0].hash(&mut H);
        _8.hash(&mut H);
        1_i128.hash(&mut H);
    }
}

pub fn main() {
    fn10();
    unsafe {
        println!("hash: {}", H.finish());
    }
}

This should output 15917201677548574216: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a51ce49127180037639732b4a990ddbe.

But on -Zmir-opt-level=0 -Copt-level>=1 it prints something different. -Zmir-opt-level>=1 masks the bug.

% rustc -Zmir-opt-level=0 -Copt-level=0 repro.rs && ./repro
hash: 15917201677548574216
% rustc -Zmir-opt-level=0 -Copt-level=1 repro.rs && ./repro
hash: 2617855399195014552

This is only reproducible on aarch64-apple-darwin, but not on x86_64-apple-darwin or aarch64-unknown-linux-gnu. The reproducer is very sensitive to small changes. For instance, changing the length of _23 in fn16 to 8 prevents the bug. For these reasons I suspect it's an Apple Silicon specific calling convention bug leading to some stack corruption.

@Nilstrieb Nilstrieb added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness C-bug Category: This is a bug. labels Jun 12, 2023
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Jun 12, 2023
@Jules-Bertholet
Copy link
Contributor

@rustbot label O-aarch64 O-macos

@rustbot rustbot added O-AArch64 Armv8-A or later processors in AArch64 mode O-macos Operating system: macOS labels Jun 12, 2023
@apiraino
Copy link
Contributor

WG-prioritization assigning priority (Zulip discussion).

@rustbot label -I-prioritize +P-high

@rustbot rustbot added P-high High priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Jun 13, 2023
@apiraino apiraino added the A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. label Jun 13, 2023
@cbeuw
Copy link
Contributor Author

cbeuw commented Jun 15, 2023

I have another reproduction of what I believe is the same bug, but this one is only 34 lines, has only safe code and doesn't need a hasher:

use std::fmt::Debug;

pub fn dump_var<T: Debug>(
    f: usize,
    var0: usize,
    val0: T,
    var1: usize,
    val1: i32,
    var2: usize,
    val2: i32,
    var3: usize,
    val3: i32,
) {
    println!(
        "fn{f}:_{var0} = {val0:?}\n_{var1} = {val1:?}\n_{var2} = {val2:?}\n_{var3} = {val3:?}"
    );
}

fn fn10() {
    let _2 = 302053680377554952743809083898710279615_u128;
    let mut _5 = _2.wrapping_shl(_2 as u32);
    let mut _18: (u128, [isize; 3]) = (0, [0; 3]);
    _18.1 = [9223372036854775807_isize; 3];
    dump_var(11, 0, 0, 0, 0, 0, 0, 0, 0);
    dump_var(11, 0, 0, 0, 0, 0, 0, /* addend */ 1, 0);
    let _1 = _5 | _18.0;
    let _11 = _5.wrapping_mul(_1);
    _5 = _11;
    dump_var(10, 5, _5, 0, 0, 0, 0, 0, 0);
}

pub fn main() {
    fn10();
}

This should print

fn11:_0 = 0
_0 = 0
_0 = 0
_0 = 0
fn11:_0 = 0
_0 = 0
_0 = 0
_1 = 0
fn10:_5 = 85070591730234615865843651857942052864
_0 = 0
_0 = 0
_0 = 0

but with -Zmir-opt-level=0 -Copt-level=3

% rustc -Zmir-opt-level=0 -Copt-level=3 repro.rs && ./repro
fn11:_0 = 0
_0 = 0
_0 = 0
_0 = 0
fn11:_0 = 0
_0 = 0
_0 = 0
_1 = 0
fn10:_5 = 85070591730234615865843651857942052865
_0 = 0
_0 = 0
_0 = 0

The value of _5 is different. In fact, the number commented "addend" in the original code gets added to _5 in the miscompiled result. If you change it to e.g. 42 you get _5 = 85070591730234615865843651857942052906

@nikic
Copy link
Contributor

nikic commented Jun 23, 2023

Any chance that the fix for #112767 also fixes this one? Looks kinda similar to me.

@cbeuw
Copy link
Contributor Author

cbeuw commented Jun 23, 2023

No it fixes neither program in this issue.

llvm/llvm-project#63475 might be related, since it also passes a vector on the stack

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness O-AArch64 Armv8-A or later processors in AArch64 mode O-macos Operating system: macOS P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants