Skip to content

Commit bc3b044

Browse files
authored
fix(header): use a set_len guard in IntoIter drop (#838)
1 parent 1b968dc commit bc3b044

2 files changed

Lines changed: 61 additions & 5 deletions

File tree

src/header/map.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3195,13 +3195,20 @@ impl<T> FusedIterator for IntoIter<T> {}
31953195

31963196
impl<T> Drop for IntoIter<T> {
31973197
fn drop(&mut self) {
3198-
// Ensure the iterator is consumed
3199-
for _ in self.by_ref() {}
3198+
struct Guard<'a, T>(&'a mut IntoIter<T>);
32003199

3201-
// All the values have already been yielded out.
3202-
unsafe {
3203-
self.extra_values.set_len(0);
3200+
impl<'a, T> Drop for Guard<'a, T> {
3201+
fn drop(&mut self) {
3202+
unsafe {
3203+
self.0.extra_values.set_len(0);
3204+
}
3205+
}
32043206
}
3207+
3208+
let guard = Guard(self);
3209+
3210+
// Ensure the iterator is consumed
3211+
for _ in guard.0.by_ref() {}
32053212
}
32063213
}
32073214

tests/header_map.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,3 +723,52 @@ fn ensure_miri_valueitermut_not_violated() {
723723
*first += 10;
724724
*second += 20;
725725
}
726+
727+
#[test]
728+
fn into_iter_drop_panic_after_yielding_extra_value_double_drops() {
729+
use std::panic::{catch_unwind, AssertUnwindSafe};
730+
731+
struct ManuallyAllocated {
732+
ptr: *mut u8,
733+
panic_on_drop: bool,
734+
}
735+
736+
impl ManuallyAllocated {
737+
fn new(byte: u8, panic_on_drop: bool) -> Self {
738+
Self {
739+
ptr: Box::into_raw(Box::new(byte)),
740+
panic_on_drop,
741+
}
742+
}
743+
}
744+
745+
impl Drop for ManuallyAllocated {
746+
fn drop(&mut self) {
747+
unsafe {
748+
drop(Box::from_raw(self.ptr));
749+
}
750+
751+
if self.panic_on_drop {
752+
panic!("intentional drop panic");
753+
}
754+
}
755+
}
756+
757+
let mut map: HeaderMap<ManuallyAllocated> = HeaderMap::default();
758+
map.append("x-first", ManuallyAllocated::new(1, false));
759+
map.append("x-first", ManuallyAllocated::new(2, false));
760+
map.insert("x-second", ManuallyAllocated::new(3, true));
761+
762+
let mut iter = map.into_iter();
763+
764+
// HeaderMap::IntoIter yields extra values with ptr::read from
765+
// self.extra_values and relies on Drop setting self.extra_values.len() to
766+
// zero after the iterator has been fully consumed. If a later value's Drop
767+
// panics while IntoIter::drop is draining the iterator, that set_len(0) is
768+
// skipped. The Vec then drops already-yielded extra value slots again. The
769+
// safe sequence below therefore double-frees byte 2 under Miri.
770+
drop(iter.next().unwrap());
771+
drop(iter.next().unwrap());
772+
773+
let _ = catch_unwind(AssertUnwindSafe(|| drop(iter)));
774+
}

0 commit comments

Comments
 (0)