Skip to content

Commit 9cdbf39

Browse files
author
Lukas Markeffsky
committed
btree: don't leak value if destructor of key panics
1 parent 84fae7e commit 9cdbf39

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

alloc/src/collections/btree/map/tests.rs

+48
Original file line numberDiff line numberDiff line change
@@ -2270,6 +2270,54 @@ fn test_into_iter_drop_leak_height_0() {
22702270
assert_eq!(e.dropped(), 1);
22712271
}
22722272

2273+
#[test]
2274+
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
2275+
fn test_into_iter_drop_leak_kv_panic_in_key() {
2276+
let a_k = CrashTestDummy::new(0);
2277+
let a_v = CrashTestDummy::new(1);
2278+
let b_k = CrashTestDummy::new(2);
2279+
let b_v = CrashTestDummy::new(3);
2280+
let c_k = CrashTestDummy::new(4);
2281+
let c_v = CrashTestDummy::new(5);
2282+
let mut map = BTreeMap::new();
2283+
map.insert(a_k.spawn(Panic::Never), a_v.spawn(Panic::Never));
2284+
map.insert(b_k.spawn(Panic::InDrop), b_v.spawn(Panic::Never));
2285+
map.insert(c_k.spawn(Panic::Never), c_v.spawn(Panic::Never));
2286+
2287+
catch_unwind(move || drop(map.into_iter())).unwrap_err();
2288+
2289+
assert_eq!(a_k.dropped(), 1);
2290+
assert_eq!(a_v.dropped(), 1);
2291+
assert_eq!(b_k.dropped(), 1);
2292+
assert_eq!(b_v.dropped(), 1);
2293+
assert_eq!(c_k.dropped(), 1);
2294+
assert_eq!(c_v.dropped(), 1);
2295+
}
2296+
2297+
#[test]
2298+
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
2299+
fn test_into_iter_drop_leak_kv_panic_in_val() {
2300+
let a_k = CrashTestDummy::new(0);
2301+
let a_v = CrashTestDummy::new(1);
2302+
let b_k = CrashTestDummy::new(2);
2303+
let b_v = CrashTestDummy::new(3);
2304+
let c_k = CrashTestDummy::new(4);
2305+
let c_v = CrashTestDummy::new(5);
2306+
let mut map = BTreeMap::new();
2307+
map.insert(a_k.spawn(Panic::Never), a_v.spawn(Panic::Never));
2308+
map.insert(b_k.spawn(Panic::Never), b_v.spawn(Panic::InDrop));
2309+
map.insert(c_k.spawn(Panic::Never), c_v.spawn(Panic::Never));
2310+
2311+
catch_unwind(move || drop(map.into_iter())).unwrap_err();
2312+
2313+
assert_eq!(a_k.dropped(), 1);
2314+
assert_eq!(a_v.dropped(), 1);
2315+
assert_eq!(b_k.dropped(), 1);
2316+
assert_eq!(b_v.dropped(), 1);
2317+
assert_eq!(c_k.dropped(), 1);
2318+
assert_eq!(c_v.dropped(), 1);
2319+
}
2320+
22732321
#[test]
22742322
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
22752323
fn test_into_iter_drop_leak_height_1() {

alloc/src/collections/btree/node.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -1173,11 +1173,25 @@ impl<K, V, NodeType> Handle<NodeRef<marker::Dying, K, V, NodeType>, marker::KV>
11731173
/// The node that the handle refers to must not yet have been deallocated.
11741174
#[inline]
11751175
pub unsafe fn drop_key_val(mut self) {
1176+
// Run the destructor of the value even if the destructor of the key panics.
1177+
struct Dropper<'a, T>(&'a mut MaybeUninit<T>);
1178+
impl<T> Drop for Dropper<'_, T> {
1179+
#[inline]
1180+
fn drop(&mut self) {
1181+
unsafe {
1182+
self.0.assume_init_drop();
1183+
}
1184+
}
1185+
}
1186+
11761187
debug_assert!(self.idx < self.node.len());
11771188
let leaf = self.node.as_leaf_dying();
11781189
unsafe {
1179-
leaf.keys.get_unchecked_mut(self.idx).assume_init_drop();
1180-
leaf.vals.get_unchecked_mut(self.idx).assume_init_drop();
1190+
let key = leaf.keys.get_unchecked_mut(self.idx);
1191+
let val = leaf.vals.get_unchecked_mut(self.idx);
1192+
let _guard = Dropper(val);
1193+
key.assume_init_drop();
1194+
// dropping the guard will drop the value
11811195
}
11821196
}
11831197
}

0 commit comments

Comments
 (0)