Skip to content

Unsound to call EntityRef::get_components with a QueryData that performs resource access #20315

@chescock

Description

@chescock

What problem does this solve or what need does it fill?

There is an unsound interaction between EntityRef::get_components #13375 and declaring resource access from queries #16843 where safe code can get read access to a resource without checking for conflicts. If a QueryData requests read access to a resource, then a system like

fn system(query: Query<EntityRef>, res: ResMut<SomeResource>) {
  query.single().get_components::<QueryDataUsingSomeResource>();
}

will create a &SomeResource during get_components() at the same time as the &mut SomeResource from ResMut, resulting in UB.

The bad SAFETY comment is in EntityRef::get_components:

// - We have read-only access to all components of this entity.

While we do have access to all components, the query may also require resource access that EntityRef does not have.

What solution would you like?

Create a new unsafe trait for a WorldQuery that does no resource access, and bound the get_components methods by that trait.

Additional context

This is difficult to actually trigger at the moment, so may not be urgent to fix. It requires a custom QueryData impl, as the only in-engine use of a WorldQuery requesting resource access is AssetChanged<A>, a QueryFilter.

If we start expanding the cases where queries access data outside of the current entity, particularly for relations, then this may become more important.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ECSEntities, components, systems, and eventsC-BugAn unexpected or incorrect behaviorP-UnsoundA bug that results in undefined compiler behaviorS-Ready-For-ImplementationThis issue is ready for an implementation PR. Go for it!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions