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

Miscompile in the GVN transform #130853

Closed
jwong101 opened this issue Sep 25, 2024 · 14 comments · Fixed by #133474
Closed

Miscompile in the GVN transform #130853

jwong101 opened this issue Sep 25, 2024 · 14 comments · Fixed by #133474
Assignees
Labels
A-codegen Area: Code generation A-mir-opt Area: MIR optimizations I-miscompile Issue: Correct Rust code lowers to incorrect machine code I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-mir-opt Working group: MIR optimizations

Comments

@jwong101
Copy link
Contributor

jwong101 commented Sep 25, 2024

I tried this code:

unsafe fn src(x: &&u8) -> bool {
    let y = **x;
    unknown();
    **x == y
}

static mut SUSSY: *mut u8 = core::ptr::null_mut();

#[inline(never)]
unsafe fn unknown() {
    *SUSSY = 1;
}

fn main() {
    let mut s = 0;
    unsafe {
        SUSSY = core::ptr::addr_of_mut!(s);
        println!("{}", src(&*core::ptr::addr_of!(SUSSY).cast::<&u8>()));
    }
}

I expected to see this happen:

This should print false, as I believe this is DB under both Stacked and Tree borrows(according to MIRI).

Instead, this happened:

It returns false in Debug mode, and the GVN MIR pass makes src() unconditionally return true in Release mode.

$ cargo miri run
Compiling sus v0.1.0 (/Users/jwong3/test/sus)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
     Running `/Users/jwong3/.rustup/toolchains/nightly-aarch64-apple-darwin/bin/cargo-miri runner target/miri/aarch64-apple-darwin/debug/sus`
false
$ cargo run -r
   Compiling sus v0.1.0 (/Users/jwong3/test/sus)
    Finished `release` profile [optimized] target(s) in 0.31s
     Running `target/release/sus`
true

Meta

Here's the MIR before the GVN pass:

// MIR for `src` before GVN

fn src(_1: &&u8) -> bool {
    debug x => _1;
    let mut _0: bool;
    let _2: u8;
    let _3: ();
    let mut _4: u8;
    let mut _5: u8;
    let mut _6: &u8;
    let mut _7: &u8;
    scope 1 {
        debug y => _2;
    }

    bb0: {
        StorageLive(_2);
        _6 = deref_copy (*_1);
        _2 = copy (*_6);
        _3 = unknown() -> [return: bb1, unwind continue];
    }

    bb1: {
        StorageLive(_4);
        _7 = deref_copy (*_1);
        _4 = copy (*_7);
        StorageLive(_5);
        _5 = copy _2;
        _0 = Eq(move _4, move _5);
        StorageDead(_5);
        StorageDead(_4);
        StorageDead(_2);
        return;
    }
}
After
// MIR for `src` after GVN

fn src(_1: &&u8) -> bool {
    debug x => _1;
    let mut _0: bool;
    let _2: u8;
    let _3: ();
    let mut _4: u8;
    let mut _5: u8;
    let mut _6: &u8;
    let mut _7: &u8;
    scope 1 {
        debug y => _2;
    }

    bb0: {
        nop;
        _6 = copy (*_1);
        _2 = copy (*_6);
        _3 = unknown() -> [return: bb1, unwind continue];
    }

    bb1: {
        StorageLive(_4);
        _7 = copy _6;
        _4 = copy _2;
        StorageLive(_5);
        _5 = copy _2;
        _0 = const true;
        StorageDead(_5);
        StorageDead(_4);
        nop;
        return;
    }
}

It would be justified to make src() return true if _6 was dereferenced again in bb1, however, the write in unknown() shouldn't invalidate the actual pointer stored in _1 if my understanding of Stacked Borrows is correct.

This is present in both Stable and Nightly.

@jwong101 jwong101 added the C-bug Category: This is a bug. label Sep 25, 2024
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 25, 2024
@saethlin saethlin added I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness A-mir-opt Area: MIR optimizations labels Sep 25, 2024
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Sep 25, 2024
@saethlin saethlin added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. I-prioritize Issue: Indicates that prioritization has been requested for this issue. and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Sep 25, 2024
@RalfJung
Copy link
Member

