Shorten the 'world lifetime returned from QueryLens::query(). (#17694)
# Objective Fix unsoundness introduced by #15858. `QueryLens::query()` would hand out a `Query` with the full `'w` lifetime, and the new `_inner` methods would let the results outlive the `Query`. This could be used to create aliasing mutable references, like ```rust fn bad<'w>(mut lens: QueryLens<'w, EntityMut>, entity: Entity) { let one: EntityMut<'w> = lens.query().get_inner(entity).unwrap(); let two: EntityMut<'w> = lens.query().get_inner(entity).unwrap(); assert!(one.entity() == two.entity()); } ``` Fixes #17693 ## Solution Restrict the `'world` lifetime in the `Query` returned by `QueryLens::query()` to `'_`, the lifetime of the borrow of the `QueryLens`. The model here is that `Query<'w, 's, D, F>` and `QueryLens<'w, D, F>` have permission to access their components for the lifetime `'w`. So going from `&'a mut QueryLens<'w>` to `Query<'w, 'a>` would borrow the permission only for the `'a` lifetime, but incorrectly give it out for the full `'w` lifetime. To handle any cases where users were calling `get_inner()` or `iter_inner()` on the `Query` and expecting the full `'w` lifetime, we introduce a new `QueryLens::query_inner()` method. This is only valid for `ReadOnlyQueryData`, so it may safely hand out a copy of the permission for the full `'w` lifetime. Since `get_inner()` and `iter_inner()` were only valid on `ReadOnlyQueryData` prior to #15858, that should cover any uses that relied on the longer lifetime. ## Migration Guide Users of `QueryLens::query()` who were calling `get_inner()` or `iter_inner()` will need to replace the call with `QueryLens::query_inner()`.
This commit is contained in:
parent
5ff7062c1c
commit
62c1812e72
@ -0,0 +1,24 @@
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::system::SystemState;
|
||||
|
||||
#[derive(Component, Eq, PartialEq, Debug)]
|
||||
struct Foo(u32);
|
||||
|
||||
fn main() {
|
||||
let mut world = World::default();
|
||||
let e = world.spawn(Foo(10_u32)).id();
|
||||
|
||||
let mut system_state = SystemState::<Query<&mut Foo>>::new(&mut world);
|
||||
{
|
||||
let mut query = system_state.get_mut(&mut world);
|
||||
let mut lens = query.as_query_lens();
|
||||
dbg!("hi");
|
||||
{
|
||||
let mut data: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
||||
let mut data2: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
||||
//~^ E0499
|
||||
assert_eq!(&mut *data, &mut *data2); // oops UB
|
||||
}
|
||||
dbg!("bye");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
error[E0499]: cannot borrow `lens` as mutable more than once at a time
|
||||
--> tests/ui\query_lens_lifetime_safety.rs:18:39
|
||||
|
|
||||
17 | let mut data: Mut<Foo> = lens.query().get_inner(e).unwrap();
|
||||
| ---- first mutable borrow occurs here
|
||||
@ -2127,7 +2127,21 @@ pub struct QueryLens<'w, Q: QueryData, F: QueryFilter = ()> {
|
||||
|
||||
impl<'w, Q: QueryData, F: QueryFilter> QueryLens<'w, Q, F> {
|
||||
/// Create a [`Query`] from the underlying [`QueryState`].
|
||||
pub fn query(&mut self) -> Query<'w, '_, Q, F> {
|
||||
pub fn query(&mut self) -> Query<'_, '_, Q, F> {
|
||||
Query {
|
||||
world: self.world,
|
||||
state: &self.state,
|
||||
last_run: self.last_run,
|
||||
this_run: self.this_run,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'w, Q: ReadOnlyQueryData, F: QueryFilter> QueryLens<'w, Q, F> {
|
||||
/// Create a [`Query`] from the underlying [`QueryState`].
|
||||
/// This returns results with the actual "inner" world lifetime,
|
||||
/// so it may only be used with read-only queries to prevent mutable aliasing.
|
||||
pub fn query_inner(&self) -> Query<'w, '_, Q, F> {
|
||||
Query {
|
||||
world: self.world,
|
||||
state: &self.state,
|
||||
@ -2138,9 +2152,9 @@ impl<'w, Q: QueryData, F: QueryFilter> QueryLens<'w, Q, F> {
|
||||
}
|
||||
|
||||
impl<'w, 's, Q: QueryData, F: QueryFilter> From<&'s mut QueryLens<'w, Q, F>>
|
||||
for Query<'w, 's, Q, F>
|
||||
for Query<'s, 's, Q, F>
|
||||
{
|
||||
fn from(value: &'s mut QueryLens<'w, Q, F>) -> Query<'w, 's, Q, F> {
|
||||
fn from(value: &'s mut QueryLens<'w, Q, F>) -> Query<'s, 's, Q, F> {
|
||||
value.query()
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user