Skip to content

Commit 165a1d2

Browse files
committed
Fix UB in ResourceData and ComponentSparseSet
1 parent 59a0def commit 165a1d2

File tree

3 files changed

+31
-30
lines changed

3 files changed

+31
-30
lines changed

crates/bevy_ecs/src/storage/resource.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
};
66
use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
77
use bevy_utils::prelude::DebugName;
8-
use core::{cell::UnsafeCell, mem::ManuallyDrop, panic::Location};
8+
use core::{cell::UnsafeCell, panic::Location};
99

1010
#[cfg(feature = "std")]
1111
use std::thread::ThreadId;
@@ -16,7 +16,7 @@ use std::thread::ThreadId;
1616
///
1717
/// [`World`]: crate::world::World
1818
pub struct ResourceData<const SEND: bool> {
19-
data: ManuallyDrop<BlobArray>,
19+
data: BlobArray,
2020
is_present: bool,
2121
added_ticks: UnsafeCell<Tick>,
2222
changed_ticks: UnsafeCell<Tick>,
@@ -50,7 +50,7 @@ impl<const SEND: bool> Drop for ResourceData<SEND> {
5050
// been dropped. The validate_access call above will check that the
5151
// data is dropped on the thread it was inserted from.
5252
unsafe {
53-
ManuallyDrop::drop(&mut self.data);
53+
self.data.drop(1, self.is_present().into());
5454
}
5555
}
5656
}
@@ -379,7 +379,7 @@ impl<const SEND: bool> Resources<SEND> {
379379
)
380380
};
381381
ResourceData {
382-
data: ManuallyDrop::new(data),
382+
data,
383383
is_present: false,
384384
added_ticks: UnsafeCell::new(Tick::new(0)),
385385
changed_ticks: UnsafeCell::new(Tick::new(0)),

crates/bevy_ecs/src/storage/sparse_set.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use crate::{
33
component::{CheckChangeTicks, ComponentId, ComponentInfo, ComponentTicks, Tick, TickCells},
44
entity::{Entity, EntityRow},
55
query::DebugCheckedUnwrap,
6-
storage::VecExtensions,
7-
storage::{AbortOnPanic, TableRow, ThinColumn},
6+
storage::{AbortOnPanic, TableRow, ThinColumn, VecExtensions},
87
};
98
use alloc::{boxed::Box, vec::Vec};
109
use bevy_ptr::{OwningPtr, Ptr};
@@ -155,7 +154,7 @@ impl ComponentSparseSet {
155154
/// Returns `true` if the sparse set contains no component values.
156155
#[inline]
157156
pub fn is_empty(&self) -> bool {
158-
self.entities.len() == 0
157+
self.entities.is_empty()
159158
}
160159

161160
/// Inserts the `entity` key and component `value` pair into this sparse
@@ -330,12 +329,12 @@ impl ComponentSparseSet {
330329
self.sparse.remove(entity.row()).map(|dense_index| {
331330
#[cfg(debug_assertions)]
332331
assert_eq!(entity, self.entities[dense_index.index()]);
333-
let len = self.entities.len();
334-
if dense_index.index() >= len - 1 {
332+
let last = self.entities.len() - 1;
333+
if dense_index.index() >= last {
335334
#[cfg(debug_assertions)]
336-
assert_eq!(dense_index.index(), len - 1);
335+
assert_eq!(dense_index.index(), last);
337336
// SAFETY: TODO
338-
unsafe { self.entities.set_len(dense_index.index()) };
337+
unsafe { self.entities.set_len(last) };
339338
// SAFETY: TODO
340339
unsafe {
341340
self.dense
@@ -361,7 +360,7 @@ impl ComponentSparseSet {
361360
// SAFETY: dense_index was just removed from `sparse`, which ensures that it is valid
362361
unsafe {
363362
self.dense
364-
.swap_remove_and_forget_unchecked(len, dense_index)
363+
.swap_remove_and_forget_unchecked_nonoverlapping(last, dense_index)
365364
}
366365
}
367366
})
@@ -376,14 +375,14 @@ impl ComponentSparseSet {
376375
.map(|dense_index| {
377376
#[cfg(debug_assertions)]
378377
assert_eq!(entity, self.entities[dense_index.index()]);
379-
let len = self.entities.len();
380-
if dense_index.index() >= len - 1 {
378+
let last = self.entities.len() - 1;
379+
if dense_index.index() >= last {
381380
#[cfg(debug_assertions)]
382-
assert_eq!(dense_index.index(), len - 1);
381+
assert_eq!(dense_index.index(), last);
383382
// SAFETY: TODO
384-
unsafe { self.entities.set_len(dense_index.index()) };
383+
unsafe { self.entities.set_len(last) };
385384
// SAFETY: TODO
386-
unsafe { self.dense.drop_last_component(dense_index.index()) };
385+
unsafe { self.dense.drop_last_component(last) };
387386
} else {
388387
// SAFETY: TODO
389388
unsafe {
@@ -403,8 +402,8 @@ impl ComponentSparseSet {
403402
// SAFETY: TODO
404403
unsafe {
405404
self.dense
406-
.swap_remove_and_drop_unchecked_nonoverlapping(len, dense_index);
407-
};
405+
.swap_remove_and_drop_unchecked_nonoverlapping(last, dense_index);
406+
}
408407
}
409408
})
410409
.is_some()
@@ -415,6 +414,17 @@ impl ComponentSparseSet {
415414
}
416415
}
417416

417+
impl Drop for ComponentSparseSet {
418+
fn drop(&mut self) {
419+
let len = self.entities.len();
420+
self.entities.clear();
421+
// SAFETY: `cap` and `len` are correct. `dense` is never accessed again after this call.
422+
unsafe {
423+
self.dense.drop(self.entities.capacity(), len);
424+
}
425+
}
426+
}
427+
418428
/// A data structure that blends dense and sparse storage
419429
///
420430
/// `I` is the type of the indices, while `V` is the type of data stored in the dense storage.

crates/bevy_ecs/src/storage/table/mod.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
component::{CheckChangeTicks, ComponentId, ComponentInfo, ComponentTicks, Components, Tick},
44
entity::Entity,
55
query::DebugCheckedUnwrap,
6-
storage::{ImmutableSparseSet, SparseSet},
6+
storage::{AbortOnPanic, ImmutableSparseSet, SparseSet},
77
};
88
use alloc::{boxed::Box, vec, vec::Vec};
99
use bevy_platform::collections::HashMap;
@@ -182,15 +182,6 @@ pub struct Table {
182182
entities: Vec<Entity>,
183183
}
184184

185-
struct AbortOnPanic;
186-
187-
impl Drop for AbortOnPanic {
188-
fn drop(&mut self) {
189-
// Panicking while unwinding will force an abort.
190-
panic!("Aborting due to allocator error");
191-
}
192-
}
193-
194185
impl Table {
195186
/// Fetches a read-only slice of the entities stored within the [`Table`].
196187
#[inline]
@@ -821,7 +812,7 @@ impl Drop for Table {
821812
let cap = self.capacity();
822813
self.entities.clear();
823814
for col in self.columns.values_mut() {
824-
// SAFETY: `cap` and `len` are correct
815+
// SAFETY: `cap` and `len` are correct. `col` is never accessed again after this call.
825816
unsafe {
826817
col.drop(cap, len);
827818
}

0 commit comments

Comments
 (0)