Skip to content

Commit c4c1bff

Browse files
committed
Make vec::IntoIter::drop and VecInner::truncate consistent.
1 parent 45bb608 commit c4c1bff

1 file changed

Lines changed: 70 additions & 45 deletions

File tree

src/vec/mod.rs

Lines changed: 70 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -735,24 +735,24 @@ impl<T, LenT: LenType, S: VecStorage<T> + ?Sized> VecInner<T, LenT, S> {
735735

736736
/// Shortens the vector, keeping the first `len` elements and dropping the rest.
737737
pub fn truncate(&mut self, len: usize) {
738-
// This is safe because:
738+
// NOTE: It is intentional that this is `>`. Changing it to `>=` may have negative
739+
// performance implications. See rust-lang/rust#78884 for more details.
740+
if len > self.len() {
741+
return;
742+
}
743+
744+
// SAFETY: `self.buffer` contains initialized elements in the range `len..(len + remaining_len)`,
745+
// so it is safe to create a mutable slice of them and drop them.
739746
//
740-
// * the slice passed to `drop_in_place` is valid; the `len > self.len` case avoids creating
741-
// an invalid slice, and
742-
// * the `len` of the vector is shrunk before calling `drop_in_place`, such that no value
743-
// will be dropped twice in case `drop_in_place` were to panic once (if it panics twice,
744-
// the program aborts).
747+
// `self.len` is set before dropping the excess elements so that a panicking
748+
// `Drop` implementation does not result in inconsistent state during
749+
// unwinding that could lead to undefined behaviour.
745750
unsafe {
746-
// Note: It's intentional that this is `>` and not `>=`.
747-
// Changing it to `>=` has negative performance
748-
// implications in some cases. See rust-lang/rust#78884 for more.
749-
if len > self.len() {
750-
return;
751-
}
752751
let remaining_len = self.len() - len;
753-
let s = ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len);
752+
let remaining =
753+
ptr::slice_from_raw_parts_mut(self.as_mut_ptr().add(len), remaining_len);
754754
self.len = LenT::from_usize(len);
755-
ptr::drop_in_place(s);
755+
ptr::drop_in_place(remaining);
756756
}
757757
}
758758

@@ -1572,13 +1572,21 @@ where
15721572
impl<T, LenT: LenType, const N: usize> Drop for IntoIter<T, N, LenT> {
15731573
fn drop(&mut self) {
15741574
let len = self.vec.len;
1575-
self.vec.len = LenT::ZERO;
1575+
1576+
// SAFETY: `self.vec` contains initialized elements in the range `self.next..len`,
1577+
// so it is safe to create a mutable slice of them and drop them.
1578+
//
1579+
// `self.vec.len` is set to `0` before dropping the elements so that
1580+
// a panicking `Drop` implementation does not result in inconsistent
1581+
// state during unwinding that could lead to undefined behaviour.
15761582
unsafe {
1577-
// Drop all the elements that have not been moved out of vec
1583+
// Drop all elements that have not been consumed yet.
1584+
let remaining_len = len - self.next;
15781585
let remaining = slice::from_raw_parts_mut(
15791586
self.vec.as_mut_ptr().add(self.next.into_usize()),
1580-
(len - self.next).into_usize(),
1587+
remaining_len.into_usize(),
15811588
);
1589+
self.vec.len = LenT::ZERO;
15821590
ptr::drop_in_place(remaining);
15831591
}
15841592
}
@@ -1810,7 +1818,7 @@ where
18101818
mod tests {
18111819
use core::fmt::Write;
18121820
use std::{
1813-
panic::catch_unwind,
1821+
panic::{catch_unwind, AssertUnwindSafe},
18141822
sync::atomic::{AtomicI32, Ordering::Relaxed},
18151823
};
18161824

@@ -2393,40 +2401,57 @@ mod tests {
23932401
}
23942402
}
23952403

2404+
#[derive(Debug)]
2405+
struct Dropper<'a>(&'a AtomicI32);
2406+
2407+
impl<'a> Dropper<'a> {
2408+
fn new(count: &'a AtomicI32) -> Self {
2409+
count.fetch_add(1, Relaxed);
2410+
Self(count)
2411+
}
2412+
}
2413+
impl Drop for Dropper<'_> {
2414+
fn drop(&mut self) {
2415+
self.0.fetch_sub(1, Relaxed);
2416+
panic!("Testing panicking");
2417+
}
2418+
}
2419+
2420+
// This tests that a panic in a downstream drop implementation does not lead to an
2421+
// inconsistent state during unwinding that could lead to undefined behaviour.
2422+
// For more info see https://github.com/rust-embedded/heapless/issues/659.
23962423
#[test]
2397-
fn test_use_after_free_intoiter_drop() {
2398-
// This tests that a panic in a downstream drop implementation does not lead to an
2399-
// inconsistent state during unwinding that could lead to undefined behaviour.
2400-
// For more info see https://github.com/rust-embedded/heapless/issues/659
2424+
fn test_use_after_free_clear() {
2425+
let count = AtomicI32::new(0);
24012426

2402-
static COUNT: AtomicI32 = AtomicI32::new(0);
2427+
let mut vec = Vec::<Dropper, 5>::new();
2428+
vec.push(Dropper::new(&count)).unwrap();
24032429

2404-
#[derive(Debug)]
2405-
#[allow(unused)]
2406-
struct Dropper();
2430+
catch_unwind(AssertUnwindSafe(|| {
2431+
vec.clear();
2432+
}))
2433+
.unwrap_err();
24072434

2408-
impl Dropper {
2409-
fn new() -> Self {
2410-
COUNT.fetch_add(1, Relaxed);
2411-
Self()
2412-
}
2413-
fn count() -> i32 {
2414-
COUNT.load(Relaxed)
2415-
}
2416-
}
2417-
impl Drop for Dropper {
2418-
fn drop(&mut self) {
2419-
COUNT.fetch_sub(1, Relaxed);
2420-
panic!("Testing panicking");
2421-
}
2422-
}
2435+
assert_eq!(count.load(Relaxed), 0);
2436+
assert_eq!(vec.len(), 0);
2437+
}
2438+
2439+
// This tests that a panic in a downstream drop implementation does not lead to an
2440+
// inconsistent state during unwinding that could lead to undefined behaviour.
2441+
// For more info see https://github.com/rust-embedded/heapless/issues/659.
2442+
#[test]
2443+
fn test_use_after_free_intoiter_drop() {
2444+
let count = AtomicI32::new(0);
24232445

24242446
let mut vec = Vec::<Dropper, 5>::new();
2425-
vec.push(Dropper::new()).unwrap();
2426-
let iterator = vec.into_iter();
2447+
vec.push(Dropper::new(&count)).unwrap();
2448+
2449+
catch_unwind(move || {
2450+
drop(vec.into_iter());
2451+
})
2452+
.unwrap_err();
24272453

2428-
catch_unwind(move || drop(iterator)).unwrap_err();
2429-
assert_eq!(Dropper::count(), 0);
2454+
assert_eq!(count.load(Relaxed), 0);
24302455
}
24312456

24322457
fn _test_variance<'a: 'b, 'b>(x: Vec<&'a (), 42>) -> Vec<&'b (), 42> {

0 commit comments

Comments
 (0)