Skip to content

gc: DATA_VIEW_REGISTRY and SHARED_ARRAY_BUFFER_REGISTRY are never pruned on buffer death (leak + ABA address-reuse, same class as #6302) #6337

Description

@proggeramlug

Summary

finalize_collected_dead_buffer (crates/perry-runtime/src/buffer/header.rs:483) exists specifically to drop "every registry/side-table entry keyed by a dead buffer's address" — its own doc comment cites preventing "the #6080 ABA class". But it only prunes three tables:

  • BUFFER_REGISTRY
  • ARRAY_BUFFER_REGISTRY
  • BUFFER_AB_ALIAS

Two sibling address-keyed registries are never pruned — anywhere:

  • DATA_VIEW_REGISTRY
  • SHARED_ARRAY_BUFFER_REGISTRY

Verified on origin/main: git grep 'DATA_VIEW_REGISTRY\|SHARED_ARRAY_BUFFER_REGISTRY' has zero .remove / .retain / prune sites.

Why it matters

Two consequences, the same pair already proven for the WebCrypto tables:

  1. Unbounded leak — one permanent entry per DataView / SharedArrayBuffer ever created, for the life of the process.
  2. ABA / address reusearena_reset_empty_blocks resets a fully-empty block's offset to 0 while keeping its base pointer, so a reset block re-issues the same addresses. A stale entry keyed on a reused address then answers for a completely unrelated object.

Precedent

Exactly this was just found and fixed for the WebCrypto/KeyObject tables in #6302 / PR #6310: a probe over 60k subtle.importKey HMAC keys with all JS references dropped showed 59,998 of 60,000 metadata entries retained after the buffers were swept. CRYPTO_KEY_META_REGISTRY, SECRET_KEY_REGISTRY, UINT8ARRAY_FROM_CTOR, three process-global external_* maps, and perry-stdlib's CRYPTO_KEY_REGISTRY had all been left off the finalizer. They are now added.

DATA_VIEW_REGISTRY and SHARED_ARRAY_BUFFER_REGISTRY are the remaining members of that class.

Note on pinning

Do not "fix" this by pinning the backing buffers: GC_FLAG_PINNED objects are not traced (gc/cycle.rs only enqueues when flags & (MARKED|PINNED) == 0), so pinning trades a stale entry for an untraced-reference walk and makes the leak permanent by design. Prune in the sweep, as #6310 does.

Suggested fix

Add both tables to finalize_collected_dead_buffer, and add a leak regression test in the shape of the one added by PR #6310 (allocate N, drop refs, force a full collection, assert the registry drains).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions