Skip to content

Fix unsafe reclamation when dropping maps with a shared collector - #103

Open
jimezesinachi wants to merge 1 commit into
ibraheemdev:masterfrom
jimezesinachi:fix/shared-collector-drop
Open

jimezesinachi wants to merge 1 commit into
ibraheemdev:masterfrom
jimezesinachi:fix/shared-collector-drop

Conversation

@jimezesinachi

@jimezesinachi jimezesinachi commented Sep 16, 2026 •

Copy link
Copy Markdown

Summary

Fixes a use-after-free that can occur when multiple Papaya maps share the same Seize collector.

When a HashMap is dropped, Papaya currently calls Collector::reclaim_all() before destroying the map's table chain. This is safe when the map exclusively owns its collector, but not when the collector was supplied through HashMap::builder().shared_collector(...).

With a shared collector, another map may still have an active guard protecting retired objects. Calling reclaim_all() ignores that protection and immediately reclaims every pending object associated with the collector. References protected by those active guards can then point to freed memory.

The issue and standalone reproducer are documented in:

#102

Changes

The map drop path now distinguishes between exclusive and shared collector ownership.

Exclusively owned collector

When Arc::get_mut(&mut self.collector) succeeds, no other strong or weak references to the collector exist. The map can therefore preserve the existing behavior:

  1. Reclaim all pending retired objects.
  2. Destroy the map's table chain immediately.

This retains eager reclamation for ordinary maps that own their collector.

Shared collector

When the collector has other owners, the map no longer calls reclaim_all().

Instead, it:

  1. Enters the shared collector to obtain a guard.
  2. Retires the root of this map's table chain through that guard.
  3. Defers destruction of the complete table chain until the collector determines that no active guard can still reference it.

Only the table chain owned by the map being dropped is retired. Pending objects belonging to other maps using the same collector are left under the collector's normal reclamation rules.

The existing table-chain destruction logic has been extracted into drop_tables() so both ownership paths use the same cleanup implementation.

Regression Test

The new test recreates the failure using three maps backed by one shared collector:

  1. A protected map exposes a reference under an active guard.
  2. A growing map resizes and retires tables into the shared collector.
  3. A third map is dropped while the protected guard remains active.

The test verifies that dropping the third map does not reclaim the protected value. After the guard and remaining owners are dropped, it verifies that the value is reclaimed exactly once.

Validation

  • The standalone reproducer no longer observes premature destruction.
  • The new regression test passes.
  • Existing Papaya tests pass.
  • cargo check --all-targets passes.

Fixes #102

Signed-off-by: Jim Ezesinachi <ezesinachijim@gmail.com>
@jimezesinachi jimezesinachi changed the title Fix map drop with shared collectors Fix unsafe reclamation when dropping maps with a shared collector Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Dropping a map with a shared collector reclaims entries protected by another map’s guard

1 participant