Skip to content

Commit 5a95716

Browse files
committed
Fix double drop in BlobVec::replace_unchecked (#2597)
1 parent d65fbd7 commit 5a95716

File tree

2 files changed

+29
-7
lines changed

2 files changed

+29
-7
lines changed

crates/bevy_ecs/src/storage/blob_vec.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,12 @@ impl BlobVec {
8787

8888
/// # Safety
8989
/// - index must be in bounds
90-
/// - memory must be reserved and uninitialized
90+
/// - the memory in the `BlobVec` starting at index `index`, of a size matching this `BlobVec`'s
91+
/// `item_layout`, must have been previously allocated, but not initialized yet
92+
/// - the memory at `*value` must be previously initialized with an item matching this
93+
/// `BlobVec`'s `item_layout`
94+
/// - the item that was stored in `*value` is left logically uninitialised/moved out of after
95+
/// calling this function, and as such should not be used or dropped by the caller.
9196
#[inline]
9297
pub unsafe fn initialize_unchecked(&mut self, index: usize, value: *mut u8) {
9398
debug_assert!(index < self.len());
@@ -97,12 +102,24 @@ impl BlobVec {
97102

98103
/// # Safety
99104
/// - index must be in-bounds
100-
// - memory must be previously initialized
105+
/// - the memory in the `BlobVec` starting at index `index`, of a size matching this `BlobVec`'s
106+
/// `item_layout`, must have been previously initialized with an item matching this `BlobVec`'s
107+
/// item_layout
108+
/// - the memory at `*value` must also be previously initialized with an item matching this
109+
/// `BlobVec`'s `item_layout`
110+
/// - the item that was stored in `*value` is left logically uninitialised/moved out of after
111+
/// calling this function, and as such should not be used or dropped by the caller.
101112
pub unsafe fn replace_unchecked(&mut self, index: usize, value: *mut u8) {
102113
debug_assert!(index < self.len());
103114
let ptr = self.get_unchecked(index);
115+
// If `drop` panics, then when the collection is dropped during stack unwinding, the
116+
// collection's `Drop` impl will call `drop` again for the old value (which is still stored
117+
// in the collection), so we get a double drop. To prevent that, we set len to 0 until we're
118+
// done.
119+
let old_len = std::mem::replace(&mut self.len, 0);
104120
(self.drop)(ptr);
105121
std::ptr::copy_nonoverlapping(value, ptr, self.item_layout.size());
122+
self.len = old_len;
106123
}
107124

108125
/// 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)