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

InstCombine introduces an incorrect use of a local after its storage has ended #78192

Closed
tmiasko opened this issue Oct 21, 2020 · 15 comments · Fixed by #78247, #78387 or #78434
Closed

InstCombine introduces an incorrect use of a local after its storage has ended #78192

tmiasko opened this issue Oct 21, 2020 · 15 comments · Fixed by #78247, #78387 or #78434
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 requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@tmiasko
Copy link
Contributor

tmiasko commented Oct 21, 2020

pub fn f<T>(a: &T) -> *const T {
    let b: &*const T = &(a as *const T);
    *b
}
--- mir_dump/a.f.005-003.InstCombine.before.mir
+++ mir_dump/a.f.005-003.InstCombine.after.mir
@@ -1,2 +1,2 @@
-// MIR for `f` before InstCombine
+// MIR for `f` after InstCombine
 
@@ -18,5 +18,5 @@
         _3 = &_4;                        // scope 0 at a.rs:2:24: 2:40
-        _2 = &(*_3);                     // scope 0 at a.rs:2:24: 2:40
+        _2 = _3;                         // scope 0 at a.rs:2:24: 2:40
         StorageDead(_3);                 // scope 0 at a.rs:2:40: 2:41
-        _0 = (*_2);                      // scope 1 at a.rs:3:5: 3:7
+        _0 = (*_3);                      // scope 1 at a.rs:3:5: 3:7
         StorageDead(_4);                 // scope 0 at a.rs:4:1: 4:2

Found by MIR validator #77369 & #78147.

cc @simonvandel #76683

@tmiasko tmiasko added the C-bug Category: This is a bug. label Oct 21, 2020
@jonas-schievink jonas-schievink added A-mir-opt Area: MIR optimizations I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Oct 21, 2020
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Oct 21, 2020
@tmiasko
Copy link
Contributor Author

tmiasko commented Oct 21, 2020

I will open a PR to disable problematic part of InstCombine for now.

@LeSeulArtichaut
Copy link
Contributor

Did you bisect the regression to #76683?

bors added a commit to rust-lang-ci/rust that referenced this issue Oct 21, 2020
Disable "optimization to avoid load of address" in InstCombine

The transformation is incorrect rust-lang#78192. Disable it.
@spastorino
Copy link
Member

spastorino commented Oct 21, 2020

I see that we've disabled the optimization, can't we also add a regression test for it? cc @tmiasko

EDIT (camelid): See the relevant discussion in wg-prioritization.

@spastorino spastorino added regression-from-stable-to-beta Performance or correctness regression from stable to beta. P-critical Critical priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Oct 21, 2020
@spastorino
Copy link
Member

Assigning P-critical as discussed as part of the Prioritization Working Group procedure and removing I-prioritize.

@camelid camelid added the E-needs-test Call for participation: Writing correctness tests. label Oct 21, 2020
@camelid
Copy link
Member

camelid commented Oct 21, 2020

Is the cause of the unsoundness in this code? I haven't done MIR stuff before, but it looks like to me like it's not checking that the storage is live...

for stmt in statements_to_look_in {
match &stmt.kind {
// Exhaustive match on statements to detect conditions that warrant we bail out of the optimization.
rustc_middle::mir::StatementKind::Assign(box (l, r))
if l.local == local_being_derefed =>
{
match r {
// Looking for immutable reference e.g _local_being_deref = &_1;
Rvalue::Ref(
_,
// Only apply the optimization if it is an immutable borrow.
BorrowKind::Shared,
place_taken_address_of,
) => {
self.optimizations
.unneeded_deref
.insert(location, *place_taken_address_of);
return Some(());
}

@tmiasko
Copy link
Contributor Author

tmiasko commented Oct 21, 2020

The optimization starts by locating a dereference of a local: ... = (*_a);, then looks back to determine a reaching definition of _a. If the definition is of the form _a = &p; it replaces (*_a) with p. In original example, _a would be _2, and p would be (*_3), so

_2 = &(*_3);
StorageDead(_3);
_0 = (*_2);

is changed as follows (there is an additional change in the diff, but it is a separate optimization, so lets ignore it):

_2 = &(*_3);
StorageDead(_3);
_0 = (*_3);

In this case, the primary issue is that reading _3 after StorageDead(_3) is undefined behaviour. The optimization never ensured that evaluating place later will give an equivalent result. The computation of reaching definition also looks suspect to me (it should stop in a presence of any indirect assignments or check that address of _a is never taken).

@tmiasko
Copy link
Contributor Author

tmiasko commented Oct 23, 2020

Example that leads to an end-to-end miscompilation:

fn main() {
    let a = 1u32;
    let b = 2u32;

    let mut c: *const u32 = &a;
    let d: &u32 = &b;

    let x = unsafe { &*c };
    c = d;
    let z = *x;

    assert_eq!(1, z);
}

@LeSeulArtichaut LeSeulArtichaut added P-medium Medium priority requires-nightly This issue requires a nightly compiler in some way. and removed E-needs-test Call for participation: Writing correctness tests. P-critical Critical priority regression-from-stable-to-beta Performance or correctness regression from stable to beta. labels Oct 24, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Oct 25, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Oct 26, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Oct 26, 2020
bors added a commit to rust-lang-ci/rust that referenced this issue Oct 26, 2020
Rollup of 10 pull requests

Successful merges:

 - rust-lang#74477 (`#[deny(unsafe_op_in_unsafe_fn)]` in sys/wasm)
 - rust-lang#77836 (transmute_copy: explain that alignment is handled correctly)
 - rust-lang#78126 (Properly define va_arg and va_list for aarch64-apple-darwin)
 - rust-lang#78137 (Initialize tracing subscriber in compiletest tool)
 - rust-lang#78161 (Add issue template link to IRLO)
 - rust-lang#78214 (Tweak match arm semicolon removal suggestion to account for futures)
 - rust-lang#78247 (Fix rust-lang#78192)
 - rust-lang#78252 (Add codegen test for rust-lang#45964)
 - rust-lang#78268 (Do not try to report on closures to avoid ICE)
 - rust-lang#78295 (Add some regression tests)

Failed merges:

r? `@ghost`
@bors bors closed this as completed in 57d01a9 Oct 26, 2020
@tmiasko
Copy link
Contributor Author

tmiasko commented Oct 26, 2020

This is now enabled by default again, and still an issue. Example from #78192 (comment):

$ rustc +stage1 -Aunused a.rs -Zmir-opt-level=0 && ./a
$ rustc +stage1 -Aunused a.rs && ./a
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `1`,
 right: `2`', a.rs:12:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

@LeSeulArtichaut LeSeulArtichaut added regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. I-prioritize Issue: Indicates that prioritization has been requested for this issue. and removed requires-nightly This issue requires a nightly compiler in some way. labels Oct 26, 2020
@camelid camelid added P-critical Critical priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Oct 26, 2020
@camelid
Copy link
Member

camelid commented Oct 26, 2020

Assigning P-critical and removing I-prioritize as discussed in the prioritization working group.

@LeSeulArtichaut LeSeulArtichaut removed the P-medium Medium priority label Oct 27, 2020
@simonvandel
Copy link
Contributor

I will have time to investigate this after today 1700(utc+1). If anyone finds the cause or arrives at a solution before, go ahead and open a PR.

@jonas-schievink
Copy link
Contributor

This optimization should stay disabled by default until we're sure that it is sound.

@jonas-schievink
Copy link
Contributor

Opened #78434 to disable it

@bors bors closed this as completed in 2a71e45 Oct 27, 2020
@LeSeulArtichaut LeSeulArtichaut added requires-nightly This issue requires a nightly compiler in some way. and removed P-critical Critical priority regression-from-stable-to-nightly Performance or correctness regression from stable to nightly. labels Oct 27, 2020
simonvandel added a commit to simonvandel/rust that referenced this issue Nov 3, 2020
Do not apply the optimization if we deref a mutable ref
@tmiasko tmiasko closed this as completed Jan 23, 2021
@camelid
Copy link
Member

camelid commented Jan 23, 2021

Why was this reopened and then closed?

@LeSeulArtichaut
Copy link
Contributor

I reopened it after #78434 since the bug wasn't fixed, only disabled so it doesn't hit stable. Is it fixed now @tmiasko?

@tmiasko
Copy link
Contributor Author

tmiasko commented Jan 23, 2021

I closed it because the code responsible is no longer there.

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 requires-nightly This issue requires a nightly compiler in some way. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
7 participants