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

-Zmir-opt-level=2 misoptimises valid code under Tree Borrows #110947

Closed
cbeuw opened this issue Apr 28, 2023 · 14 comments · Fixed by #110954
Closed

-Zmir-opt-level=2 misoptimises valid code under Tree Borrows #110947

cbeuw opened this issue Apr 28, 2023 · 14 comments · Fixed by #110954
Labels
A-mir-opt Area: MIR optimizations C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness

Comments

@cbeuw
Copy link
Contributor

cbeuw commented Apr 28, 2023

This code has UB under Stacked Borrows in Miri, but is fine with -Zmiri-tree-borrows, and it should print false

#![feature(custom_mir, core_intrinsics)]
extern crate core;
use core::intrinsics::mir::*;
#[custom_mir(dialect = "runtime", phase = "initial")]
pub fn fn0() -> bool {
    mir! {
    type RET = bool;
    let pair: (i8, bool);
    let ptr: *mut bool;
    {
        pair = (1, false);
        ptr = core::ptr::addr_of_mut!(pair.1);
        RET = pair.1 <= (*ptr);
        pair = (1, false);
        (*ptr) = RET | RET;
        RET = !pair.1;
        Return()
    }

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

However, under -Zmir-opt-level=2 and above, it prints true

% rustc -Zmir-opt-level=2 repro.rs && ./repro
true

Meta

rustc --version --verbose:

rustc 1.71.0-nightly (1c42cb4ef 2023-04-26)
binary: rustc
commit-hash: 1c42cb4ef0544fbfaa500216e53382d6b079c001
commit-date: 2023-04-26
host: aarch64-apple-darwin
release: 1.71.0-nightly
LLVM version: 16.0.2

cc @Vanille-N @RalfJung

@cbeuw cbeuw added the C-bug Category: This is a bug. label Apr 28, 2023
@cbeuw
Copy link
Contributor Author

cbeuw commented Apr 28, 2023

Here's a pure-surface Rust reproduction

pub fn fn0() -> bool {
    let mut pair = (1, false);
    let ptr = core::ptr::addr_of_mut!(pair.1);
    let mut ret = pair.1 <= unsafe { *ptr };
    pair = (1, false);
    unsafe {
        *ptr = ret | ret;
    }
    ret = !pair.1;
    return ret;
}

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

@RalfJung
Copy link
Member

@JakobDegen any guess which MIR opt might be the culprit here?

@RalfJung
Copy link
Member

Even more minimized reproducer:

pub fn fn0() -> bool {
    let mut pair = (1, false);
    let ptr = core::ptr::addr_of_mut!(pair.1);
    pair = (1, false);
    unsafe {
        *ptr = true;
    }
    let ret = !pair.1;
    return ret;
}

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

My first guess would be that some MIR analysis assumes that pair = (1, false) means that no pointer to pair can exist any more -- with Stacked Borrows that was true, but not with Tree Borrows. I usually push back against making Stacked Borrows assumptions in optimizations but looks like something slipped through?

Cc @cjgillot

@RalfJung
Copy link
Member

RalfJung commented Apr 28, 2023

Even stranger, with Rust 1.69 we get true even without optimizations. This goes back all the way to Rust 1.51; before that, we didn't have addr_of_mut. Only beta and nightly without optimizations seem to do this correctly.

@saethlin
Copy link
Member

Does this look like the bad optimization?

-// MIR for `fn0` before ConstProp
+// MIR for `fn0` after ConstProp
 
 fn fn0() -> bool {
     let mut _0: bool;                    // return place in scope 0 at src/main.rs:1:17: 1:21
@@ -19,13 +19,13 @@ fn fn0() -> bool {
     }
 
     bb0: {
-        _1 = (const 1_i32, const false); // scope 0 at src/main.rs:2:20: 2:30
+        _1 = const (1_i32, false);       // scope 0 at src/main.rs:2:20: 2:30
         _2 = &raw mut (_1.1: bool);      // scope 1 at /rustc/1a6ae3d692cfb52b21d0f45ba50b659486e53d6c/library/core/src/ptr/mod.rs:2192:5: 2192:20
-        _1 = (const 1_i32, const false); // scope 2 at src/main.rs:4:5: 4:22
+        _1 = const (1_i32, false);       // scope 2 at src/main.rs:4:5: 4:22
         (*_2) = const true;              // scope 3 at src/main.rs:6:9: 6:20
-        _4 = (_1.1: bool);               // scope 2 at src/main.rs:8:16: 8:22
-        _3 = Not(move _4);               // scope 2 at src/main.rs:8:15: 8:22
-        _0 = _3;                         // scope 4 at src/main.rs:9:12: 9:15
+        _4 = const false;                // scope 2 at src/main.rs:8:16: 8:22
+        _3 = const true;                 // scope 2 at src/main.rs:8:15: 8:22
+        _0 = const true;                 // scope 4 at src/main.rs:9:12: 9:15
         return;                          // scope 0 at src/main.rs:10:2: 10:2
     }
 }

@RalfJung
Copy link
Member

@saethlin yes, very much so. Specifically the _4 = const false is wrong, the rest then follows from that.

Did ConstProp never learn that locals that have their address taken cannot be propagated?

@saethlin
Copy link
Member

I'm about to be busy for a few hours, so if someone else wants to bisect what changed with ConstProp they should do that. I'll check back later.

@cjgillot
Copy link
Contributor

I've been suspecting such a bug without managing to reproduce it for a few months. The bug is in the CanConstProp visitor, which mistreats Projection place context. I can make a targeted fix in the hour.

@saethlin
Copy link
Member

@cbeuw I'm curious, how did you come up with this example?

@cbeuw
Copy link
Contributor Author

cbeuw commented Apr 28, 2023

@saethlin I'm making a fuzzer targeting custom MIR :D

It's still quite incomplete and currently hosted on an ETH Zürich private GitLab instance https://gitlab.inf.ethz.ch/ou-plf/rustlantis. I guess I should make a public mirror on GitHub soon...

@saethlin
Copy link
Member

That is awesome! This is exactly the kind of stuff I was hoping to find with one.

@matthiaskrgr
Copy link
Member

Ah so should we report these? I think I read somewhere that custom mir is likely to get wrong and would always cause crashes then or something which is why I have ignored all of the custom-mir /core_instrinsics ICEs that I came across.

@saethlin
Copy link
Member

This isn't an ICE, this is a change in behavior due to enabling an optimization.

@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 Apr 28, 2023
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Apr 28, 2023
@saethlin saethlin removed the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Apr 28, 2023
@matthiaskrgr
Copy link
Member

ah right sorry, I was confused with #110902

@bors bors closed this as completed in 18d4e22 May 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-mir-opt Area: MIR optimizations C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants