Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - unsafeify World::entities_mut #4093

Closed
wants to merge 1 commit into from

Conversation

BoxyUwU
Copy link
Member

@BoxyUwU BoxyUwU commented Mar 4, 2022

Objective

make bevy ecs a lil bit less unsound

Solution

make unsound API unsafe so that there is an unsafe block to blame:

use bevy_ecs::prelude::*;

#[derive(Debug, Component)]
struct Foo(u8);

fn main() {
    let mut world = World::new();
    let e1 = world.spawn().id();
    let e2 = world.spawn().insert(Foo(2)).id();
    world.entities_mut().meta[0] = world.entities_mut().meta[1].clone();
    let foo = world.entity(e1).get::<Foo>().unwrap();
    // whoo i love having components i dont have
    dbg!(foo);
}

This is not strictly speaking UB, however:

  • Query::get_multiple cannot work if this is allowed
  • bevy_ecs is a pile of unsafe code whose soundness generally depends on the world being in a "correct" state with "no funny business" so it seems best to disallow this
  • it is trivial to get bevy to panic inside of functions with safety invariants that have been violated (the entity location is not valid)
  • it seems to violate what the safety invariant on Entities::flush is trying to ensure

@BoxyUwU BoxyUwU added the A-ECS Entities, components, systems, and events label Mar 4, 2022
@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Mar 4, 2022
@IceSentry IceSentry removed the S-Needs-Triage This issue needs to be labelled label Mar 4, 2022
@BoxyUwU BoxyUwU force-pushed the remove_entities_mut branch 2 times, most recently from db0f44b to 52bdff0 Compare March 4, 2022 02:19
crates/bevy_ecs/src/world/mod.rs Outdated Show resolved Hide resolved
@@ -198,7 +198,7 @@ impl Plugin for RenderPlugin {
// flushing as "invalid" ensures that app world entities aren't added as "empty archetype" entities by default
// these entities cannot be accessed without spawning directly onto them
// this _only_ works as expected because clear_entities() is called at the end of every frame.
render_app.world.entities_mut().flush_as_invalid();
unsafe { render_app.world.entities_mut() }.flush_as_invalid();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to avoid this entirely?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure, I dont particularly understand what the render app is actually trying to accomplish here 😅

Copy link
Member

@mockersf mockersf Mar 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the render app "reserve" all the entities from the main app, but actually spawn something only on some of them. this marks all entities by default as invalid so that the one which won't be used aren't usable

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "right" way to do this would be to expose a safe mirror of flush_as_invalid() on World. But honestly this feature is niche enough that im fine leaving this as-is for now.

crates/bevy_ecs/src/entity/mod.rs Outdated Show resolved Hide resolved
@BoxyUwU BoxyUwU changed the title yeet World::entities_mut >:( yeet safe World::entities_mut >:( Mar 4, 2022
@alice-i-cecile alice-i-cecile added C-Bug An unexpected or incorrect behavior P-High This is particularly urgent, and deserves immediate attention labels Mar 4, 2022
@Guvante
Copy link
Contributor

Guvante commented Mar 17, 2022

Figuring out how to fix the flush_as_invalid use case seems valuable but the changes otherwise seem good.

@BoxyUwU BoxyUwU changed the title yeet safe World::entities_mut >:( unsafeify World::entities_mut Mar 22, 2022
Copy link
Member

@alice-i-cecile alice-i-cecile left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this change: this is very easy to create UB in safe code with, and so it's unsound if this is safe.

Entities is not very useful in general to the average user, and mutating it is particularly rare.

I really wish we would could avoid this being pub, but we can't, as it's needed in bevy_render.

@alice-i-cecile alice-i-cecile added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Mar 28, 2022
@alice-i-cecile alice-i-cecile added this to the Bevy 0.7 milestone Mar 30, 2022
@alice-i-cecile
Copy link
Member

Adding to the 0.7 milestone; this is now actually UB since #4298 is merged.

@cart
Copy link
Member

cart commented Mar 30, 2022

bors r+

bors bot pushed a commit that referenced this pull request Mar 30, 2022
# Objective
make bevy ecs a lil bit less unsound

## Solution
make unsound API unsafe so that there is an unsafe block to blame:

```rust
use bevy_ecs::prelude::*;

#[derive(Debug, Component)]
struct Foo(u8);

fn main() {
    let mut world = World::new();
    let e1 = world.spawn().id();
    let e2 = world.spawn().insert(Foo(2)).id();
    world.entities_mut().meta[0] = world.entities_mut().meta[1].clone();
    let foo = world.entity(e1).get::<Foo>().unwrap();
    // whoo i love having components i dont have
    dbg!(foo);
}
```

This is not _strictly_ speaking UB, however: 
- `Query::get_multiple` cannot work if this is allowed
- bevy_ecs is a pile of unsafe code whose soundness generally depends on the world being in a "correct" state with "no funny business" so it seems best to disallow this
- it is trivial to get bevy to panic inside of functions with safety invariants that have been violated (the entity location is not valid)
- it seems to violate what the safety invariant on `Entities::flush` is trying to ensure
@bors bors bot changed the title unsafeify World::entities_mut [Merged by Bors] - unsafeify World::entities_mut Mar 31, 2022
@bors bors bot closed this Mar 31, 2022
@james7132 james7132 added the C-Breaking-Change A breaking change to Bevy's public API that needs to be noted in a migration guide label Mar 31, 2022
aevyrie pushed a commit to aevyrie/bevy that referenced this pull request Jun 7, 2022
# Objective
make bevy ecs a lil bit less unsound

## Solution
make unsound API unsafe so that there is an unsafe block to blame:

```rust
use bevy_ecs::prelude::*;

#[derive(Debug, Component)]
struct Foo(u8);

fn main() {
    let mut world = World::new();
    let e1 = world.spawn().id();
    let e2 = world.spawn().insert(Foo(2)).id();
    world.entities_mut().meta[0] = world.entities_mut().meta[1].clone();
    let foo = world.entity(e1).get::<Foo>().unwrap();
    // whoo i love having components i dont have
    dbg!(foo);
}
```

This is not _strictly_ speaking UB, however: 
- `Query::get_multiple` cannot work if this is allowed
- bevy_ecs is a pile of unsafe code whose soundness generally depends on the world being in a "correct" state with "no funny business" so it seems best to disallow this
- it is trivial to get bevy to panic inside of functions with safety invariants that have been violated (the entity location is not valid)
- it seems to violate what the safety invariant on `Entities::flush` is trying to ensure
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective
make bevy ecs a lil bit less unsound

## Solution
make unsound API unsafe so that there is an unsafe block to blame:

```rust
use bevy_ecs::prelude::*;

#[derive(Debug, Component)]
struct Foo(u8);

fn main() {
    let mut world = World::new();
    let e1 = world.spawn().id();
    let e2 = world.spawn().insert(Foo(2)).id();
    world.entities_mut().meta[0] = world.entities_mut().meta[1].clone();
    let foo = world.entity(e1).get::<Foo>().unwrap();
    // whoo i love having components i dont have
    dbg!(foo);
}
```

This is not _strictly_ speaking UB, however: 
- `Query::get_multiple` cannot work if this is allowed
- bevy_ecs is a pile of unsafe code whose soundness generally depends on the world being in a "correct" state with "no funny business" so it seems best to disallow this
- it is trivial to get bevy to panic inside of functions with safety invariants that have been violated (the entity location is not valid)
- it seems to violate what the safety invariant on `Entities::flush` is trying to ensure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ECS Entities, components, systems, and events C-Breaking-Change A breaking change to Bevy's public API that needs to be noted in a migration guide C-Bug An unexpected or incorrect behavior P-High This is particularly urgent, and deserves immediate attention S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants