Skip to content

fix(multitude): tighten allocator safety proofs and docs#443

Open
geeknoid wants to merge 1 commit into
mainfrom
multitude
Open

fix(multitude): tighten allocator safety proofs and docs#443
geeknoid wants to merge 1 commit into
mainfrom
multitude

Conversation

@geeknoid
Copy link
Copy Markdown
Member

  • arena/inner_value.rs (impl_alloc_inner_arc_with, impl_alloc_inner_value, impl_alloc_inner_with): replace the u16::try_from(...).expect("...MAX_CHUNK_BYTES <= u16::MAX...") panic surfaces with debug_assert! + assert_unchecked + unwrap_unchecked. The previous bound was wrong (MAX_CHUNK_BYTES = 65536 > u16::MAX); the real bound is local_max_bump_extent::<A>() = CHUNK_ALIGN − header_size.
  • arena/inner_slice.rs: apply the same pattern to the two oversized-slice sites where the u16::try_from followed core::mem::forget(init_guard) — a panic there would have freed the chunk backing with live un-dropped elements.
  • arena/refill.rs, arena/mod.rs::reset: replace mirror_dc.max(chunk_dc) with mirror_dc. The mirror is the source of truth; the .max() silently masked the dangerous divergence direction (chunk_dc > mirror_dc would make replay_drops walk past the last written entry).
  • arena/alloc_unsized.rs: bare +saturating_add in the oversized DST helpers needed computation.
  • internal/chunk_provider.rs::reserve_budget: replace load(Acquire) + check + fetch_add(Relaxed) with speculative fetch_add(AcqRel) + check + fetch_sub(Release) on overflow, removing the TOCTOU window with cross-thread release_budget and rejecting on usize wraparound.
  • internal/drop_list.rs, local_chunk.rs, shared_chunk.rs: add an AbortOnUnwind guard so the no_std replay_drops shim call force-aborts on a panicking T::Drop instead of unwinding past route_release and leaking the chunk.
  • bytesbuf.rs::ArenaBlockState::clone: add the check_shared_refcount-style overflow guard, making the abort-on-overflow convention uniform across every refcounted type in the crate.
  • bytesbuf.rs::reserve: add a # Panics doc section for the u32::MAX (4 GiB) per-reservation cap.
  • arena_builder.rs, internal/chunk_provider.rs, internal/constants.rs, arena/primitives.rs, arena/inner_slice.rs, arena/alloc_unsized.rs, docs/DESIGN.md: rewrite the max_normal_alloc doc/comment block — the threshold gates chunk acquisition in the slow path, not the bump probe. Requests above the threshold that fit in the current chunks tail are satisfied directly.
  • vec/mutate.rs::inc_len: minor wording fix in the #[mutants::skip] rationale.

Copilot AI review requested due to automatic review settings May 22, 2026 18:05
@geeknoid geeknoid requested a review from ralfbiedert May 22, 2026 18:07
@geeknoid geeknoid enabled auto-merge (squash) May 22, 2026 18:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens multitude’s arena allocator safety invariants by removing panic surfaces in places where unwinding would corrupt drop-list/chunk reclamation, tightening drop-count reconciliation, and clarifying the semantics of max_normal_alloc across docs and comments.

Changes:

  • Replace u16::try_from(...).expect(...) panic surfaces in allocator hot/slow paths with debug_assert! + assert_unchecked/unwrap_unchecked where proofs guarantee the bounds.
  • Make no-std drop replay robust against panicking T::Drop by introducing an abort-on-unwind guard; fix drop-count persistence to avoid walking past the last valid drop entry.
  • Improve operational correctness and documentation: close a byte-budget TOCTOU window, use saturating_add in oversized DST size computations, and update docs/comments around max_normal_alloc and panic behavior.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
crates/multitude/src/vec/mutate.rs Updates mutation-testing rationale wording for inc_len.
crates/multitude/src/internal/shared_chunk.rs Adds no-std abort-on-unwind guard around drop shims and clarifies behavior in comments.
crates/multitude/src/internal/local_chunk.rs Mirrors shared chunk’s no-std abort-on-unwind handling for drop replay.
crates/multitude/src/internal/drop_list.rs Introduces AbortOnUnwind guard (no-std) to force abort on unwinding T::Drop during replay_drops.
crates/multitude/src/internal/constants.rs Clarifies meaning of MAX_NORMAL_ALLOC as a chunk-acquisition threshold.
crates/multitude/src/internal/chunk_provider.rs Refactors reserve_budget to speculative fetch_add with rollback; expands max_normal_alloc semantics docs.
crates/multitude/src/bytesbuf.rs Documents reserve panics, adds refcount overflow guard to ArenaBlockState::clone.
crates/multitude/src/arena/refill.rs Writes mirror_dc as authoritative drop-count value to avoid unsafe over-replay.
crates/multitude/src/arena/primitives.rs Clarifies why oversized routing is slow-path only (chunk acquisition vs bump probe).
crates/multitude/src/arena/mod.rs Applies authoritative mirror_dc drop-count write during reset.
crates/multitude/src/arena/inner_value.rs Removes expect panic surfaces for u16 offsets, replacing with asserted unchecked conversions.
crates/multitude/src/arena/inner_slice.rs Removes post-forget(init_guard) panic surfaces for u16 conversions; expands max_normal_alloc routing docs.
crates/multitude/src/arena/alloc_unsized.rs Uses saturating_add for oversized DST needed computations; removes panic surfaces for u16 conversions.
crates/multitude/src/arena_builder.rs Updates builder docs to correctly describe max_normal_alloc behavior (acquisition-time).
crates/multitude/docs/DESIGN.md Updates design documentation for correct max_normal_alloc semantics.
Comments suppressed due to low confidence (1)

