Skip to content

Verify iter adapter unsafe methods + safe abstractions (challenge #16)#602

Open
MavenRain wants to merge 4 commits into
model-checking:mainfrom
MavenRain:16-iter-adapters
Open

Verify iter adapter unsafe methods + safe abstractions (challenge #16)#602
MavenRain wants to merge 4 commits into
model-checking:mainfrom
MavenRain:16-iter-adapters

Conversation

@MavenRain

@MavenRain MavenRain commented Jun 17, 2026

Copy link
Copy Markdown

Challenge 16: Verify the safety of core::iter adapters

Towards challenge #16 (tracking issue #280). Adds Kani harnesses for the unsafe
methods and safe abstractions in library/core/src/iter/adapters/.

Unsafe methods (proven free of UB)

adapter unsafe methods
cloned __iterator_get_unchecked, next_unchecked
copied __iterator_get_unchecked
enumerate __iterator_get_unchecked
fuse __iterator_get_unchecked
map __iterator_get_unchecked, next_unchecked
skip __iterator_get_unchecked
zip __iterator_get_unchecked

The __iterator_get_unchecked harnesses read at a symbolic in-bounds index
(kani::any_where(|i| i < size_hint().0)) over a kani::any() backing array;
the next_unchecked harnesses establish the non-empty precondition by
construction.

Safe abstractions (proven free of UB)

array_chunks::next_back_remainder and array_chunks::fold,
copied::spec_next_chunk, filter::next_chunk_dropless,
filter_map::next_chunk, the map_windows Buffer operations (as_array_ref,
as_uninit_array_mut, push, drop), step_by::original_step,
take::spec_fold and take::spec_for_each, and zip::next / next_back /
nth / fold / spec_fold.

Approach

  • Harnesses live in a #[cfg(kani)] mod verify at the end of each adapter file.
  • Because Kani cannot attach a proof_for_contract to a generic trait method
    (kani#1997), each generic method is instantiated at representative concrete
    element types via macros, with the caller precondition established by
    construction rather than as a #[requires].
  • The representative element types are unit (ZST), u8 (1-byte), char (4-byte,
    validity-constrained), and compound tuples with padding such as (char, u8)
    and (u32, i16); zip harnesses pair two of them. These exercise the distinct
    memory-layout classes, since the unsafe operations depend on
    size_of::<T>()
    and align_of::<T>() rather than on T's identity.

Honest caveats (please review)

Two points where this encoding does not literally match the challenge's success
criteria, written out so the committee can weigh them:

  1. Monomorphization. The criteria ask for a proof that holds for generic T.
    These harnesses instead prove the property at the representative concrete
    instantiations above. The soundness argument is the size-and-align one
    described (the unsafe accesses do not branch on T's identity, and the
    representative types cover ZST, single-byte, 4-byte validity-constrained,
    and
    padded-compound layouts), but this is representative-type coverage rather
    than
    a single universal-over-T proof. The reason is architectural: CBMC
    monomorphizes and Kani has no generic contracts (kani#1997). Glad to switch
    encodings if the committee prefers a different form.
  2. Boundedness. For the single-source accessor harnesses (cloned,
    copied,
    enumerate, fuse, map, skip), the __iterator_get_unchecked proof is
    effectively unbounded in the ZST and u8 cases: the backing length ranges up
    to the type's representable maximum (isize::MAX / u32::MAX elements) with a
    symbolic index. The char and padded-tuple cases of those harnesses use a
    fixed backing length of 50, and zip's __iterator_get_unchecked uses a fixed
    length of 50 for every element-type pair except u8 / u8 (which is
    u32::MAX). The iterating abstractions (zip next/nth/fold/spec_fold,
    take spec_fold/spec_for_each, filter, filter_map,
    array_chunks::fold, step_by) use a bounded length (5 to 16) with an
    explicit #[kani::unwind], because CBMC must unroll the loop. The per-element
    safety obligation is structurally identical at every position, so a modest
    bound exercises it, but coverage there is bounded rather than arbitrary-length.

AI-assisted (Claude); disclosed per repo policy.

…del-checking#16)

  Kani harnesses for core::iter::adapters:
  - Unsafe (__iterator_get_unchecked / next_unchecked / get_unchecked):
    cloned, copied, enumerate, fuse, map, skip, zip.
  - Safe abstractions with internal unsafe: array_chunks::next_back_remainder,
    copied::spec_next_chunk, map_windows Buffer::{as_array_ref,
    as_uninit_array_mut, push, drop}, step_by::original_step, zip::{next,
  next_back}.

  Each harness takes a symbolic-length sub-slice of a fixed bounded array and is
  instantiated per representative element type ((), u8, char, (char, u8)) via
  macros. The generic unsafe trait methods use plain #[kani::proof] with the
  #[requires] precondition established by construction, since Kani cannot put
  contracts on generic trait methods (kani#1997).

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
@MavenRain MavenRain requested a review from a team as a code owner June 17, 2026 00:53
…ing#16)

Adds Kani harnesses for the iterating / chunk-building iterator-adapter
methods deferred from the initial model-checking#16 set for tractability:

  array_chunks::fold              filter::next_chunk_dropless
  filter_map::next_chunk          take::spec_fold / spec_for_each
  zip::nth / fold / spec_fold

Each is instantiated over (), u8, char and a composite tuple type
(32 harnesses total); all verify with 0 failures.

These methods loop over a symbolic-length slice. Drafted without an
explicit unwind bound, CBMC over-unwound the loop and timed out; adding
assumption) plus a tightened MAX_LEN makes each verify in under a second.
  upstream_test runs ./x fmt --check with rust-lang/rust's rustfmt.toml
  (style_edition 2024, use_small_heuristics = Max). Reflow the check_zip_safe!
  invocations and the Filter::new call one argument per line. Formatting only.

Signed-off-by: Onyeka Obi <softwareengineerasaservant@isurvivable.cv>
@MavenRain

Copy link
Copy Markdown
Author

Status update for reviewers: this PR is green on every check and mergeable
against current main.

It covers the complete unsafe surface of the iter adapters: all
__iterator_get_unchecked, next_unchecked, and get_unchecked methods
across cloned, copied, enumerate, fuse, map, skip, and zip, plus the safe abstractions
listed in the description. Each generic trait method is verified at representative
concrete element types via macro instantiation, with the caller precondition
established by construction, since Kani cannot place contracts on generic trait
methods (kani#1997).

The one design decision worth a maintainer's eye is that kani#1997 workaround:
these use plain #[kani::proof] plus precondition-by-construction rather than
proof_for_contract, which proves the same no-UB property the contract would
express. Glad to switch encodings if you would prefer a different form.

The iterating adapters listed as deferred are already verified on a follow-up
branch; I can fold them into this PR or send them separately, whichever you
prefer. Thanks for taking a look whenever you have a chance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant