Skip to content

Fix #C-16: insert_by_ids accepts a documented-compliant short iterator - #25266

Open
kiana1kaslana wants to merge 1 commit into
bevyengine:mainfrom
kiana1kaslana:fix-insert-by-ids-soundness
Open

Fix #C-16: insert_by_ids accepts a documented-compliant short iterator#25266
kiana1kaslana wants to merge 1 commit into
bevyengine:mainfrom
kiana1kaslana:fix-insert-by-ids-soundness

Conversation

@kiana1kaslana

@kiana1kaslana kiana1kaslana commented Aug 2, 2026

Copy link
Copy Markdown

insert_by_ids is unsafe, but its Safety docs never said the iterator has to match the length of component_ids. The internal pairing uses zip, so a short iterator silently gets truncated while Table::allocate has already reserved all the columns. Reading them later is UB.

Three changes in crates/bevy_ecs/src/world/entity_access/:

  • Update the Safety docs on insert_by_ids to require an exact length match.
  • Update the Safety docs on insert_dynamic_bundle to say both iterators must match.
  • In insert_by_ids_internal, collect the iterator in debug builds and debug_assert_eq! its length against component_ids.len(). Gated on #[cfg(debug_assertions)], so release builds are unchanged.

Added two #[should_panic] tests in entity_access::mod.rs:

  • insert_by_ids_short_iterator_panics_in_debug (2 IDs, 1 component)
  • insert_by_ids_long_iterator_panics_in_debug (1 ID, 2 components)

All 71 existing entity_access tests still pass. cargo check -p bevy_ecs is clean in both debug and release.

The other two callers of insert_by_ids_internal (bundle::writer and entity::clone_entities) always pass matching lengths, so they're unaffected.

…tor length

`EntityWorldMut::insert_by_ids` is an `unsafe` API whose `# Safety` docs
did not state that `iter_components` must yield exactly
`component_ids.len()` items. Internally, `insert_dynamic_bundle` pairs
the two via `zip`, which silently truncates to the shorter iterator. If
the caller passes a short iterator, `Table::allocate` has already
reserved space for all components but only some are written — leaving
uninitialized memory that is later read as valid data (UB).

Changes:

1. **`insert_by_ids` Safety docs** — add the missing contract: the
   iterator must yield exactly `component_ids.len()` items. Fewer → UB
   from uninitialized memory; more → silently ignored.

2. **`insert_dynamic_bundle` Safety docs** — state that both iterators
   must yield the same number of items.

3. **`insert_by_ids_internal` debug guard** — in debug builds, eagerly
   collect `iter_components` into a `Vec` and `debug_assert_eq!` its
   length against `component_ids.len()`. This catches the mismatch
   before any uninitialized memory is accessed. The check is removed
   entirely in release builds (zero overhead).

4. **Regression tests** — two `#[should_panic]` tests (debug-only)
   verifying that both short and long iterators trigger the assertion.
@kiana1kaslana kiana1kaslana changed the title Fix C-16: insert_by_ids soundness — document and debug-assert itera… Fix #C-16: insert_by_ids accepts a documented-compliant short iterator Aug 2, 2026
@JaySpruce JaySpruce added C-Bug An unexpected or incorrect behavior C-Docs An addition or correction to our documentation A-ECS Entities, components, systems, and events P-Unsound A bug that results in undefined compiler behavior D-Unsafe Touches with unsafe code in some way S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Aug 6, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in ECS Aug 6, 2026
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-Bug An unexpected or incorrect behavior C-Docs An addition or correction to our documentation D-Unsafe Touches with unsafe code in some way P-Unsound A bug that results in undefined compiler behavior S-Needs-Review Needs reviewer attention (from anyone!) to move forward

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

2 participants