Conversation
Add mod verify harnesses for all 22 Challenge 24 targets in IntoIter and the spec_extend / spec_from_iter / spec_from_elem / extract_if helpers. Harnesses exercise the real shipped bodies (no cfg(kani) rewrites) over symbolic-length inputs, with every kani::assume paired with a satisfied kani::cover. Unbounded and generic-T are not literally met (committee gate) and are disclosed. Additive only.
…y, drop) Add harnesses instantiating the IntoIter / spec_* move and index paths over element shapes the existing u8/i8/() harnesses do not exercise: an over-aligned type, an odd-stride type, a validity-niche type, and a destructor type across the move/consume paths. Existing harnesses are unchanged; additive only.
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.
TLDR: Kani harnesses for all 22 Challenge 24 targets (
Vec::IntoIter+ thespec_*/from_elem/extract_ifhelpers) — 34 harnesses verifying the real shipped bodies (nocfg(kani)rewrites) over symbolic-length inputs, with functional postconditions, ZST-arm coverage, and element-type shape coverage (alignment, stride, validity, drop). Unbounded and generic-Tare not literally met — the same committee question open across the sentence-pair challenges — and are disclosed below.Towards #285.
Context
Challenge 24 covers the safety of
Vec's consuming iteratorIntoIter(library/alloc/src/vec/into_iter.rs) plus the specialization helpers it routes through (spec_extend,spec_from_iter,spec_from_iter_nested,spec_from_elem,extract_if). These read and advance a raw pointer pair (ptr/end) into a heap allocation, so the safety obligations are pointer in-bounds, no double-drop on early exit or panic, and correct handling of the ZST encoding (whereendwalks by bytes andptrstays fixed).Solution
34 harnesses across the 22 listed functions. Each exercises the function's actual shipped body — no
#[cfg(kani)]body substitution and nokani::assume(false)on any real branch — and asserts the function's observable effect, not just the absence of UB.kani::slice::any_slice_of_array+to_vec, so the pointer-walking loops (fold,try_fold,next/next_back,advance_by/advance_back_by) run a symbolic number of iterations up to the backing size (64 for theIntoIterharnesses; the allocation-heavy harnesses use smaller bounds, detailed under limitations).size_hint == (len, Some(len));next/next_backreturn exactly the first/last element and shrink the remaining length by one;advance_by(k)/advance_back_by(k)returnOkiffk <= len, with the remaining length checked in both branches;next_chunk::<2>succeeds ifflen >= 2;foldvisits exactlylenelements;from_elem(elem, n)produces lengthnwithv[j] == elemat a symbolic index;spec_extend/from_iterpreserve length and element values at a symbolic index;into_vecdequepreserves length.try_folduses a comparator that can short-circuit (Err), in both au8variant and aDrop-carrying variant whose remaining elements are destroyed byIntoIter's realDrop— the double-drop class the body'sptr.add(1)-before-fordering exists to prevent.dropandforget_allocation_drop_remainingalso use theDrop-carrying type.extract_if::nextreads and writes through the actualvec.as_mut_ptr().add(i)pointer (nocan_writeassumption).IntoItermethod has a structurally separateT::IS_ZSTbranch (byte-walkingend, fixedptr);next/fold/advance_byare verified onVec<()>at symbolic length, covering that distinct branch.__iterator_get_uncheckedalready carries#[requires(i < self.len())]+kani::modifies(self)onmain, previously with no exercising harness. Kani cannot resolve a generic trait-impl method as aproof_for_contracttarget (kani#1997), so the harness here is the mirroring assume-guarded proof, asserting the read's value against the source slice, with an in-code note.kani::coverwitness (14 covers total, including loop witnesses on the fold/drain harnesses).How to verify
Scoped to these harnesses (from the repo root):
Expected:
Complete - 34 successfully verified harnesses, 0 failures, 34 total.with1 of 1 cover properties satisfiedfor each of the 14 covers. The harnesses also run in the standard./scripts/run-kani.shsweep, as in CI. (Verified locally at Kani 0.67.0 / CBMC 6.10.0.)Additional element-type shape coverage
The existing per-type harnesses are unchanged. This adds 8 harnesses extending
coverage to properties of
Tthe currentu8/i8/()instantiations do notexercise. Values and lengths stay symbolic; each new harness body is written
once for arbitrary
Tand instantiated per shape.check_into_iter_next_al16next#[repr(align(16))]): allocation + 16-byte stridecheck_into_iter_next_boolnextcheck_into_iter_next_droptokennextcheck_into_iter_next_back_droptokennext_backcheck_into_iter_get_unchecked_arr3__iterator_get_uncheckedadd(i);T: NonDropis the trait's own boundcheck_spec_extend_intoiter_droptokenspec_extend(IntoIter)forget_remaining_elementswithout double-dropcheck_from_iter_intoiter_droptokenSpecFromIter(IntoIter)ManuallyDrop+from_partsownership transfercheck_extract_if_next_droptokenExtractIf::nextShapes: a
#[repr(align(16))]wrapper (Al16),[u8; 3](odd stride),bool(validity niche), and a destructor type (
DropToken, with a realDropimpl soneeds_drop()is true) applied across the move/consume paths. Each new bodymirrors its existing
_u8counterpart with the element type swapped, so a greenresult is a genuine property check, not a tautology. These shapes exercise drop
glue, validity-niche preservation, over-alignment, and odd (non-power-of-two)
stride for the element type — behaviour the
u8/i8/()instantiations cannotreach.
On
booland validity:kani::any::<bool>()yields only valid bit patterns,so this harness proves the move-only element handling preserves a niche type's
validity invariant (the code never fabricates a
boolfrom arbitrary bits) —reading uninitialised memory as any
Tis caught by Kani's memory modelindependently of the element type.
On the generic-
Tclause: this PR keeps its existing per-type harnesses andadds shape coverage in generic-body form for the new instantiations. Full
conversion of the whole suite to single generic bodies is deferred pending the
committee ruling on the clause — we would rather not churn a verified suite
before the Option-A form is confirmed as the standard, and will complete the
conversion promptly on a favorable ruling.
CI cost
The 8 added harnesses each verify
VERIFICATION:- SUCCESSFULat the pinnedKani (
152c6a8, kani 0.67) / CBMC 6.10.0, object-bits 12,--no-assert-contracts(CI-faithful,
run-kani.sh). CBMC solving times: 2.7–15.5 s each, ≈57 s summed.Exclusions (unchanged from the original submission)
SpecFromElem's genericT: Clonedefault impl routes throughextend_with(a Challenge 23 target); not claimed.
Reviewer notes — disclosed limitations
IntoIterfunctions; smaller where noted below). We also measured a loop-contract route to genuine unboundedness on theIntoIterpointer loops: once the loop contract havocs the pointer, the invariant must re-establish pointer validity viakani::mem::same_allocation, and CBMC does not terminate on that predicate for these loops. A minimalsame_allocationloop invariant over a stack array does verify, so the limit is specific to theIntoIterheap-pointer shape rather than the predicate in general; the index-based loop invariants used for thecoreiterators do not hit it. The bounded harnesses above are the working encoding today.T: not met. Harnesses use representative element types (u8,()for the ZST arm, aDrop-carrying token for drop glue, and the over-aligned, odd-stride, and validity-niche shapes described under Additional element-type shape coverage above). This is the same acceptance question open across the sentence-pair challenges; deferring to the committee on whether representative-type coverage satisfies the clause.extract_if::next(3) and the defaultfrom_iter(4) exceed the CI-standard--object-bits 12object budget at larger sizes (measured); theDrop-token harnesses use a small fixed size (4) because drop obligations are per-element identical, so larger sizes add solver time without new proof obligations. Each bound is noted in-code at its site.spec_extendharnesses pre-size the destination soappend_elements'reserveis a no-op: the copy path is verified; element-by-element growth routes throughextend_desugared(a Challenge 23 target) whose reallocation branch exceeds the object-bits budget above.+687 / -0across 7 files); no runtime logic is modified.