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

Double free in Vec::from_iter specialization when drop panics #83618

Closed
Qwaz opened this issue Mar 28, 2021 · 2 comments
Closed

Double free in Vec::from_iter specialization when drop panics #83618

Qwaz opened this issue Mar 28, 2021 · 2 comments
Assignees
Labels
A-collections Area: std::collections. A-destructors Area: destructors (Drop, ..) C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-critical Critical priority T-libs Relevant to the library team, which will review and decide on the PR/issue.

Comments

@Qwaz
Copy link
Contributor

Qwaz commented Mar 28, 2021

// drop any remaining values at the tail of the source
src.drop_remaining();

pub(super) fn drop_remaining(&mut self) {
unsafe {
ptr::drop_in_place(self.as_mut_slice());
}
self.ptr = self.end;
}

SpecFromIter<T, I> for Vec<T> calls Vec::IntoIter::drop_remaining(). drop_remaining() calls drop_in_place() before overwriting the pointer. As a result, dropped elements are not invalidated and dropped again under panic.

PoC:

#![forbid(unsafe_code)]

use std::iter::FromIterator;

#[derive(Debug)]
enum MyEnum {
    DroppedTwice(Box<i32>),
    PanicOnDrop,
}

impl Drop for MyEnum {
    fn drop(&mut self) {
        match self {
            MyEnum::DroppedTwice(_) => println!("Dropping!"),
            MyEnum::PanicOnDrop => {
                if !std::thread::panicking() {
                    panic!();
                }
            }
        }
    }
}

fn main() {
    let v = vec![MyEnum::DroppedTwice(Box::new(123)), MyEnum::PanicOnDrop];
    Vec::from_iter(v.into_iter().take(0));
}

Output:

Dropping!
thread 'main' panicked at 'explicit panic', src/main.rs:17:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Dropping!
free(): double free detected in tcache 2

Tested with rustc 1.51.0. Here is a playground link to the code snippet.

@Qwaz Qwaz added the C-bug Category: This is a bug. label Mar 28, 2021
@jonas-schievink jonas-schievink added A-collections Area: std::collections. A-destructors Area: destructors (Drop, ..) I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Mar 28, 2021
@rustbot rustbot added the I-prioritize Issue: Indicates that prioritization has been requested for this issue. label Mar 28, 2021
@the8472
Copy link
Member

the8472 commented Mar 29, 2021

Thanks, I totally didn't consider that case!
According to #14875 panic during drop should result in the storage being leaked. That shouldn't be too difficult to achieve by forgetting the original allocation before the drop.

The question is whether it is necessary to attempt to drop the stuff moved to output vec or whether we can leak those too.

@rustbot claim

@JohnTitor
Copy link
Member

Assigning P-critical as discussed as part of the Prioritization Working Group procedure and removing I-prioritize.

@JohnTitor JohnTitor added P-critical Critical priority and removed I-prioritize Issue: Indicates that prioritization has been requested for this issue. labels Mar 29, 2021
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Apr 1, 2021
…r=m-ou-se

Fix double-drop in `Vec::from_iter(vec.into_iter())` specialization when items drop during panic

This fixes the double-drop but it leaves a behavioral difference compared to the default implementation intact: In the default implementation the source and the destination vec are separate objects, so they get dropped separately. Here they share an allocation and the latter only exists as a pointer into the former. So if dropping the former panics then this fix will leak more items than the default implementation would. Is this acceptable or should the specialization also mimic the default implementation's drops-during-panic behavior?

Fixes rust-lang#83618

`@rustbot` label T-libs-impl
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Apr 2, 2021
…r=m-ou-se

Fix double-drop in `Vec::from_iter(vec.into_iter())` specialization when items drop during panic

This fixes the double-drop but it leaves a behavioral difference compared to the default implementation intact: In the default implementation the source and the destination vec are separate objects, so they get dropped separately. Here they share an allocation and the latter only exists as a pointer into the former. So if dropping the former panics then this fix will leak more items than the default implementation would. Is this acceptable or should the specialization also mimic the default implementation's drops-during-panic behavior?

Fixes rust-lang#83618

`@rustbot` label T-libs-impl
@bors bors closed this as completed in 542f441 Apr 2, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-collections Area: std::collections. A-destructors Area: destructors (Drop, ..) C-bug Category: This is a bug. I-unsound Issue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/Soundness P-critical Critical priority T-libs Relevant to the library team, which will review and decide on the PR/issue.
Projects
None yet
5 participants