Conversation
Safety-precondition contracts and Kani harnesses for the Challenge 17 slice functions (library/core/src/slice/mod.rs): 43 harnesses in slice::verify — 10 unsafe (contracts; proof_for_contract where the impl resolves) and 27 safe (no-UB), should_panic pairs for the documented panics, and a kani::cover on every assume-bearing harness. Loop-bearing functions are verified at bounded lengths; genuine-unbounded and generic-T are disclosed in the PR description.
This branch has not been deployed
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.
Towards #281.
Kani harnesses and safety-precondition contracts for Challenge 17: Verify the safety of
slicefunctions — the 10 unsafe functions (contracts + no-UB) and the 27 safe functions (no-UB) fromcore::slice. This adds 43 harnesses toslice::verify;align_to/align_to_mut(unsafe) andreverse(safe) already have harnesses there and are unchanged. Each harness runs the real shipped body — no#[cfg(kani)]rewrite — and every assume-bearing harness carries a satisfiedkani::cover(32 total). All pass viascripts/run-kani.sh.Unbounded length and generic
Tare not literally met. This is left as a committee question open across the sentence-pair challenges, and is disclosed under Limitations. Most slices are verified over a symbolic length bounded by a concrete backing array (kani::slice::any_slice_of_array);get_disjoint_check_valid, which takes a raw length, is verified over a fully symbolic length;partition_dedup_by(fixed length 4) and therotate_*dispatch harnesses (lengths fixed to select an algorithm) are verified over fixed lengths. The four loop-bearing functions (binary_search_by,partition_dedup_by,swap_with_slice,rotate_*) additionally carrykani::unwindbounds, each for the reason noted with the function below.Success criteria
#[requires]+proof_for_contract; theget_uncheckedtrio via a mirroredkani::assume(proof_for_contractcan't resolve them at the pinned Kani — see Upstream Kani contributions);align_to/align_to_mutpre-existing.T(no monomorphization)What the harnesses check
proof_for_contractwhere the trait impl resolves); no-UB harnesses on the safe functions. No#[cfg(kani)]body rewrites.should_panicpairs (4:swap_with_slice/copy_from_slicelength mismatch,copy_within/rotate_leftout-of-range).rotatedispatch.ptr_rotateselects one of three algorithms by element size; thememmoveandgcdalgorithms are verified (dispatch confirmed from the run trace). Theswapalgorithm is reached only for very large slices (min(left, right) > 256 / size_of::<T>, i.e. hundreds of elements) — intractable at the CI object budget, so it is disclosed rather than verified.kani::cover(32 total).Per-function coverage (click to expand)
Unsafe (10):
get_uncheckedcheck_get_unchecked_usizeget_unchecked_mutcheck_get_unchecked_mut_usizeget_disjoint_unchecked_mutcheck_get_disjoint_unchecked_mut_usize_2swap_uncheckedcheck_swap_unchecked_u8#[requires]+proof_for_contractas_chunks_uncheckedcheck_as_chunks_unchecked_u8_4#[requires]+proof_for_contractas_chunks_unchecked_mutcheck_as_chunks_unchecked_mut_u8_4#[requires]+proof_for_contractsplit_at_uncheckedcheck_split_at_unchecked_u8#[requires]+proof_for_contractsplit_at_mut_uncheckedcheck_split_at_mut_unchecked_u8#[requires]+proof_for_contractalign_toproof_for_contractmatrix (src×dst types)align_to_mutproof_for_contractmatrix (src×dst types)Safe (27):
first_chunk/first_chunk_mutcheck_first_chunk_3/check_first_chunk_mut_3split_first_chunk/split_first_chunk_mutcheck_split_first_chunk_3/check_split_first_chunk_mut_3split_last_chunk/split_last_chunk_mutcheck_split_last_chunk_3/check_split_last_chunk_mut_3last_chunk/last_chunk_mutcheck_last_chunk_3/check_last_chunk_mut_3as_chunks/as_chunks_mutcheck_as_chunks_3,check_as_chunks_4/check_as_chunks_mut_3as_rchunkscheck_as_rchunks_3,check_as_rchunks_4split_at_checked/split_at_mut_checkedcheck_split_at_checked/check_split_at_mut_checkedas_flattened/as_flattened_mutcheck_as_flattened/check_as_flattened_mutcopy_from_slicecheck_copy_from_slice(+check_copy_from_slice_len_mismatch)copy_withincheck_copy_within(+check_copy_within_oob)swap_with_slicecheck_swap_with_slice(+check_swap_with_slice_len_mismatch)as_simd/as_simd_mutcheck_as_simd_i32/check_as_simd_mut_i32get_disjoint_mutcheck_get_disjoint_mut_usize_2get_disjoint_check_validcheck_get_disjoint_check_valid_usize_2(fully symbolic length)binary_search_bycheck_binary_search_by(bounded)partition_dedup_bycheck_partition_dedup_by_spike(fixed length 4)rotate_leftcheck_rotate_memmove_u8,check_rotate_gcd_big_t,check_rotate_zst,check_rotate_noop_zero(+check_rotate_left_oob)rotate_rightcheck_rotate_right_memmove_u8reversecheck_reverse(pre-existing)Upstream Kani contributions
The
get_uncheckedtrio is verified through a mirroredkani::assumebecauseproof_for_contractcan't resolve generic trait-impl methods at the pinned Kani. We fixed that upstream in model-checking/kani#4865 (merged; not yet in this pin) — once a pin bump includes it, those functions can return toproof_for_contract. The bounded-binary_search_bylimit below is filed as model-checking/kani#4911, which now records the measured root cause; the bound can drop once upstream fixes for it land in a pin bump.How to verify
Expected: every harness in
slice::verifypasses, with each cover satisfied.Limitations (disclosed)
any_slice_of_array); a slice of unbounded symbolic length is not constructible in Kani (a real slice needs backing memory). The loop-free functions are length-agnostic, so the bounded proof is representative;get_disjoint_check_valid(taking a raw length) is verified over a fully symbolic length. This is the arbitrary-length half of the sentence-pair committee question.kani::unwind, each for a specific reason noted in-code:binary_search_by—get_unchecked's precondition is not discharged when the comparator closure receives the element by reference (Loop contract:get_uncheckedprecondition not discharged when the slice element is passed into a closure kani#4911).partition_dedup_by— an explicitloop_modifiesclause cannot name the loop body's compiler-generated temporaries, so no valid modifies set can be formed for a loop-invariant proof.swap_with_slice— the element copy runs throughswap_nonoverlapping's internal chunked loop, a shared function that carries no loop contract, so it requires an unwind bound.rotate_left/rotate_right— thememmoveandgcdalgorithms are verified; theswapalgorithm needs hundreds of elements and is intractable at the CI object budget (see therotatedispatch note above).T: representative types. Kani operates on concrete GOTO programs, so a single proof generic overTis not expressible. The unsafe operations depend onsize_of/align_of/drop glue, not type identity; the representative types cover those axes. This is the generic-Thalf of the sentence-pair committee question.+374 / −0, one file); no runtime logic is modified.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.