Skip to content

Commit 8221c7f

Browse files
committed
Fix double drop in BlobVec::replace_unchecked (#2597)
1 parent 615d43b commit 8221c7f

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

crates/bevy_ecs/src/storage/blob_vec.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ impl BlobVec {
8888
/// # Safety
8989
/// - index must be in bounds
9090
/// - memory must be reserved and uninitialized
91+
/// - the contents of `*value` must not be dropped by the caller
9192
#[inline]
9293
pub unsafe fn initialize_unchecked(&mut self, index: usize, value: *mut u8) {
9394
debug_assert!(index < self.len());
@@ -97,12 +98,19 @@ impl BlobVec {
9798

9899
/// # Safety
99100
/// - index must be in-bounds
100-
// - memory must be previously initialized
101+
/// - memory must be previously initialized
102+
/// - the contents of `*value` must not be dropped by the caller. (In fact,
103+
/// this function may overwrite them.)
101104
pub unsafe fn replace_unchecked(&mut self, index: usize, value: *mut u8) {
102105
debug_assert!(index < self.len());
103106
let ptr = self.get_unchecked(index);
104-
(self.drop)(ptr);
105-
std::ptr::copy_nonoverlapping(value, ptr, self.item_layout.size());
107+
// We need to make sure that we don't still contain the old value before
108+
// calling `drop`. Otherwise, if `drop` panics, then when the collection
109+
// is dropped stack unwinding, its `Drop` impl will call `drop` again
110+
// for the old value, so we get a double drop. Also, the new value
111+
// wouldn't be dropped.
112+
std::ptr::swap_nonoverlapping(value, ptr, self.item_layout.size());
113+
(self.drop)(value);
106114
}
107115

108116
/// increases the length by one (and grows the vec if needed) with uninitialized memory and

crates/bevy_ecs/src/storage/sparse_set.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,18 @@ impl ComponentSparseSet {
123123
self.dense.len() == 0
124124
}
125125

126-
/// Inserts the `entity` key and component `value` pair into this sparse set.
127-
/// The caller is responsible for ensuring the value is not dropped. This collection will drop
128-
/// the value when needed.
126+
/// Inserts the `entity` key and component `value` pair into this sparse
127+
/// set. This collection takes ownership of the contents of `value`, and
128+
/// will drop the value when needed. Also, it may overwrite the contents of
129+
/// the `value` pointer if convenient. The caller is responsible for
130+
/// ensuring it does not drop `*value` after calling `insert`.
129131
///
130132
/// # Safety
131-
/// The `value` pointer must point to a valid address that matches the `Layout`
132-
/// inside the `ComponentInfo` given when constructing this sparse set.
133+
/// * The `value` pointer must point to a valid address that matches the
134+
/// `Layout` inside the `ComponentInfo` given when constructing this
135+
/// sparse set.
136+
/// * The caller is responsible for ensuring it does not drop `*value` after
137+
/// calling `insert`.
133138
pub unsafe fn insert(&mut self, entity: Entity, value: *mut u8, change_tick: u32) {
134139
if let Some(&dense_index) = self.sparse.get(entity) {
135140
self.dense.replace_unchecked(dense_index, value);

0 commit comments

Comments
 (0)