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
103 changes: 103 additions & 0 deletions crates/beamr/RUSTSEC-0000-0000.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
```toml
[advisory]
id = "RUSTSEC-0000-0000"
package = "beamr"
date = "2026-07-23"
url = "https://github.com/ablative-io/beamr/blob/main/CHANGELOG.md"
categories = ["memory-corruption"]
keywords = ["use-after-free", "garbage-collection", "memory-corruption"]

[versions]
patched = [">= 0.16.3"]
```

# Three classes of silent memory corruption in the garbage collector, ETS, and binary BIFs

`beamr` is a BEAM virtual machine. Its collector is a per-process generational
**copying** collector: a minor collection physically relocates live young-heap
objects and then zero-fills the vacated region. The only roots are the X/Y
register file and the process's native-root slots. A `Term` held in a Rust
local, or a `&[u8]` borrowed out of a binary, is **not** a root and is **not**
forwarded.

Three classes of defect follow from that, all fixed by `0.16.3`:

1. **`as_bytes` borrow-across-allocation** (fixed in `0.16.3`). A helper
returned `&'static [u8]` borrowed from garbage-collected process-heap
memory. Consumers held that borrow across a call that could trigger a
collection and then read through it. Eleven real crossings were enumerated,
including nine BIF sites reachable from ordinary Erlang/Gleam string and
binary operations, `binary_to_term`, and a JIT binary-match helper.

2. **Garbage-collector refcount-release walk** (fixed in `0.16.2`). The walk
inferred an object's type from `word[0]` and could call `Arc::from_raw` on
a heap-cons payload — freeing memory that was never refcounted.

3. **ETS stored borrowed caller-heap terms** (fixed in `0.16.2`) that outlived
the heap they pointed into.

## Disclosure timeline

The `date` field above carries the **earliest** public disclosure of any class
covered here. The classes became public on two different days and are named
separately so the single field never has to stand for both:

- **Classes 2 and 3 — `2026-07-23`.** Described in full in the `0.16.2`
changelog entry, published to crates.io `2026-07-23T11:05:21Z`.
- **Class 1 — `2026-07-28`.** Described in the `0.16.3` changelog entry,
published to crates.io `2026-07-28T15:09:08Z`. (That entry heads itself
`2026-07-29`; the changelog dates in the maintainers' local zone, crates.io
in UTC. The UTC date is the one used here.)

No class was ever publicly described while unfixed: each disclosure shipped
with its own fix.

## Impact

**There is no error, no panic and no crash in any of these classes.** The
vacated heap region is zero-filled rather than poisoned, so the failure mode
is corrupted or freed data read as valid. A passing test suite is not evidence
of non-exposure.

Reaching class 1 requires no unsafe code and no unusual configuration on the
part of a user of the VM — the affected operations are ordinary string and
binary BIFs invoked by compiled Erlang/Gleam bytecode.

## Affected versions

**Upgrade to `0.16.3` or later.** `0.17.0` is the current release.

No lower bound is claimed, and that is deliberate rather than unknown-by-
omission. Class 1's mechanism was measured across the crate's tag history:
the file carrying the borrow helper and all five affected string BIFs is
**byte-identical (one blob hash) across all 29 tags from `0.4.4` through
`0.15.2`, and at the `0.16.2` release commit**. The helper's signature is
present as far back as `0.2.0`. The introduction points of classes 2 and 3
have **not** been measured; they are stated here as unmeasured rather than
assumed narrow. Treat every version below `0.16.3` as affected.

## A separate, still-open class — disclosed, not fixed

Independently of the three above, `beamr` publicly discloses a remaining set
of JIT-reachable rooting sites that is **not fixed in any released version,
including `0.17.0`**, and is reachable under the `jit` feature, which is on by
default. Contrary to what `beamr`'s own earlier release notes said, that
feature **cannot be disabled in any build that retains `threads`** — such a
build does not compile — so turning it off is not an available mitigation.

That class is deliberately **not** the subject of this advisory: it has no fix
to point at and no action a consumer could take in response. It is recorded
here so that upgrading to `0.16.3` or `0.17.0` is not mistaken for a clean
bill of health. See "Correction to the 0.16.2 and 0.16.3 advisories" and
"Known remaining JIT sites" in the project's `CHANGELOG.md`.

## References

- Advisory and full class detail:
<https://github.com/ablative-io/beamr/blob/main/CHANGELOG.md>
- Site-level audit of class 1, including the forward-only amendments —
document as of `4055cbe`; its **sweep base** is `f684d60`, which is where
the audit's line coordinates resolve, not `main`:
<https://github.com/ablative-io/beamr/blob/4055cbe/docs/design/beamr/briefs/evidence/aion-encode-gc-defect/asbytes-sweep/AUDIT.md>

*Filed by the crate maintainers.*