Challenge 21 (partial): bounded Kani PROBE of shipped TwoWaySearcher::next_back short-period arms - #664
Open
ivmat wants to merge 2 commits into
Open
Challenge 21 (partial): bounded Kani PROBE of shipped TwoWaySearcher::next_back short-period arms#664ivmat wants to merge 2 commits into
ivmat wants to merge 2 commits into
Conversation
…:next_back short-period arms
The repo's `upstream_test` CI job runs `./x fmt --check` inside a rust-lang/rust checkout, which uses that repo's rustfmt.toml (style_edition 2024, use_small_heuristics = "Max"). This crate has no rustfmt.toml of its own, so a plain `cargo fmt` does not reproduce it. Formatting only: no harness, assertion, cover string, or bound changed.
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.
What this is
A single, bounded Kani harness (
check_twoway_search_back_arm_covers) that drives the unmodified,shipped
TwoWaySearcher::next_backthrough the real publicReverseSearcherAPI and proves — withinan explicitly stated bound — per-step memory safety, functional soundness of every returned
Match,bounded completeness (no silently-missed match at the fixture), and reachability of all five backward
search-loop arms.
Honest status up front: this is a PROBE, not a solution to Challenge 21. It does not satisfy the
challenge's success criteria on its own. It is offered as a differentiated partial contribution with
its residuals named at the headline, not buried.
What is proved (within the bound)
Fixture: needle
"abab"(fixed; short-period, non-degenerate on both critical factorizations),HAY_LEN = 5(fixed), haystack content symbolic over the 3-symbol representative alphabet{a,b,z},#[kani::unwind(6)](VM-confirmed, every unwinding assertion SUCCESS at this bound).start (adjacency/no-skip), re-derived from the source, not assumed.
Matchslice is byte-for-byte equal to the needle (not merelyin-bounds).
expected match set (
Option<usize>; the two candidate positions 0 and 1 are mutually exclusive at thisfixture) — a silently-missed real match fails an
assert_eq!directly.kani::covers witness that all five backward arms (byteset-skip,left-mismatch, memory_back-guarded fast-forward, match, terminal) genuinely fire within the bound.
Oracle is proven genuine (negative controls)
The soundness oracle was shown to catch a real defect at this exact fixture (HAY_LEN=5, needle
"abab"). The completeness (no-miss) oracle's design was validated the same way, but not at this exact
fixture size — disclosed honestly below rather than glossed over.
match_pos = self.end - needle.len() + 1, an off-by-one in theMatcharm)was run against this fixture and is VM-confirmed RED: the adjacency/slice-equality oracle catches
it directly (3 of 5 cover properties satisfied,
rc=1, FAILED).self.end -= self.period→self.end -= self.period + 1) does not diverge from the correctalgorithm at this exact HAY_LEN=5 fixture — confirmed by exhaustive offline enumeration over all 3^5
haystacks (0 divergent cases; the mutation is provably undetectable at this fixture size). At the
smallest fixture size that does discriminate it (HAY_LEN=6, otherwise the same harness design and the
same no-miss oracle shape), the same one-line mutation is VM-confirmed RED (5 of 5 cover properties
satisfied,
rc=1, FAILED on the no-miss assertion). That HAY_LEN=6 harness is a control only — it isnot part of this submission. It confirms the no-miss oracle design is discriminating, not that it
fires on this exact submitted fixture's completeness axis, which remains an open, disclosed residual
(this harness's HAY_LEN=5 fixture happens to be too small for this particular Galil-rule defect class
to surface observably; a different defect or a larger fixture might not have this gap).
What is NOT proved (residuals — headline, not footnote)
next_back.MatchOnlybackward path (next_match_back/rfind, whereuse_early_reject() == falseisgenuinely different control flow) — the 5-arm reachability here is witnessed under the
RejectAndMatchinstantiation only.
next_backsafety contract is future work: it needs a symbolic-bound quantified loopinvariant that this pinned toolchain cannot discharge (see the corrected note below).
Alternative direction toward a generic contract (diagnostic — not a commitment)
The bounded probe is not the only conceivable contribution here; the natural next rung is a generic
next_backsafety contract (symbolic needle, arbitrary length). The concrete blocker is specific andworth stating for other contributors: a generic proof needs a symbolic-bound quantified loop
invariant — a
forall-quantified "no match skipped in the region already scanned" invariant on thetwo-way search loop, whose stride is data-dependent (
needle.len(),self.period,i - crit_pos + 1). [Corrected 2026-08-30] At this submission's pinned toolchain (Kanid4df833/ CBMC 6.8.0), aforall-quantified predicate whose range is not a compile-time constant is not encoded by the defaultSAT backend — the quantifier is silently dropped, and this affects a flat assertion just as much as one
inside a
#[kani::loop_invariant]. A quantified loop invariant over a constant range does discharge.A generic
next_backcontract needs the data-dependent case, which is therefore out of scope here. Westate this as an observed limitation of this pinned toolchain, not a proof of impossibility. (An
earlier revision of this paragraph attributed the limitation to the quantifier's position — flat
assertion vs. loop invariant; a controlled 2×2 measurement at this pin showed it tracks the bound kind
instead.)
We are not claiming that adding quantified-loop-invariant support to Kani would be sufficient to
land the generic contract. The harder, still-open question is whether the two-way skip lemmas —
byteset-skip, the
crit_posleft-part shift, and the period/memoryfast-forward each preserving "nomatch in the skipped span" — are themselves tractable as bounded or contract-backed obligations. That
is a separate analysis. This note flags the capability gap so the frontier is visible; it is not a
promise of a tool change.
Reproduce
Verified at this branch's head with Kani 0.67.0 (d4df833) / CBMC 6.8.0 (the repo's pinned
toolchain):
VERIFICATION:- SUCCESSFUL, 5 of 5 cover properties satisfied.(Unwind is pinned in-source via
#[kani::unwind(6)].)Acceptance manifest
A machine-readable statement of exactly what these proofs establish and do not establish — one entry
per claim, with declared bounds, evidence, and assurance band, checked by a standalone validator — is
shipped as a real-world example in the format's own repo:
examples/verify-rust-std-pr664/acceptance.toml(format). It records the submitted probe (graded A3:
a red mutation control plus explicit
assumesnarrowing what the result covers) and an explicit A0gap entry for what this PR does not prove (the
MatchOnlybackward path; a generic symbolic-needlecontract). Restamped and re-verified at this branch's head (
09a3d026f66).Edited 2026-08-30: corrected the quantifier characterization under "Alternative direction" after a
controlled measurement at this PR's pinned toolchain, and added the acceptance-manifest section.