Cc @rust-lang/wg-mir-opt

@RalfJung
Copy link
Member

Nice find, thanks. :)

@DianQK
Copy link
Member

DianQK commented Sep 29, 2024

Hmm, I don't think it's a miscompile. Your unsafe code breaks the semantic of immutable borrowing.

@RalfJung
Copy link
Member

The test passes Miri. So no, it does not break the semantics of immutable borrowing as we understand them currently.

@workingjubilee workingjubilee added I-miscompile Issue: Correct Rust code lowers to incorrect machine code and removed C-bug Category: This is a bug. labels Oct 2, 2024
@apiraino
Copy link
Contributor

apiraino commented Oct 7, 2024

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 Oct 7, 2024
@RalfJung
Copy link
Member

RalfJung commented Oct 7, 2024

Cc @rust-lang/opsem

@lqd
Copy link
Member

lqd commented Oct 7, 2024

cc @cjgillot

@workingjubilee
Copy link
Member

...huh, the semantics of immutable borrowing can't justify unifying two reads of a reference to a reference of a Freeze type?

@workingjubilee
Copy link
Member

I thought this was THE optimization we were doing this for?

@oxalica
Copy link
Contributor

oxalica commented Oct 11, 2024

...huh, the semantics of immutable borrowing can't justify unifying two reads of a reference to a reference of a Freeze type?

There are already some discussion in the linked issue and more linked issues in it. rust-lang/unsafe-code-guidelines#532

I also agree this is my intuition about the goal of aliasing analysis. But it's not for now. So at least one of the current model and this optimization should change.

BTW, the optimization in this issue already exists since 1.77.0 (https://godbolt.org/z/anPK3Ef5z) which is more than 7months ago. Is there any more user data, other than this, showing that this optimization is unintended/un-intuitive? If not, I think we may need to question about the current model itself.

@RalfJung
Copy link
Member

I thought this was THE optimization we were doing this for?

Not quite, THE optimization is for reads from a reference.

The nested case is a lot more complicated, and so far there's no model at all that could justify the optimization.

There are already some discussion in the linked issue and more linked issues in it. rust-lang/unsafe-code-guidelines#532

Yeah that issue description still needs to be rewritten to be a self-contained introduction of the problem -- see my comment there. :)

If not, I think we may need to question about the current model itself.

That's what rust-lang/unsafe-code-guidelines#532 is for. But we don't do optimizations that we don't have a model for, so this remains an I-unsound issue until a model is proposed (and ideally implemented in Miri).

@workingjubilee
Copy link
Member

Yeah, we have to drop this optimization for now, not because it is unjustifiable per se but because it is unjustified at the moment.

@RalfJung
Copy link
Member

RalfJung commented Nov 1, 2024

@cjgillot you are our main expert for the GVN transform... any chance you could take a look at this? Or leave some notes as starting points for someone else on what would be the best way to fix this?

@jieyouxu
Copy link
Member

P-high review pre-triage: assigning this issue to @cjgillot since they have a PR open (#132461) to address this already.

@wesleywiser wesleywiser added the WG-mir-opt Working group: MIR optimizations label Nov 15, 2024
bors added a commit to rust-lang-ci/rust that referenced this issue Nov 25, 2024
Do not unify dereferences of shared borrows in GVN

Repost of rust-lang#132461, the last commit applies my suggestions.

Fixes rust-lang#130853
@bors bors closed this as completed in 6b6a867 Nov 27, 2024
github-actions bot pushed a commit to rust-lang/miri that referenced this issue Nov 28, 2024
Do not unify dereferences of shared borrows in GVN

Repost of rust-lang/rust#132461, the last commit applies my suggestions.

Fixes rust-lang/rust#130853
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-codegen Area: Code generation A-mir-opt Area: MIR optimizations I-miscompile Issue: Correct Rust code lowers to incorrect machine code I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. WG-mir-opt Working group: MIR optimizations
Projects
None yet
13 participants