crates/multitude/src/internal/chunk_provider.rs:170

  • release_budget's comment still refers to a "reserve_budget Acquire load", but reserve_budget now uses fetch_add(..., AcqRel) and no longer does an Acquire load. Please update this comment to match the new synchronization story so it doesn't mislead future changes.
    }

    /// Release `bytes` from the lifetime byte budget tracker.
    fn release_budget(&self, bytes: usize) {
        // Release so a subsequent owner-thread `reserve_budget`

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/multitude/docs/DESIGN.md Outdated
Comment thread crates/multitude/src/bytesbuf.rs Outdated
Comment thread crates/multitude/src/bytesbuf.rs Outdated
@geeknoid
Copy link
Copy Markdown
Member Author

Also updated the stale comment in release_budget (called out in the suppressed-confidence note) so it references the new fetch_add(AcqRel) reservation path instead of the old Acquire load.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.0%. Comparing base (460b7b8) to head (da00a19).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #443   +/-   ##
=======================================
  Coverage   100.0%   100.0%           
=======================================
  Files         299      299           
  Lines       23562    23585   +23     
=======================================
+ Hits        23562    23585   +23     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI review requested due to automatic review settings May 22, 2026 19:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.

* `arena/inner_value.rs` (`impl_alloc_inner_arc_with`,
  `impl_alloc_inner_value`, `impl_alloc_inner_with`): replace the
  `u16::try_from(...).expect("...MAX_CHUNK_BYTES <= u16::MAX...")`
  panic surfaces with `debug_assert! + assert_unchecked +
  unwrap_unchecked`. The previous bound was wrong (`MAX_CHUNK_BYTES =
  65536 > u16::MAX`); the real bound is
  `local_max_bump_extent::<A>() = CHUNK_ALIGN − header_size`.
* `arena/inner_slice.rs`: apply the same pattern to the two
  oversized-slice sites where the `u16::try_from` followed
  `core::mem::forget(init_guard)` — a panic there would have
  freed the chunk backing with live un-dropped elements.
* `arena/refill.rs`, `arena/mod.rs::reset`: replace
  `mirror_dc.max(chunk_dc)` with `mirror_dc`. The mirror is the
  source of truth; the `.max()` silently masked the dangerous
  divergence direction (`chunk_dc > mirror_dc` would make
  `replay_drops` walk past the last written entry).
* `arena/alloc_unsized.rs`: bare `+` → `saturating_add` in the
  oversized DST helpers `needed` computation.
* `internal/chunk_provider.rs::reserve_budget`: replace
  `load(Acquire) + check + fetch_add(Relaxed)` with speculative
  `fetch_add(AcqRel) + check + fetch_sub(Release) on overflow`,
  removing the TOCTOU window with cross-thread `release_budget`
  and rejecting on `usize` wraparound.
* `internal/drop_list.rs`, `local_chunk.rs`, `shared_chunk.rs`:
  add an `AbortOnUnwind` guard so the `no_std` `replay_drops`
  shim call force-aborts on a panicking `T::Drop` instead of
  unwinding past `route_release` and leaking the chunk.
* `bytesbuf.rs::ArenaBlockState::clone`: add the
  `check_shared_refcount`-style overflow guard, making the
  abort-on-overflow convention uniform across every refcounted
  type in the crate.
* `bytesbuf.rs::reserve`: add a `# Panics` doc section for the
  `u32::MAX` (`4 GiB`) per-reservation cap.
* `arena_builder.rs`, `internal/chunk_provider.rs`,
  `internal/constants.rs`, `arena/primitives.rs`,
  `arena/inner_slice.rs`, `arena/alloc_unsized.rs`,
  `docs/DESIGN.md`: rewrite the `max_normal_alloc` doc/comment
  block — the threshold gates *chunk acquisition* in the slow
  path, not the bump probe. Requests above the threshold that fit
  in the current chunks tail are satisfied directly.
* `vec/mutate.rs::inc_len`: minor wording fix in the
  `#[mutants::skip]` rationale.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

2 participants