fix: phase lifecycle safety bugs (nested phase + realloc UB)#10
Open
fix: phase lifecycle safety bugs (nested phase + realloc UB)#10
Conversation
…o prevent UB on aliased dst
b4f5280 to
23004b5
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two phase lifecycle bugs found by automated bug hunter (boundary-value testing + hypothesis-driven code audit). Both are memory-safety issues in the arena's phase management — one causes silent data corruption, the other is undefined behavior per the Rust spec.
Depends on #9 (size-routing infrastructure provides
PHASE_DEPTHcounter andPhaseGuard).Bug 1: Nested
begin_phase()Silently Corrupts Outer Phase Data (high)Root cause:
begin_phase()unconditionally bumpedGENERATIONand recycled every thread's slab. When called inside an already-active phase, the innerbegin_phase()reset the slab — silently overwriting all bump-allocated data from the outer phase.Characterization:
0xAAbegin_phase()+ allocation overwrites the slab from offset 0test_phase_guard::nested_phase_guards_composemissed this because it allocated nothing in the outer phaseWhy it matters: Today leanMultisig calls
begin_phase()/end_phase()at the top-level prove boundary, so nesting doesn't occur. But the API permits it, and nothing warns the caller. As zk-alloc integrates deeper — library wrappers,PhaseGuardin helper functions, test scaffolding — a nested call silently corrupts with no diagnostic. A public allocator API that silently destroys live data on a legal call sequence is a latent safety hole.Fix:
PHASE_DEPTHatomic counter.begin_phase()increments depth; only the outermost transition (0 → 1) bumpsGENERATIONand activates the arena.end_phase()decrements; only the outermost transition (1 → 0) deactivates. Nested calls compose safely as no-ops.Bug 2:
reallocUsescopy_nonoverlappingon Potentially Aliased Memory (medium)Root cause:
GlobalAlloc::realloccalledptr::copy_nonoverlapping(old_ptr, new_ptr, ...)to move data during growth. When growing within the same slab (bump pointer advanced past old allocation, then old allocation is reallocated to a larger size at the same base),old_ptrandnew_ptrcan alias — the new allocation overlaps the source.copy_nonoverlappingis UB on overlapping regions per the Rust spec.Characterization:
0xBBreallocto 256 bytes — new pointer returned at same base address (bump allocator reuses position)copy_nonoverlappingwith overlapping src/dst — UB, Miri would flagFix: Replace
ptr::copy_nonoverlappingwithptr::copy(memmove semantics). Handles overlapping regions correctly. Zero performance impact — realloc is not on the hot path.Findings Summary
begin_phaserecycles outer slabcopy_nonoverlappingon aliased src/dstARENA_ACTIVEOVERFLOW_COUNTskipped on first no-slab allocOVERFLOW_COUNTcounts failed OOM as overflowValidation
cargo test --release— all tests pass including 2 new regression testsend_phase()calls are no-ops (saturate at zero depth)Test plan
cargo test --release— all existing + new tests passcargo +nightly miri test— verify hunt-2 fix eliminates UB under MiriGenerated with Claude Code