diff --git a/crates/hecs/RUSTSEC-0000-0000.md b/crates/hecs/RUSTSEC-0000-0000.md new file mode 100644 index 0000000000..a069f1b4c9 --- /dev/null +++ b/crates/hecs/RUSTSEC-0000-0000.md @@ -0,0 +1,41 @@ +```toml +[advisory] +id = "RUSTSEC-0000-0000" +package = "hecs" +date = "2026-10-08" +url = "https://github.com/Ralith/hecs/pull/471" +categories = ["memory-corruption"] +keywords = ["memory-safety", "double-free", "panic-safety"] + +[affected.functions] +"hecs::World::clear" = ["< 0.11.2"] +"hecs::World::despawn" = ["< 0.11.2"] +"hecs::World::spawn_at" = ["< 0.11.2"] +"hecs::World::spawn_column_batch_at" = ["< 0.11.2"] +"hecs::CommandBuffer::run_on" = ["< 0.11.2"] + +[versions] +patched = [">= 0.11.2"] +``` + +# Double free when a component's `Drop` panics + +Several `hecs` operations run component destructors before committing the +bookkeeping that records which slots hold live components. If a destructor +unwinds, that commit is skipped and the already-dropped slots are still treated +as live, so a later `Drop for Archetype` or a subsequent operation destroys them +a second time — a double free (CWE-415) reachable from safe Rust. Storing a +component whose `Drop` panics is enough. + +- `Archetype::clear` does not reset the length before running destructors, and + `Archetype::remove` does not commit the length before the move. Reached from + `World::clear`, `World::despawn`, `World::spawn_at` and + `World::spawn_column_batch_at`. +- `CommandBuffer::run_on` skips `components.clear()` on unwind, so the buffer + drops components the `World` now owns. + +`Archetype::remove` also copies the last component into the removed slot before +committing, so an unwind leaves the same value live in two slots and both are +dropped. + +Confirmed with AddressSanitizer on 0.11.1.