Tests from overnight audit#64
Open
xarantolus wants to merge 13 commits into
Open
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds regression tests covering several panic/UB scenarios found during an audit, primarily around allocator exhaustion semantics and out-of-bounds mutable access.
Changes:
- Added tests for
BitAlloc::allocallocating an exact multiple ofBITS_PER_WORD(full-word allocations). - Added tests for
Vec::at2_mut/at3_mut,IndexMap::get2_mut/get3_mut, andBitReclaimMap::insert_witherror-path behavior. - Added a test demonstrating
FixedPoolcan panic whenWORDS*BITS_PER_WORD > N.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/types/pool.rs |
Adds a test that expects FixedPool::alloc to return None (not panic) when indices exceed N. |
src/types/bitset.rs |
Adds tests for full-capacity allocations that currently hit an OOB access in allocation marking. |
src/types/array.rs |
Adds tests demonstrating OOB/UB in multi-mutable access helpers and a leak in BitReclaimMap::insert_with. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
10d82e7 to
0b96b58
Compare
Tests added (all currently fail on the unfixed code): - BitAlloc::alloc(N*BITS_PER_WORD) panics on word-aligned full allocation (alloc_full_two_words, alloc_full_three_words). Reachable from PFA when allocating all available pages. - Vec::at2_mut / at3_mut return Some(&mut <uninit>) for out-of-bounds indices (UB) despite the doc-comment promising None for OOB. - IndexMap::get2_mut / get3_mut panic on OOB indices via split_at_mut / array indexing, inconsistent with the rest of Get/GetMut returning None. - BitReclaimMap::insert_with leaks BitAlloc bits on closure error. The closure is user-supplied; sustained errors exhaust the allocator. No production code is changed; this commit only adds failing tests that serve as regression reproducers for the issues. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
FixedPool<T, N, WORDS> exposes N and WORDS as independent const generics, but BitAlloc<WORDS> tracks WORDS*BITS_PER_WORD bits while blocks has only N slots. Any user picking WORDS*BITS_PER_WORD > N hits an OOB panic on the (N+1)-th alloc from `self.blocks[idx]` indexing past the end — violating the allocator's "None on exhaustion" contract. Currently no in-tree caller instantiates FixedPool, but the bug is real for any future user. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirror the at2_mut format-string pattern: when at3_mut returns Some(&mut <uninit>) for an out-of-bounds index, the failure message now prints the garbage value, making the UB obvious. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- BitAlloc::alloc: guard last-word write against `len % BITS_PER_WORD == 0` (run ends on a word boundary, no trailing partial word). - Vec::at2_mut / at3_mut: bounds-check each index against self.len before constructing &mut T; previously returned references into uninitialized memory for out-of-bounds indices. - IndexMap::get2_mut / get3_mut: return None for out-of-bounds indices instead of panicking inside split_at_mut / array indexing. - BitReclaimMap::insert_with: release the BitAlloc bit when the user-supplied closure returns an error. - FixedPool::alloc: bounds-check the BitAlloc-returned index against N before indexing blocks; the const generics WORDS and N are independent and BitAlloc tracks WORDS * BITS_PER_WORD bits. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Use `&mut self.data[idx] as *mut Option<V>` (released into a raw pointer) instead of as_mut_ptr() + add(); now that bounds-checks gate the indexing, the original pattern is cleaner and avoids unsafe pointer arithmetic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When raw_insert rejects an idx that BitAlloc handed out (idx >= N because BitAlloc<N> tracks N*BITS_PER_WORD bits while IndexMap<N> has only N slots), release the bit so it can't accumulate. Mirrors the insert_with fix. The new test probes BitAlloc directly: after a failed insert it expects the next free bit to be 2 (the index that raw_insert rejected), confirming the bit was returned to the allocator. Verified to fail before the fix (BitAlloc returns Some(3), proving bit 2 is leaked). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When length > N, the original code wrote N inline clones and only then attempted to allocate the extra Box. If the allocation failed, vec.len was still 0 → Drop called clear() which dropped nothing → the N clones leaked. Reorder so the Box allocation happens first (no inline clones written on OOM), and bump vec.len after each individual write so a panicking T::clone leaves Drop with a consistent state. Verified via the new clones==drops invariant test (fails before fix with clones=2, drops=1; passes after). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the `// -------- ... --------` group banners; test names already describe what's being tested. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per `audit-memory-report.md`: - BestFitAllocator: double-free creates free-list cycle (BLOCKER) - PFA bitset: free() accepts sub-begin addresses (silent corruption) - Region::contains: returns true for unplaced regions - no-MMU unmap was a no-op (per-task BestFit leak, BLOCKER) - no-MMU AddressSpace had no Drop (PFA pages leaked forever, BLOCKER) - no-MMU virt/phys translation accepted out-of-range addresses - HAL cortex-m ArmStack: byte/word unit confusion in bounds checks 12 new tests added (78 passing total, up from 66 on this branch). 5 Kani proofs validated locally, reverted before commit per cycle convention. The BitReclaimMap insert_with bit-leak fix from the prior cycle on this branch is load-bearing for thread-memory correctness — flagged in the report. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three sites still referenced the pre-POSIX error names after the rebase onto current main: - src/mem/vmm/nommu.rs::unmap: InvalidArgument -> EINVAL - src/types/array.rs test: OutOfMemory -> ENOMEM - machine/cortex-m/src/native/sched.rs::Stack::new: Error::default() -> PosixError::EINVAL Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c6d980b to
e0e5a9b
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.
This adds a couple of tests that result in panics / undefined behaviour, which were found using Claude.
alloc_full_two_wordsbitset.rsBitAlloc::alloc(N*BITS_PER_WORD)panics — reachable from PFAalloc_full_three_wordsbitset.rsat2_mut_out_of_bounds_returns_nonearray.rsVec::at2_mutUB: returns&mut Tto uninit memory for OOBat3_mut_out_of_bounds_returns_nonearray.rsat3_mutindexmap_get2_mut_oob_does_not_panicarray.rsIndexMap::get2_mutpanics on OOB, inconsistent with the rest ofGet/GetMutindexmap_get3_mut_oob_does_not_panicarray.rsget3_mutbitreclaim_insert_with_failed_closure_does_not_leakarray.rsfixed_pool_alloc_beyond_n_returns_nonepool.rsFixedPoolOOB panic whenWORDS*BITS_PER_WORD > N