Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions crates/hecs/RUSTSEC-0000-0000.md
Original file line number Diff line number Diff line change
@@ -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.
Loading