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 on drop will cause double drops. #3

Closed
Stebalien opened this issue Aug 20, 2015 · 5 comments
Closed

Panics on drop will cause double drops. #3

Stebalien opened this issue Aug 20, 2015 · 5 comments

Comments

@Stebalien
Copy link

IntoIter and ArrayVec need to be poisoned to prevent dropping before being drained; otherwise, if one of the elements panics on drop, it will be dropped twice. I've worked on a couple of solutions but they all have design tradeoffs so I figured I'd just let you fix it.

Drain doesn't have this issue because the ArrayVec's length is set to 0 when the Drain is initialized.

@Stebalien
Copy link
Author

Test cases (abusing rust's panic on drop -> abort).

#[test]
#[should_panic]
fn test_drop_panic() {
    struct DropPanic;

    impl Drop for DropPanic {
        fn drop(&mut self) {
            panic!("drop");
        }
    }

    let mut array = ArrayVec::<[DropPanic; 1]>::new();
    array.push(DropPanic);
}

#[test]
#[should_panic]
fn test_drop_panic_into_iter() {
    struct DropPanic;

    impl Drop for DropPanic {
        fn drop(&mut self) {
            panic!("drop");
        }
    }

    let mut array = ArrayVec::<[DropPanic; 1]>::new();
    array.push(DropPanic);
    array.into_iter();
}

@bluss
Copy link
Owner

bluss commented Aug 20, 2015

Thanks! I guess this won't be easy..

Stebalien added a commit to Stebalien/arrayvec that referenced this issue Aug 20, 2015
Stebalien added a commit to Stebalien/arrayvec that referenced this issue Aug 20, 2015
bluss pushed a commit that referenced this issue Aug 20, 2015
We need to make sure the NoDrop enum's discriminant is set to Dropped,
even if we panic during drop of one of the elements.

Bug reported by @Stebalien

Fixes #3
@bluss
Copy link
Owner

bluss commented Aug 20, 2015

For the lols, please read #5. Hey you said you would leave it for me to fix! But I do think you have the sane version, and I the insane..

bluss added a commit that referenced this issue Aug 20, 2015
ArrayVec::drop was not panic safe — if there would be a panic during an
element's drop, the discriminant would never be set to Dropped, and the
array elements would potentially double drop.

Fix this by going back to the old NoDrop composition. The NoDrop struct
thas its own Drop impl, that will trigger too on panic during an element's
drop. This serves to make ArrayVec::drop panic safe.

Also tweak IntoIter::drop to make it panic safe: set inner ArrayVec's
length before dropping any elements.

Thank you to @Stebalien for reporting this bug and providing the
excellent testcases in this commit.

Using NoDrop expands ArrayVec to have two drop flags again, but this
is a temporary tradeoff, drop flags will eventually go away.

Fixes #3
@bluss bluss closed this as completed in #6 Aug 21, 2015
@bluss
Copy link
Owner

bluss commented Aug 21, 2015

Fixed in version 0.3.10. Use (unstable) feature flag nodrop/no_drop_flag to recover size. (However, it's not completely equivalent in terms of overhead, even if the size is the same.)

@bluss
Copy link
Owner

bluss commented Aug 21, 2015

Great, now it's fixed in smallvec too.

bors added a commit to rust-lang-ci/rust that referenced this issue Sep 12, 2021
Add -Z panic-in-drop={unwind,abort} command-line option

This PR changes `Drop` to abort if an unwinding panic attempts to escape it, making the process abort instead. This has several benefits:
- The current behavior when unwinding out of `Drop` is very unintuitive and easy to miss: unwinding continues, but the remaining drops in scope are simply leaked.
- A lot of unsafe code doesn't expect drops to unwind, which can lead to unsoundness:
  - servo/rust-smallvec#14
  - bluss/arrayvec#3
- There is a code size and compilation time cost to this: LLVM needs to generate extra landing pads out of all calls in a drop implementation. This can compound when functions are inlined since unwinding will then continue on to process drops in the callee, which can itself unwind, etc.
  - Initial measurements show a 3% size reduction and up to 10% compilation time reduction on some crates (`syn`).

One thing to note about `-Z panic-in-drop=abort` is that *all* crates must be built with this option for it to be sound since it makes the compiler assume that dropping `Box<dyn Any>` will never unwind.

cc rust-lang/lang-team#97
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants