Make SystemParam::new_archetype and QueryState::new_archetype unsafe functions (#13044)
# Objective Fix #2128. Both `Query::new_archetype` and `SystemParam::new_archetype` do not check if the `Archetype` comes from the same World the state is initialized from. This could result in unsoundness via invalid accesses if called incorrectly. ## Solution Make them `unsafe` functions and lift the invariant to the caller. This also caught one instance of us not validating the World in `SystemState::update_archetypes_unsafe_world_cell`'s implementation. --- ## Changelog Changed: `QueryState::new_archetype` is now an unsafe function. Changed: `SystemParam::new_archetype` is now an unsafe function. ## Migration Guide `QueryState::new_archetype` and `SystemParam::new_archetype` are now an unsafe functions that must be sure that the provided `Archetype` is from the same `World` that the state was initialized from. Callers may need to add additional assertions or propagate the safety invariant upwards through the callstack to ensure safety.
This commit is contained in:
parent
8403c41c67
commit
54456b7ea6
@ -239,8 +239,9 @@ pub fn impl_param_set(_input: TokenStream) -> TokenStream {
|
|||||||
(#(#param,)*)
|
(#(#param,)*)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_archetype(state: &mut Self::State, archetype: &Archetype, system_meta: &mut SystemMeta) {
|
unsafe fn new_archetype(state: &mut Self::State, archetype: &Archetype, system_meta: &mut SystemMeta) {
|
||||||
<(#(#param,)*) as SystemParam>::new_archetype(state, archetype, system_meta);
|
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
|
||||||
|
unsafe { <(#(#param,)*) as SystemParam>::new_archetype(state, archetype, system_meta); }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
|
fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
|
||||||
@ -425,8 +426,9 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_archetype(state: &mut Self::State, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
|
unsafe fn new_archetype(state: &mut Self::State, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
|
||||||
<#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::new_archetype(&mut state.state, archetype, system_meta)
|
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
|
||||||
|
unsafe { <#fields_alias::<'_, '_, #punctuated_generic_idents> as #path::system::SystemParam>::new_archetype(&mut state.state, archetype, system_meta) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
|
fn apply(state: &mut Self::State, system_meta: &#path::system::SystemMeta, world: &mut #path::world::World) {
|
||||||
|
@ -161,8 +161,12 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
let mut state = Self::new_uninitialized(world);
|
let mut state = Self::new_uninitialized(world);
|
||||||
for archetype in world.archetypes.iter() {
|
for archetype in world.archetypes.iter() {
|
||||||
if state.new_archetype_internal(archetype) {
|
// SAFETY: The state was just initialized from the `world` above, and the archetypes being added
|
||||||
state.update_archetype_component_access(archetype, access);
|
// come directly from the same world.
|
||||||
|
unsafe {
|
||||||
|
if state.new_archetype_internal(archetype) {
|
||||||
|
state.update_archetype_component_access(archetype, access);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state.archetype_generation = world.archetypes.generation();
|
state.archetype_generation = world.archetypes.generation();
|
||||||
@ -342,7 +346,11 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
std::mem::replace(&mut self.archetype_generation, archetypes.generation());
|
std::mem::replace(&mut self.archetype_generation, archetypes.generation());
|
||||||
|
|
||||||
for archetype in &archetypes[old_generation..] {
|
for archetype in &archetypes[old_generation..] {
|
||||||
self.new_archetype_internal(archetype);
|
// SAFETY: The validate_world call ensures that the world is the same the QueryState
|
||||||
|
// was initialized from.
|
||||||
|
unsafe {
|
||||||
|
self.new_archetype_internal(archetype);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,13 +379,19 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
/// (if applicable, i.e. if the archetype has any intersecting [`ComponentId`] with the current [`QueryState`]).
|
/// (if applicable, i.e. if the archetype has any intersecting [`ComponentId`] with the current [`QueryState`]).
|
||||||
///
|
///
|
||||||
/// The passed in `access` will be updated with any new accesses introduced by the new archetype.
|
/// The passed in `access` will be updated with any new accesses introduced by the new archetype.
|
||||||
pub fn new_archetype(
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `archetype` must be from the `World` this state was initialized from.
|
||||||
|
pub unsafe fn new_archetype(
|
||||||
&mut self,
|
&mut self,
|
||||||
archetype: &Archetype,
|
archetype: &Archetype,
|
||||||
access: &mut Access<ArchetypeComponentId>,
|
access: &mut Access<ArchetypeComponentId>,
|
||||||
) {
|
) {
|
||||||
if self.new_archetype_internal(archetype) {
|
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from.
|
||||||
self.update_archetype_component_access(archetype, access);
|
let matches = unsafe { self.new_archetype_internal(archetype) };
|
||||||
|
if matches {
|
||||||
|
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from.
|
||||||
|
unsafe { self.update_archetype_component_access(archetype, access) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +400,10 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
///
|
///
|
||||||
/// Returns `true` if the given `archetype` matches the query. Otherwise, returns `false`.
|
/// Returns `true` if the given `archetype` matches the query. Otherwise, returns `false`.
|
||||||
/// If there is no match, then there is no need to update the query's [`FilteredAccess`].
|
/// If there is no match, then there is no need to update the query's [`FilteredAccess`].
|
||||||
fn new_archetype_internal(&mut self, archetype: &Archetype) -> bool {
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `archetype` must be from the `World` this state was initialized from.
|
||||||
|
unsafe fn new_archetype_internal(&mut self, archetype: &Archetype) -> bool {
|
||||||
if D::matches_component_set(&self.fetch_state, &|id| archetype.contains(id))
|
if D::matches_component_set(&self.fetch_state, &|id| archetype.contains(id))
|
||||||
&& F::matches_component_set(&self.filter_state, &|id| archetype.contains(id))
|
&& F::matches_component_set(&self.filter_state, &|id| archetype.contains(id))
|
||||||
&& self.matches_component_set(&|id| archetype.contains(id))
|
&& self.matches_component_set(&|id| archetype.contains(id))
|
||||||
@ -431,7 +448,10 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
|||||||
/// For the given `archetype`, adds any component accessed used by this query's underlying [`FilteredAccess`] to `access`.
|
/// For the given `archetype`, adds any component accessed used by this query's underlying [`FilteredAccess`] to `access`.
|
||||||
///
|
///
|
||||||
/// The passed in `access` will be updated with any new accesses introduced by the new archetype.
|
/// The passed in `access` will be updated with any new accesses introduced by the new archetype.
|
||||||
pub fn update_archetype_component_access(
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `archetype` must be from the `World` this state was initialized from.
|
||||||
|
pub unsafe fn update_archetype_component_access(
|
||||||
&mut self,
|
&mut self,
|
||||||
archetype: &Archetype,
|
archetype: &Archetype,
|
||||||
access: &mut Access<ArchetypeComponentId>,
|
access: &mut Access<ArchetypeComponentId>,
|
||||||
|
@ -284,12 +284,15 @@ impl<Param: SystemParam> SystemState<Param> {
|
|||||||
/// This method only accesses world metadata.
|
/// This method only accesses world metadata.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) {
|
pub fn update_archetypes_unsafe_world_cell(&mut self, world: UnsafeWorldCell) {
|
||||||
|
assert_eq!(self.world_id, world.id(), "Encountered a mismatched World. A System cannot be used with Worlds other than the one it was initialized with.");
|
||||||
|
|
||||||
let archetypes = world.archetypes();
|
let archetypes = world.archetypes();
|
||||||
let old_generation =
|
let old_generation =
|
||||||
std::mem::replace(&mut self.archetype_generation, archetypes.generation());
|
std::mem::replace(&mut self.archetype_generation, archetypes.generation());
|
||||||
|
|
||||||
for archetype in &archetypes[old_generation..] {
|
for archetype in &archetypes[old_generation..] {
|
||||||
Param::new_archetype(&mut self.param_state, archetype, &mut self.meta);
|
// SAFETY: The assertion above ensures that the param_state was initialized from `world`.
|
||||||
|
unsafe { Param::new_archetype(&mut self.param_state, archetype, &mut self.meta) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -527,7 +530,8 @@ where
|
|||||||
|
|
||||||
for archetype in &archetypes[old_generation..] {
|
for archetype in &archetypes[old_generation..] {
|
||||||
let param_state = self.param_state.as_mut().unwrap();
|
let param_state = self.param_state.as_mut().unwrap();
|
||||||
F::Param::new_archetype(param_state, archetype, &mut self.system_meta);
|
// SAFETY: The assertion above ensures that the param_state was initialized from `world`.
|
||||||
|
unsafe { F::Param::new_archetype(param_state, archetype, &mut self.system_meta) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,12 +137,16 @@ pub unsafe trait SystemParam: Sized {
|
|||||||
/// and creates a new instance of this param's [`State`](Self::State).
|
/// and creates a new instance of this param's [`State`](Self::State).
|
||||||
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State;
|
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State;
|
||||||
|
|
||||||
/// For the specified [`Archetype`], registers the components accessed by this [`SystemParam`] (if applicable).
|
/// For the specified [`Archetype`], registers the components accessed by this [`SystemParam`] (if applicable).a
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// `archetype` must be from the [`World`] used to initialize `state` in `init_state`.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn new_archetype(
|
#[allow(unused_variables)]
|
||||||
_state: &mut Self::State,
|
unsafe fn new_archetype(
|
||||||
_archetype: &Archetype,
|
state: &mut Self::State,
|
||||||
_system_meta: &mut SystemMeta,
|
archetype: &Archetype,
|
||||||
|
system_meta: &mut SystemMeta,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,7 +212,11 @@ unsafe impl<D: QueryData + 'static, F: QueryFilter + 'static> SystemParam for Qu
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_archetype(state: &mut Self::State, archetype: &Archetype, system_meta: &mut SystemMeta) {
|
unsafe fn new_archetype(
|
||||||
|
state: &mut Self::State,
|
||||||
|
archetype: &Archetype,
|
||||||
|
system_meta: &mut SystemMeta,
|
||||||
|
) {
|
||||||
state.new_archetype(archetype, &mut system_meta.archetype_component_access);
|
state.new_archetype(archetype, &mut system_meta.archetype_component_access);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1343,8 +1351,10 @@ macro_rules! impl_system_param_tuple {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn new_archetype(($($param,)*): &mut Self::State, _archetype: &Archetype, _system_meta: &mut SystemMeta) {
|
#[allow(unused_unsafe)]
|
||||||
$($param::new_archetype($param, _archetype, _system_meta);)*
|
unsafe fn new_archetype(($($param,)*): &mut Self::State, _archetype: &Archetype, _system_meta: &mut SystemMeta) {
|
||||||
|
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
|
||||||
|
unsafe { $($param::new_archetype($param, _archetype, _system_meta);)* }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -1487,8 +1497,13 @@ unsafe impl<P: SystemParam + 'static> SystemParam for StaticSystemParam<'_, '_,
|
|||||||
P::init_state(world, system_meta)
|
P::init_state(world, system_meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_archetype(state: &mut Self::State, archetype: &Archetype, system_meta: &mut SystemMeta) {
|
unsafe fn new_archetype(
|
||||||
P::new_archetype(state, archetype, system_meta);
|
state: &mut Self::State,
|
||||||
|
archetype: &Archetype,
|
||||||
|
system_meta: &mut SystemMeta,
|
||||||
|
) {
|
||||||
|
// SAFETY: The caller guarantees that the provided `archetype` matches the World used to initialize `state`.
|
||||||
|
unsafe { P::new_archetype(state, archetype, system_meta) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
|
fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
|
||||||
|
@ -55,21 +55,26 @@ pub struct GizmosFetchState<T: GizmoConfigGroup> {
|
|||||||
unsafe impl<T: GizmoConfigGroup> SystemParam for Gizmos<'_, '_, T> {
|
unsafe impl<T: GizmoConfigGroup> SystemParam for Gizmos<'_, '_, T> {
|
||||||
type State = GizmosFetchState<T>;
|
type State = GizmosFetchState<T>;
|
||||||
type Item<'w, 's> = Gizmos<'w, 's, T>;
|
type Item<'w, 's> = Gizmos<'w, 's, T>;
|
||||||
|
|
||||||
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
|
fn init_state(world: &mut World, system_meta: &mut SystemMeta) -> Self::State {
|
||||||
GizmosFetchState {
|
GizmosFetchState {
|
||||||
state: GizmosState::<T>::init_state(world, system_meta),
|
state: GizmosState::<T>::init_state(world, system_meta),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn new_archetype(
|
|
||||||
|
unsafe fn new_archetype(
|
||||||
state: &mut Self::State,
|
state: &mut Self::State,
|
||||||
archetype: &bevy_ecs::archetype::Archetype,
|
archetype: &bevy_ecs::archetype::Archetype,
|
||||||
system_meta: &mut SystemMeta,
|
system_meta: &mut SystemMeta,
|
||||||
) {
|
) {
|
||||||
GizmosState::<T>::new_archetype(&mut state.state, archetype, system_meta);
|
// SAFETY: The caller ensures that `archetype` is from the World the state was initialized from in `init_state`.
|
||||||
|
unsafe { GizmosState::<T>::new_archetype(&mut state.state, archetype, system_meta) };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
|
fn apply(state: &mut Self::State, system_meta: &SystemMeta, world: &mut World) {
|
||||||
GizmosState::<T>::apply(&mut state.state, system_meta, world);
|
GizmosState::<T>::apply(&mut state.state, system_meta, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn get_param<'w, 's>(
|
unsafe fn get_param<'w, 's>(
|
||||||
state: &'s mut Self::State,
|
state: &'s mut Self::State,
|
||||||
system_meta: &SystemMeta,
|
system_meta: &SystemMeta,
|
||||||
|
Loading…
Reference in New Issue
Block a user