Repository navigation
Conversation
feliperodri
left a comment
There was a problem hiding this comment.
Thanks @v3risec. Reviewed alongside the other Challenge 16 solutions with our vacuity tooling + local Kani. Requesting changes — there's a soundness defect plus the criteria aren't met:
- Critical:
zip::SpecFold::spec_foldis body-swapped undercfg(kani). The realloop { … }is gated#[cfg(not(kani))]and a#[cfg(kani)]block replaces it with a single-arbitrary-iteration stub (that even introduces newunwrap_unchecked). Soharness_zip_spec_fold_*verifies the stub, not the shipping function — the proof is vacuous w.r.t. the real code's UB-freedom. Please prove the real loop via a loop contract (as you do elsewhere in this PR) instead of swapping the body. - Generic-
Tnot met — all harnesses are macro-monomorphized over a concrete type list. The challenge requires genericT, no monomorphization. - Bounded targets —
copied::spec_next_chunk,filter::next_chunk_dropless,filter_map::next_chunkare limited to fixed ≤4–5-element arrays; the challenge requires arbitrary length. - The added
#[requires]/#[modifies]are unenforced (noproof_for_contract; preconditions only mirrored viakani::assume).
The loop-contract work on take/zip and array_chunks is otherwise sound (real inductive invariants, no assume-the-conclusion). Between the three, we're prioritizing #549; fixing the spec_fold body-swap and the generic/unbounded gaps here would make this competitive.
|
Thanks for the detailed review, @feliperodri. I've pushed an update to address the feedback. Some issues have been fixed, while others remain limited by the current Kani capabilities. Here is the current status. 1.
|
| Target | Current coverage | Remaining limitation |
|---|---|---|
Copied::spec_next_chunk |
Non-ZST inputs use a symbolic slice backed by [T; 5], with N = 4. The ZST () case uses a genuinely unbounded symbolic length. |
Constructing an arbitrary-length valid non-ZST slice in the current core harness remains a memory-modeling limitation. |
Filter::next_chunk_dropless |
Four symbolic input elements, with nondeterministic predicate results. | The traversal occurs through the generic try_for_each implementation, where the adapter cannot currently attach a loop contract. |
FilterMap::next_chunk |
Four symbolic input elements, with nondeterministic Some/None results. |
The same try_for_each loop-contract limitation applies. |
Zip::spec_fold |
Real production implementation, input lengths <= 4, unwind(8). |
An unbounded inductive proof remains blocked by the method-call-in-invariant issue described above. |
The Filter and FilterMap limitation is tracked in kani#4893. For now, I have kept the bounded harnesses rather than replacing the real implementations with verification-specific models.
For MapWindows, the harnesses construct symbolic Buffer states with any valid start in 0..=N and arbitrary active-window contents. This avoids bounding the number of previous window advances. However, the proofs still instantiate representative concrete window sizes (N = 1, 2, 4), rather than proving every possible const-generic N.
4. Generic T remains unresolved
The harnesses still instantiate representative concrete types through macros, including signed and unsigned integers, bool, (), and fixed-size arrays.
Kani's verification operates on monomorphized Rust programs, so these proofs do not establish the challenge's literal requirement of a single proof for arbitrary generic T. I consider this an outstanding limitation rather than a completed requirement.
5. Upstream change
Following the upstream library subtree update in #687 (nightly-2026-09-25), the adapter-specific Cloned::next_unchecked and Map::next_unchecked implementations, along with the active UncheckedIterator trait integration, have been removed. I therefore removed their obsolete harnesses. The separate __iterator_get_unchecked implementations remain and are still covered.
Current status
The most important correction is that the real **Zip::spec_fold** implementation is now verified directly within a stated bound, with no cfg(kani) body replacement.
Several other targets use symbolic iterator lengths without an input-length bound, while the targets listed above remain bounded for the documented reasons. These changes improve the soundness and scope of the verification.
|
The The harnesses still use unbounded symbolic input lengths and loop contracts, rather than bounded unwinding. I am keeping this approach for now and will revisit the verification once the Kani issue is fixed. Therefore, these harnesses are currently blocked by the tool limitation, not successfully verified in CI. |
Summary
This PR adds Kani-based verification for Challenge 16, covering the iterator safety targets in
library/core/src/iter/adapters.The change introduces:
array_chunks.rs,skip.rs,take.rs, andzip.rsbool, zero-sized types, and arraysAll verification-only modules and annotations are gated by
cfg(kani). Normal non-Kani builds retain their existing runtime behavior.Challenge Coverage
Unsafe functions
The following unsafe functions receive safety contracts and proof coverage:
Cloned::__iterator_get_uncheckedCloned::next_unchecked(deleted)Copied::__iterator_get_uncheckedEnumerate::__iterator_get_uncheckedFuse::__iterator_get_uncheckedMap::__iterator_get_uncheckedMap::next_unchecked(deleted)Skip::__iterator_get_uncheckedZip::__iterator_get_uncheckedZipImpl::get_uncheckedThe challenge refers to
clone.rs; the corresponding file in the current source tree iscloned.rs.Because Kani's contract harness machinery cannot resolve several specialized trait-method paths, the proofs call those implementations through explicit trait paths and establish their preconditions with
kani::assume.Safe functions
Proof harnesses cover all safe abstractions listed by the challenge:
ArrayChunks::next_back_remainderArrayChunks::foldCopied::spec_next_chunkFilter::next_chunk_droplessFilterMap::next_chunkBuffer::as_array_refBuffer::as_uninit_array_mutBuffer::pushBuffer::dropStepBy::original_stepTake::spec_foldTake::spec_for_eachZip::foldZip::nextZip::nthZip::next_backZip::spec_foldVerification Approach
The proofs use a combination of:
Symbolic iterator states
Random-access adapter proofs use symbolic lengths, indices, pending skip counts, zip offsets, and payload values. Lazy ranges and mapped ranges allow many of these proofs to avoid imposing a harness-level length bound.
Safety contracts
The unsafe functions require the requested element to be within the trusted lower bound or remaining iterator size. The
Fusecontract additionally requires the inner iterator to remain present.Loop contracts
Loop invariants preserve the index and iterator-size relationships needed by unchecked accesses in
ArrayChunks,Skip,Take, andZip.Specialization-aware harnesses
The harnesses construct iterators implementing the required
TrustedRandomAccess,TrustedRandomAccessNoCoerce, orTrustedLenbounds and call the specialized implementation directly where necessary.Representative concrete types
The proofs instantiate the adapters with signed and unsigned integer widths,
bool,(), and[u8; 4].MapWindowsadditionally exercises multiple window sizes.Kani checks the resulting programs for invalid pointer accesses, uninitialized reads, mutation through invalid references, invalid values, arithmetic failures, and assertion violations.
Notes
Generic type requirement: Kani verifies concrete monomorphized programs. This branch therefore uses representative concrete types rather than providing a single non-monomorphized proof for arbitrary
T.Const-generic coverage: Chunk and window operations are exercised with representative values of
N, rather than universally quantifying over every nonzero const-generic value.Following the upstream library subtree update in Merge subtree update for toolchain nightly-2026-09-25 #687 (nightly-2026-09-25), the adapter-specific Cloned::next_unchecked and Map::next_unchecked implementations, along with the active UncheckedIterator trait integration, have been removed. I therefore removed their obsolete harnesses. The separate __iterator_get_unchecked implementations remain and are still covered.
Verification
All added Challenge 16 harnesses pass locally with Kani.
Resolves #280
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.