-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Double Drop on Rust beta #73137
Comments
@rustbot ping cleanup |
Hey Cleanup Crew ICE-breakers! This bug has been identified as a good cc @AminArria @camelid @chrissimpkins @contrun @DutchGhost @elshize @ethanboxx @h-michael @HallerPatrick @hdhoang @hellow554 @imtsuki @kanru @KarlK90 @LeSeulArtichaut @MAdrianMattocks @matheus-consoli @mental32 @nmccarty @Noah-Kennedy @pard68 @PeytonT @pierreN @Redblueflame @RobbieClarken @RobertoSnap @robjtede @SarthakSingh31 @senden9 @shekohex @sinato @spastorino @turboladen @woshilapin @yerke |
Possibly related to #71956 ? |
Here is a simpler test case: struct DD {
a: String,
}
// DD is dropped twice
impl Drop for DD {
fn drop(&mut self) {
println!("Dropping!");
}
}
struct Yolo {
a: DD,
b: Option<DD>,
}
async fn gg(x: &Yolo) {}
fn main() {
futures::executor::block_on(async {
let d2 = async { DD { a: "a".to_owned() } };
// Swap the order here and it works
let action = Yolo {
b: None,
a: d2.await,
};
gg(&action).await;
});
} |
Note that the underlying problem is not a double free. We instead copy the value of An example illustrating this is struct Foo {
a: usize,
b: &'static u32,
}
fn main() {
futures::executor::block_on(async {
// Swap the order here and it works
let action = Foo {
b: &42,
a: async { 0 }.await,
};
println!("{:p}", action.b);
// ^ This prints `0x0`
async {}.await;
});
} |
This would explain why my debugger replied with |
Assigning |
A self-contained version of @lcnr's example: #![feature(wake_trait)]
use std::future::Future;
use std::task::{Waker, Wake, Context};
use std::sync::Arc;
struct DummyWaker;
impl Wake for DummyWaker {
fn wake(self: Arc<Self>) {}
}
struct Foo {
a: usize,
b: &'static u32,
}
fn main() {
let mut fut = Box::pin(async {
// Swap the order here and it works
let action = Foo {
b: &42,
a: async { 0 }.await,
};
println!("{:p}", action.b);
// ^ This prints `0x0`
async {}.await;
});
let waker = Waker::from(Arc::new(DummyWaker));
let mut cx = Context::from_waker(&waker);
fut.as_mut().poll(&mut cx);
} |
FYI @ecstatic-morse @tmandry because #71956 was in the right commit range and seems related. |
The MIR for the example I posted contains the following two lines:
The first line treans the generator enum as being in state EDIT: Accesses using the 'wrong' variant appear to be intentional: rust/src/librustc_mir/transform/generator.rs Lines 762 to 766 in bc10b68
I suspect that the computed variant layouts are not actually compatible in this case. |
Bisected and confirmed this was introduced in #71956. For background, that PR changed the dataflow analysis we use to mark which locals require storage in the generator state machine. I don't know yet whether the new approach was simply incorrect or it violated some assumption made by the MIR transformation later on. In either case, the fact that this took 17 days to find is concerning. I think we should revert #71956 on beta even if we can identify a proper fix in the short-term. I'm continuing to investigate, but help would be appreciated. |
Co-authored-by: Aaron1011 <[email protected]>
After #71956, the generator transform no longer marks locals _3 = Foo { a: move _7, b: move _4 }; In any case, it seems the generator transform relied on this being a conflict. Because both (((*(_1.0: &mut [static generator])) as variant#4].0: Foo) = Foo { a: move _7, b: move (((*(_1.0: &mut [static generator])) as variant#3).0: &u32) }; The place on the left-hand side ( @tmandry, @jonas-schievink does this assessment seem correct? I'm less familiar with MIR lowering of generators than either one of you. If so, I think the correct solution once the revert lands is to introduce a temporary when lowering an assignment of one generator-saved local to another. |
Yep, that sounds correct to me. Off the top of my head I can't say that the second example is definitely ill-formed MIR, but it seems very likely. Using a temporary in these cases, as you suggested, ought to work. The other approach is to go back to being more conservative with the analysis (which seems less desirable, and possibly harder to implement after the changes from #71956.) |
Revert rust-lang#71956 ...since it caused unsoundness in rust-lang#73137. Also adds a reduced version of rust-lang#73137 to the test suite. The addition of the `MaybeInitializedLocals` dataflow analysis has not been reverted, but it is no longer used. Presumably there is a more targeted fix, but I'm worried that other bugs may be lurking. I'm not yet sure what the root cause of rust-lang#73137 is. This will need to get backported to beta. r? @tmandry
Indeed, MIR constructs generally require its operands to not alias, and assignment statements are one of them. I plan to have them all documented and checked by the validation pass ( (I ran into similar issues with destination propagation, so this is somewhat reassuring – we might be able to share code that tells you about aliasing constraints of MIR constructs between destination propagation, the generator transform, and the validator this way) |
The revert has landed, so this should be fixed on the latest mater. This still needs a beta backport, however, so I'm leaving the issue open until one lands. Thank you for reporting this @pranjalssh! |
If this is the official stanza on MIR semantics, we should also make sure that Miri can catch this as UB. A static check will never be able to properly reflect dynamic properties such as "operands must not alias". Cc #68364 |
@ecstatic-morse given that this has been already reverted, I guess we may want to remove |
@spastorino I find it strange that the priority of an issue can change based on whether a fix is being worked on. If (god forbid) I get hit by a bus tomorrow and no one thinks to take on the work of backporting #73153, we would still need to block the release, no? |
This is to prevent the miscompilation in rust-lang#73137 from reappearing. Only runs with `-Zvalidate-mir`.
@ecstatic-morse, maybe I didn't properly explain myself or I may be overlooking something. What I meant is that given that this problem was fixed by reverting the PR that has caused this regression, we can lower the priority of the issue now. My understanding is that after the revert the problem is fixed but we want to keep the issue open to find a better solution. Am I right? if so, this is why I consider that this is not |
If nothing were done from this point forward (i.e. no one backported #73153), this miscompilation would land on the next stable release. I didn't come up with the current labelling system, but that seems critical to me. |
I do think it makes some sense perhaps to keep this P-critical while the backport isn't accepted; I'm not sure if @pnkfelix hasn't had time to accept it yet or if we need to discuss next T-compiler meeting or what. |
@ecstatic-morse the revert won't hit stable because it was In my opinion, we should remove the |
I don't have anything to add to my comments above besides the fact that I'm generally suspicious of open-source projects who change issue priority based on things besides severity. Ultimately it's up to y'all (meaning WG-prioritization) on how to handle release blocking issues that have been fixed on |
Co-authored-by: Aaron1011 <[email protected]>
Fixed on beta in #73326 |
This is to prevent the miscompilation in rust-lang#73137 from reappearing. Only runs with `-Zvalidate-mir`.
…ir, r=tmandry Check for assignments between non-conflicting generator saved locals This is to prevent future changes to the generator transform from reintroducing the problem that caused rust-lang#73137. Namely, a store between two generator saved locals whose storage does not conflict. My ultimate goal is to introduce a modified version of rust-lang#71956 that handles this case properly. r? @tmandry
…ir, r=tmandry Check for assignments between non-conflicting generator saved locals This is to prevent future changes to the generator transform from reintroducing the problem that caused rust-lang#73137. Namely, a store between two generator saved locals whose storage does not conflict. My ultimate goal is to introduce a modified version of rust-lang#71956 that handles this case properly. r? @tmandry
…ir, r=tmandry Check for assignments between non-conflicting generator saved locals This is to prevent future changes to the generator transform from reintroducing the problem that caused rust-lang#73137. Namely, a store between two generator saved locals whose storage does not conflict. My ultimate goal is to introduce a modified version of rust-lang#71956 that handles this case properly. r? @tmandry
Getting double Drop/free errors on Rust. Sounds very dangerous. It started on 23 May rust nightly. Fails on beta as well, which is the next release.
A minimal repro https://bit.ly/2A52CJe
Dropping
is printed twice. Flipping the order of elements in struct, makes it work and then drop is only called once.Cc @asm89
The text was updated successfully, but these errors were encountered: