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

Panics in destructors can cause the return value to be leaked #47949

Open
arielb1 opened this issue Feb 1, 2018 · 30 comments · Fixed by #61430 or #78373 · May be fixed by #125923
Open

Panics in destructors can cause the return value to be leaked #47949

arielb1 opened this issue Feb 1, 2018 · 30 comments · Fixed by #61430 or #78373 · May be fixed by #125923
Assignees
Labels
A-destructors Area: Destructors (`Drop`, …) C-enhancement Category: An issue proposing an enhancement or a PR with one. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@arielb1
Copy link
Contributor

arielb1 commented Feb 1, 2018

STR

struct NoisyDrop;
impl Drop for NoisyDrop {
    fn drop(&mut self) {
        println!("dropping a NoisyDrop");
    }
}

impl NoisyDrop {
    fn new() -> Self {
        println!("creating a NoisyDrop");
        NoisyDrop
    }
}

struct PanickyDrop;
impl Drop for PanickyDrop {
    fn drop(&mut self) {
        panic!()
    }
}

fn foo() -> NoisyDrop {
    let p = PanickyDrop;
    NoisyDrop::new()
}

fn main() {
    foo();
}

Expected Result

"creating a NoisyDrop" and "dropping a NoisyDrop" should appear the same number of times

Actual Result

creating a NoisyDrop
thread 'main' panicked at 'explicit panic', src/main.rs:18:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.

The destructor is ignored.

@arielb1 arielb1 added the I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness label Feb 1, 2018
@jonas-schievink
Copy link
Contributor

Heh, this even happens on Rust 1.0

@arielb1
Copy link
Contributor Author

arielb1 commented Feb 2, 2018

@jonas-schievink

Rust 1.0 is far worse than this - it leaks everything when a destructor panics.

@solb
Copy link

solb commented Mar 8, 2018

@arielb1, could you explain why this is tagged as a soundness bug? The language makes no guarantee that destructors will ever be invoked.

@XAMPPRocky XAMPPRocky added C-enhancement Category: An issue proposing an enhancement or a PR with one. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 23, 2018
@dtolnay dtolnay removed the I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness label Oct 2, 2018
@gnzlbg
Copy link
Contributor

gnzlbg commented May 7, 2019

ping @rust-lang/compiler this should be triagged, even if it is not a soundness bug, unwinding working properly is something that a lot of code relies on.

@nikomatsakis nikomatsakis added I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness and removed I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness labels May 7, 2019
@nikomatsakis
Copy link
Contributor

nikomatsakis commented May 7, 2019

So, to be clear, it's not that we leak all destructors when panic in drop occurs. For example, this function drops both p and n as expected:

fn foo() {
    let n = NoisyDrop::new();
    let p = PanickyDrop;
}

The specific scenario here is that:

  • we are in the middle of returning the NoisyDrop to our caller (so our caller "owns it")
  • but we panic before our return completes (so they don't realize they own it)

I don't think it's quite right to say that "we make no guarantee that destructors will ever be invoked". We don't guarantee that all destructors are always invoked, but we do guarantee some things: for example, that if you have a local variable whose scope includes a call, it will be dropped when the call panics (here, n would be dropped):

let n = ...;
foo();

So the question is what we should guarantee in this particular scenario. Perhaps the answer is that we should just be very clear to document the current behavior, given how long it has existed.

@nikomatsakis nikomatsakis reopened this May 7, 2019
@nikomatsakis nikomatsakis added the T-lang Relevant to the language team, which will review and decide on the PR/issue. label May 9, 2019
@nikomatsakis
Copy link
Contributor

Adding T-lang -- I think that documenting the expected order would be a good first step.

@nikomatsakis nikomatsakis added the P-medium Medium priority label May 9, 2019
@nikomatsakis
Copy link
Contributor

We discussed in the @rust-lang/lang meeting. Everyone agreed this behavior is wrong; the returned value ought to be dropped, presumably after all other variables in the frame have been dropped. Basically it should be equivalent to having stored the value into an outer let.

However, as a good first step, it seems like we should begin by verifying the behavior with a more thorough set of test cases. For example, I found (playground) that the problem applies not only to returns but to any store:

fn foo() -> NoisyDrop {
    let n = NoisyDrop::new(1);
    let x = {
        let p = PanickyDrop;
        NoisyDrop::new(2) // <-- dtor for this never runs 
    };
    panic!()
}

Ideally what we would do is this:

  • Create various tests covering these corner cases (also perhaps things like panic when running the dtor of a struct field) so we know the current behavior
  • Decide on the desired behavior and, depending on what we think, maybe writing up an RFC
  • Implementing the fixes in drop elaboration

We thought perhaps @matthewjasper might be interested in tackling this, in particular =)

@nikomatsakis
Copy link
Contributor

I'm leaving nominated so we follow up next meeting and discuss priority -- how serious do we think it is to fix this. Obviously, this is a long-standing behavior.

@matthewjasper
Copy link
Contributor

Oh, there's an issue for this.

  • Create various tests covering these corner cases (also perhaps things like panic when running the dtor of a struct field) so we know the current behavior

This should be done for evaluation order as well.

  • Implementing the fixes in drop elaboration

The bug is in MIR construction. I don't think it's that hard to fix (except possibly for the return place).

We thought perhaps @matthewjasper might be interested in tackling this, in particular =)

=)

@nikomatsakis
Copy link
Contributor

@matthewjasper

The bug is in MIR construction.

Oh?

matthewjasper added a commit to matthewjasper/rust that referenced this issue May 30, 2019
Centril added a commit to Centril/rust that referenced this issue Jun 1, 2019
…komatsakis

Expand dynamic drop tests for cases in rust-lang#47949

Adds test cases for rust-lang#47949
bors added a commit that referenced this issue Jun 1, 2019
Rollup of 8 pull requests

Successful merges:

 - #60729 (Expand dynamic drop tests for cases in #47949)
 - #61263 (Don't generate div inside header (h4/h3/h...) elements)
 - #61364 (Stabilize reverse_bits feature)
 - #61375 (Make "panic did not include expected string" message consistent)
 - #61387 (Remove ty::BrFresh and RegionConstraintCollector::new_bound)
 - #61389 (Remove GlobalArenas and use Arena instead)
 - #61391 (Doc comment fixes for `rustc::mir::interpret::InterpretCx`)
 - #61403 (Remove unnecessary `-Z continue-parse-after-error` from tests)

Failed merges:

r? @ghost
@bors bors closed this as completed in 9122b76 Dec 5, 2020
bors added a commit to rust-lang-ci/rust that referenced this issue Feb 5, 2021
…ution-via-revert-of-pr-78373, r=matthewjasper

Revert 78373 ("dont leak return value after panic in drop")

Short term resolution for issue rust-lang#80949.

Reopen rust-lang#47949 after this lands.

(We plan to fine-tune PR rust-lang#78373 to not run into this problem.)
@pnkfelix
Copy link
Member

pnkfelix commented Feb 5, 2021

Reopening due to PR #81257

@pnkfelix pnkfelix reopened this Feb 5, 2021
ehuss pushed a commit to ehuss/rust that referenced this issue Feb 5, 2021
…ution-via-revert-of-pr-78373, r=matthewjasper

Revert 78373 ("dont leak return value after panic in drop")

Short term resolution for issue rust-lang#80949.

Reopen rust-lang#47949 after this lands.

(We plan to fine-tune PR rust-lang#78373 to not run into this problem.)
@lcnr
Copy link
Contributor

lcnr commented May 17, 2024

This has a conceptionally clear fix by changing MIR building which does not cause any issues, expect a single borrowck regression in #80949. It has also been encountered in the wild at least once in #47949 (comment). Going to bump this to P-high.

I personally would be in favor of merging #78373 again, even if it still breaks euca if it is difficult to avoid that issue.

@lcnr lcnr added P-high High priority and removed P-medium Medium priority labels May 17, 2024
@nikomatsakis
Copy link
Contributor

I agree we should fix this. I don't recall the solution in detail nor the issue though. @lcnr it seems to me that FCP is appropriate -- this is likely a @rust-lang/types FCP with @rust-lang/lang cc'd?

@lcnr
Copy link
Contributor

lcnr commented May 23, 2024

An FCP to merge #78373 again, accepting the regressions caused by it, assuming no additional major crates started to depend on the current behavior?

hmm, not totally sure which team is the most responsible here. I feel like it may be types > @rust-lang/compiler > lang as it feels more like an implementation detail/bug than an actual lang design question🤔

Yes, that seems good to me. Will put this on my list of things I'd like to do, though I would love for someone else to summarize this for an FCP proposal, as I may not get to it in the near future.

@estebank
Copy link
Contributor

@nikomatsakis this is something I explored with the panic reachability logic in redpen. I can detect that a panic might happen in a Drop impl, but for a clippy or rustc lint we'd need something a bit stronger where we know for a fact that the panic can hit (it's not just dead unreachable code).

@nikomatsakis
Copy link
Contributor

nikomatsakis commented May 24, 2024 via email

@matthewjasper matthewjasper linked a pull request Jun 3, 2024 that will close this issue
bors added a commit to rust-lang-ci/rust that referenced this issue Jun 4, 2024
Fix leaks from panics in destructors

Resurrects rust-lang#78373.

This avoids the problem with rust-lang#80949 by not unscheduling drops of function arguments until after the call (so they still get a drop terminator on the function unwind path).

Closes rust-lang#47949

r? `@lcnr`
bors added a commit to rust-lang-ci/rust that referenced this issue Jun 14, 2024
Fix leaks from panics in destructors

Resurrects rust-lang#78373.

This avoids the problem with rust-lang#80949 by not unscheduling drops of function arguments until after the call (so they still get a drop terminator on the function unwind path).

Closes rust-lang#47949

r? `@lcnr`
bors added a commit to rust-lang-ci/rust that referenced this issue Jun 20, 2024
Fix leaks from panics in destructors

Resurrects rust-lang#78373.

This avoids the problem with rust-lang#80949 by not unscheduling drops of function arguments until after the call (so they still get a drop terminator on the function unwind path).

Closes rust-lang#47949

r? `@lcnr`
bors added a commit to rust-lang-ci/rust that referenced this issue Jun 26, 2024
Fix leaks from panics in destructors

Resurrects rust-lang#78373.

This avoids the problem with rust-lang#80949 by not unscheduling drops of function arguments until after the call (so they still get a drop terminator on the function unwind path).

Closes rust-lang#47949

r? `@lcnr`
bors added a commit to rust-lang-ci/rust that referenced this issue Jul 15, 2024
Fix leaks from panics in destructors

Resurrects rust-lang#78373.

This avoids the problem with rust-lang#80949 by not unscheduling drops of function arguments until after the call (so they still get a drop terminator on the function unwind path).

Closes rust-lang#47949

r? `@lcnr`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-destructors Area: Destructors (`Drop`, …) C-enhancement Category: An issue proposing an enhancement or a PR with one. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-high High priority T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet