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

Closed
cbeuw opened this issue Jun 12, 2023 · 13 comments · Fixed by #127190
Closed

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

cbeuw opened this issue Jun 12, 2023 · 13 comments · Fixed by #127190
Assignees
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 llvm-fixed-upstream Issue expected to be fixed by the next major LLVM upgrade, or backported fixes 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. WG-llvm Working group: LLVM backend code generation

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.

@Noratrieb Noratrieb 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

@nikic
Copy link
Contributor

nikic commented Jun 21, 2024

Does this issue still reproduce?

@alex
Copy link
Member

alex commented Jun 21, 2024

Running on an MBP M1, with rustc 1.81.0-nightly (8337ba9 2024-06-12), I get the same hash output for both of the executions listed:

/tmp ❯❯❯ rustc +nightly -Zmir-opt-level=0 -Copt-level=0 repro.rs && ./repro
warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:72:16
   |
72 |         0.hash(&mut H);
   |                ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
   = note: `#[warn(static_mut_refs)]` on by default
help: use `addr_of_mut!` instead to create a raw pointer
   |
72 |         0.hash(addr_of_mut!(H));
   |                ~~~~~~~~~~~~~~~

warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:73:18
   |
73 |         _32.hash(&mut H);
   |                  ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
help: use `addr_of_mut!` instead to create a raw pointer
   |
73 |         _32.hash(addr_of_mut!(H));
   |                  ~~~~~~~~~~~~~~~

warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:82:20
   |
82 |         _9[0].hash(&mut H);
   |                    ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
help: use `addr_of_mut!` instead to create a raw pointer
   |
82 |         _9[0].hash(addr_of_mut!(H));
   |                    ~~~~~~~~~~~~~~~

warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:83:17
   |
83 |         _8.hash(&mut H);
   |                 ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
help: use `addr_of_mut!` instead to create a raw pointer
   |
83 |         _8.hash(addr_of_mut!(H));
   |                 ~~~~~~~~~~~~~~~

warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:84:21
   |
84 |         1_i128.hash(&mut H);
   |                     ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
help: use `addr_of_mut!` instead to create a raw pointer
   |
84 |         1_i128.hash(addr_of_mut!(H));
   |                     ~~~~~~~~~~~~~~~

warning: 5 warnings emitted

hash: 15917201677548574216
/tmp ❯❯❯ rustc +nightly -Zmir-opt-level=0 -Copt-level=1 repro.rs && ./repro
warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:72:16
   |
72 |         0.hash(&mut H);
   |                ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
   = note: `#[warn(static_mut_refs)]` on by default
help: use `addr_of_mut!` instead to create a raw pointer
   |
72 |         0.hash(addr_of_mut!(H));
   |                ~~~~~~~~~~~~~~~

warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:73:18
   |
73 |         _32.hash(&mut H);
   |                  ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
help: use `addr_of_mut!` instead to create a raw pointer
   |
73 |         _32.hash(addr_of_mut!(H));
   |                  ~~~~~~~~~~~~~~~

warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:82:20
   |
82 |         _9[0].hash(&mut H);
   |                    ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
help: use `addr_of_mut!` instead to create a raw pointer
   |
82 |         _9[0].hash(addr_of_mut!(H));
   |                    ~~~~~~~~~~~~~~~

warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:83:17
   |
83 |         _8.hash(&mut H);
   |                 ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
help: use `addr_of_mut!` instead to create a raw pointer
   |
83 |         _8.hash(addr_of_mut!(H));
   |                 ~~~~~~~~~~~~~~~

warning: creating a mutable reference to mutable static is discouraged
  --> repro.rs:84:21
   |
84 |         1_i128.hash(&mut H);
   |                     ^^^^^^ mutable reference to mutable static
   |
   = note: for more information, see issue #114447 <https://github.com/rust-lang/rust/issues/114447>
   = note: this will be a hard error in the 2024 edition
   = note: this mutable reference has lifetime `'static`, but if the static gets accessed (read or written) by any other means, or any other reference is created, then any further use of this mutable reference is Undefined Behavior
help: use `addr_of_mut!` instead to create a raw pointer
   |
84 |         1_i128.hash(addr_of_mut!(H));
   |                     ~~~~~~~~~~~~~~~

warning: 5 warnings emitted

hash: 15917201677548574216

@nikic
Copy link
Contributor

nikic commented Jun 21, 2024

@alex Thanks! Would you mind also trying the other test case in #112548 (comment)?

@alex
Copy link
Member

alex commented Jun 21, 2024

That version does reproduce the issue:

/tmp ❯❯❯ rustc +nightly repro.rs && ./repro
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
/tmp ❯❯❯ rustc +nightly -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

@nikic
Copy link
Contributor

nikic commented Jun 21, 2024

Thanks!

@DianQK
Copy link
Member

DianQK commented Jun 28, 2024

I can reproduce this.
@rustbot claim

@DianQK
Copy link
Member

DianQK commented Jun 29, 2024

Upstream issue: llvm/llvm-project#97147.

@DianQK
Copy link
Member

DianQK commented Jun 30, 2024

@rustbot label +llvm-fixed-upstream

@rustbot rustbot added the llvm-fixed-upstream Issue expected to be fixed by the next major LLVM upgrade, or backported fixes label Jun 30, 2024
bors added a commit to rust-lang-ci/rust that referenced this issue Jul 2, 2024
Update LLVM submodule

Fixes rust-lang#112548 and unlock rust-lang#125642.

r? `@cuviper` or `@nikic`
@bors bors closed this as completed in 77fa597 Jul 4, 2024
rust-timer added a commit to rust-lang-ci/rust that referenced this issue Jul 4, 2024
Rollup merge of rust-lang#127190 - DianQK:llvm-backport, r=nikic

Update LLVM submodule

Fixes rust-lang#112548 and unlock rust-lang#125642.

r? ``@cuviper`` or ``@nikic``
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 llvm-fixed-upstream Issue expected to be fixed by the next major LLVM upgrade, or backported fixes 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. WG-llvm Working group: LLVM backend code generation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants