-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Segfaults/corruption when reading an enum in release mode #77359
Comments
Can't reproduce with |
I don’t have MacOS, so I can’t test this myself easily. Here’s some tags though: Edit: To clarify, I’m not seeing any segfault on Windows or Linux (both 7f7a1cb 2020-09-27). Oddly enough, I’m getting @cormacrelf Perhaps you could test your less minimized version on Linux or Windows if you have access to those (since you said “the segfault started to change a bit”, so maybe the original version also segfaults on e.g. Linux). |
Yes, the program is deterministic. I have seen similar wrong variants as well in the original code on macOS, so I'll go out in a limb and say that's the same bug on Linux. I can run a docker image if you like but I think you've already found it. The fragility I was referring to was in terms of "add a debug statement or delete some line, the issue goes away or pops up on a different input instead". But once you have it it produces the same segfault again and again. Given the complexity of the repro I'm not surprised that platform differences including calling conventions etc would affect reproducibility. I did run in Miri and it didn't find anything amiss, and produced correct output, ie the full |
@bjorn3 Yeah I should have made it more obvious about macOS. I can still reproduce with:
|
Okay, I guess if it is supposed to be deterministic, this means I can then confirm the problem for linux and windows. I’m currently trying to reduce the example further, keeping the different behavior between |
Just got a segfault on Linux with a reduced example. |
SimplifyArmIdentity moves an assignment to a local before StorageLive: fn main() {
f(None);
}
pub enum A {
X,
Y,
}
pub enum B {
B(Option<A>),
}
pub fn f(a: Option<A>) -> B {
if let Some(x) = a {
let y = x;
return B::B(Some(y));
}
B::B(None)
} --- main.f.005-007.SimplifyArmIdentity.before.mir
+++ main.f.005-007.SimplifyArmIdentity.after.mir
@@ -1,2 +1,2 @@
-// MIR for `f` before SimplifyArmIdentity
+// MIR for `f` after SimplifyArmIdentity
@@ -13,6 +13,6 @@
scope 1 {
- debug x => _4; // in scope 1 at src/main.rs:15:17: 15:18
+ debug x => ((_7 as Some).0: A); // in scope 1 at src/main.rs:15:17: 15:18
let _6: A; // in scope 1 at src/main.rs:16:13: 16:14
scope 2 {
- debug y => _6; // in scope 2 at src/main.rs:16:13: 16:14
+ debug y => ((_7 as Some).0: A); // in scope 2 at src/main.rs:16:13: 16:14
}
@@ -47,9 +47,4 @@
StorageLive(_6); // scope 1 at src/main.rs:16:13: 16:14
- _6 = move _4; // scope 1 at src/main.rs:16:17: 16:18
+ _7 = move _1; // scope 2 at src/main.rs:17:21: 17:28
StorageLive(_7); // scope 2 at src/main.rs:17:21: 17:28
- StorageLive(_8); // scope 2 at src/main.rs:17:26: 17:27
- _8 = move _6; // scope 2 at src/main.rs:17:26: 17:27
- ((_7 as Some).0: A) = move _8; // scope 2 at src/main.rs:17:21: 17:28
- discriminant(_7) = 1; // scope 2 at src/main.rs:17:21: 17:28
- StorageDead(_8); // scope 2 at src/main.rs:17:27: 17:28
((_0 as B).0: std::option::Option<A>) = move _7; // scope 2 at src/main.rs:17:16: 17:29 cc @wesleywiser |
The label |
A slight modification to @tmiasko's repro and it will segfault in release mode: fn main() {
println!("{:?}", f(Some(A::Y)));
}
#[derive(Eq, Debug, PartialEq)]
pub enum A {
X(String),
Y,
}
#[derive(Eq, Debug, PartialEq)]
pub enum B {
B(Option<A>),
}
pub fn f(a: Option<A>) -> B {
if let Some(x) = a {
let y = x;
return B::B(Some(y));
}
B::B(None)
} I can open a PR disabling this optimization so this doesn't hit beta next week. |
Marking this as P-critical based on the wg-prioritization discussion |
We can probably turn bugs like this into ICEs by extending the MIR validator (unless another optimization interferes before this hits codegen) |
Because I didn’t feel like aborting my own minimization attempt, here’s the (heavily) reduced version of OPs example that I got: #[derive(Debug)]
enum MyEnum {
Variant1(Vec<u8>),
Variant2,
Variant3,
Variant4,
}
fn f(arg1: &bool, arg2: &bool, arg3: bool) -> MyStruct {
if *arg1 {
println!("{:?}", f(&arg2, arg2, arg3));
MyStruct(None)
} else {
match if arg3 { Some(MyEnum::Variant3) } else { None } {
Some(t) => {
let ah = t;
return MyStruct(Some(ah));
}
_ => MyStruct(None)
}
}
}
#[derive(Debug)]
struct MyStruct(Option<MyEnum>);
fn main() {
let arg1 = true;
let arg2 = false;
f(&arg1, &arg2, true);
} Causing a segfault on linux with nightly. |
@wesleywiser Your modified example does not consistently segfault. It only does so for nightlies newer than |
That sounds about right to me since #76844 should have been in nightly 2020-09-26. Prior to that PR, the mis-optimization was disabled in #76837 which should have been in nightly-2020-09-19 or so. Between ~2020-09-19 and ~2020-09-26 the issue should not appear. Does your repro work on the playground? When I try it with the nightly available, it works correctly. |
The optimization still has some bugs that need to be worked out such as rust-lang#77359. We can try re-enabling this again after the known issues are resolved.
…ty, r=oli-obk Disable the SimplifyArmIdentity mir-opt The optimization still has some bugs that need to be worked out such as rust-lang#77359. We can try re-enabling this again after the known issues are resolved. r? `@oli-obk`
…ness, r=wesleywiser -Zvalidate-mir: Assert that storage is allocated on local use This extends the MIR validator to check that locals are only used when their backing storage is currently allocated via `StorageLive`. The result of this is that miscompilations such as rust-lang#77359 are caught and turned into ICEs. The PR currently fails tests because miscompilations such as rust-lang#77359 are caught and turned into ICEs. I have confirmed that tests pass (even with `-Zvalidate-mir`) once `SimplifyArmIdentity` is turned into a no-op (except mir-opt tests, of course).
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Both repros work correctly on the latest nightly (2020-10-02), so I'm adjusting the tags accordingly. Thanks @cormacrelf for reporting this issue! |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Delete `SimplifyArmIdentity` and `SimplifyBranchSame` mir opts I had attempted to fix the first of these opts in rust-lang#94177 . However, despite that PR already being a full re-write, it still did not fix some of the core soundness issues. The optimizations that are attempted here are likely to be desirable, but I do not expect any of the currently written code to survive into a sound implementation. Deleting the code keeps us from having to maintain the passes in the meantime. Closes rust-lang#77359 , closes rust-lang#72800 , closes rust-lang#78628 r? `@cjgillot`
Delete `SimplifyArmIdentity` and `SimplifyBranchSame` mir opts I had attempted to fix the first of these opts in rust-lang#94177 . However, despite that PR already being a full re-write, it still did not fix some of the core soundness issues. The optimizations that are attempted here are likely to be desirable, but I do not expect any of the currently written code to survive into a sound implementation. Deleting the code keeps us from having to maintain the passes in the meantime. Closes rust-lang#77359 , closes rust-lang#72800 , closes rust-lang#78628 r? ``@cjgillot``
Reproduction here: https://github.com/cormacrelf/minimal-sigsegv-rust
There's more info in the readme on that repo. Essentially, I have some code that segfaults while debug-printing an enum. The enum has one
Output(String)
variant, followed by 11 empty variants. The repro only creates one of the empty ones, but it appears the debug routine attempts to read the string variant. It occurs in the context of some mutually recursive functions building a fairly simple datastructure by walking another. I have tried hard to minimise the repro but it could be better, but it's a bit fragile so I don't know what else I can remove. With a quick glance at the commit that bisect-rustc found, it looks like it could be related to optimising match arms, so that could be a start.Edit: for some linkage, the commit below merges #76308, which 're-enables SimplifyArmIdentity'.
cargo bisect-rustc
searched nightlies: from nightly-2020-05-08 to nightly-2020-09-23
regressed nightly: nightly-2020-09-09
searched commits: from 0e2c128 to 5099914
regressed commit: 5a6b426
bisected with cargo-bisect-rustc v0.5.2
Host triple: x86_64-apple-darwin
Reproduce with:
Note that the
ref_sequence
function is the one in which the debug segfaults, and it is debugging a larger structure. I have also seen it segfault when cloning just theEdgeData
enum, which is the String+11x one mentioned above.Backtrace from rust-lldb
The text was updated successfully, but these errors were encountered: