Skip to content

Mutating through addr_of produces LLVM IR with UB #111502

Closed
@cbeuw

Description

@cbeuw

As there is a write through a *const derived pointer, this code has UB under Stacked Borrows, but is fine under Tree Borrows,

pub fn first() -> bool {
    let mut a = (0., true);
    let b = (0., false);
    let a_ptr_and_b = (core::ptr::addr_of_mut!(a), b);
    let got = unsafe { second(a_ptr_and_b.0, a_ptr_and_b, true, true) };
    return got;
}

unsafe fn second(
    a_ptr: *mut (f32, bool),
    a_ptr_and_b: (*mut (f32, bool), (f64, bool)),
    t: bool,
    t_copy: bool,
) -> bool {
    let b_bool_ptr = core::ptr::addr_of!(a_ptr_and_b.1 .1) as *mut bool;
    let t_or_t = t | t_copy;
    let t_xor_t = t ^ t_copy;
    (*b_bool_ptr) = t_or_t ^ t_xor_t;
    let unused = a_ptr_and_b;
    return a_ptr_and_b.1.1 == (*a_ptr).1;
}

pub fn main() {
    println!("{}", first());
}

-C opt-level=0 results in true (which Miri agrees and is the right result), but -C opt-level=3 miscompiles it to print false

% rustc -Zmir-opt-level=0 -C opt-level=0 repro.rs && ./repro
true
% rustc -Zmir-opt-level=0 -C opt-level=3 repro.rs && ./repro
false

Interestingly, if you remove unused in function second, the miscompilation goes away. -Zmir-opt-level=0 is required to prevent mir-opt from doing this.

Directly getting a *mut through addr_of_mut! in the first line of second also makes the bug go away.

rustc --version -v
rustc 1.71.0-nightly (2a8221dbd 2023-05-11)
binary: rustc
commit-hash: 2a8221dbdfd180a2d56d4b0089f4f3952d8c2bcd
commit-date: 2023-05-11
host: aarch64-apple-darwin
release: 1.71.0-nightly
LLVM version: 16.0.2

cc @nikic @RalfJung

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-mir-optArea: MIR optimizationsT-opsemRelevant to the opsem team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions