Skip to content

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

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

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: An issue has been fixed and does not reproduce, but no test has been added. 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: An issue has been fixed and does not reproduce, but no test has been added. 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.

# for free to join this conversation on GitHub. Already have an account? # 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