Challenge 21: Verify safety of StrSearcher with Kani - #621
Draft
v3risec wants to merge 1 commit into
Draft
Conversation
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.
Summary
This PR adds Kani verification for the substring searcher in
core::str::pattern, covering the empty-needle implementation and the forward and reverse Two-Way search methods required by Challenge 21.Verification Coverage Report (12/12 Harnesses Verified)
nextnext_matchnext_rejectnext_backnext_match_backnext_reject_backnextRejectAndMatchcandidate, summarized forward/reverse byte scans, rejection boundary repair, forward progress, valid Match/Reject ranges, and invariant preservation.next_matchMatchOnlysearch from an arbitrary failed-candidate prefix through one real candidate and a conservative suffix summary that covers a later Match or exhaustion.next_rejectnextstep, covering every Reject or Done exit while checking progress and invariant preservation.next_backRejectAndMatchpath, summarized reverse/forward byte scans, rejection boundary repair, reverse progress, valid Match/Reject ranges, and invariant preservation.next_match_backMatchOnlysearch from an arbitrary failed-candidate prefix through one real candidate and a conservative suffix summary.next_reject_backnext_backstep, covering every Reject or Done exit.Verification Approach
The verification defines a stable-state invariant
Cfor bothStrSearcherimplementations.For
EmptyNeedle,Crequires the forward and reverse cursors to remain in bounds and on UTF-8 boundaries. The constructor harness establishesC, and each method harness starts from an arbitrary state satisfyingC, checks the returned transition, and proves thatCis preserved. The Kani-only character step nondeterministically selects a width from 1 through 4, bounds it by the remaining bytes, and imports the UTF-8 boundary fact permitted by Challenge 21. Because empty-needle results strictly alternate between Match and Reject, the four filtering methods need at most two concrete calls tonextornext_back; this removes their production loops without adding a loop invariant or unwind bound.For
TwoWaySearcher,Ccaptures the safety-relevant stable state: bounded UTF-8 cursor positions, nonzero bounded periods, bounded critical positions, consistent long-period sentinels, valid short-period memory states, and UTF-8 boundary facts for the short-period cuts. Each of the six method harnesses starts from a symbolic state satisfying this invariant and proves valid output ranges, cursor progress, preservation of preprocessing fields, valid memory-state transitions, and restoration ofCbefore returning.The Two-Way candidate loops use loop stubbing. A nondeterministic loop head represents any prefix of failed candidates, one representative candidate retains the real production control flow and arithmetic, and a suffix summary over-approximates any number of later failed candidates followed by either a Match, a Reject, or exhaustion. The summaries havoc only loop-carried state and constrain it with the corresponding loop-state relation.
The forward and reverse byte-scan loops are summarized by
KaniTwoWayScan. A nondeterministic representative scan index checks the real indexing operations over the complete scan interval. The summary then conservatively chooses a mismatch position or a completed scan while retaining the first and last UTF-8 character bytes and the period probe needed to justify safe Match boundaries.Two-Way Reject results may initially stop at byte positions that are not character boundaries. The Kani-only boundary-repair summaries first prove that the raw cursor is within the haystack, execute the real increment/decrement operation at a representative non-boundary loop head, and then use the Challenge 21 UTF-8 assumption that a character has at most three continuation bytes to summarize arrival at a nearby character boundary.
Verification Tradeoffs
Directly unwinding the nested Two-Way candidate and byte-scan loops does not finish within a practical verification budget and would make the proof depend on a fixed search length. The loop summaries avoid fixed unwind bounds for those loops and over-approximate their safety-relevant behavior.
The scan and candidate summaries are proof cuts for memory safety and valid UTF-8 result boundaries. They do not prove that every reported Match is the first or semantically correct substring match, nor do they prove the full functional correctness of the Two-Way algorithm.
The concrete harness inputs are symbolic valid UTF-8 subslices of 16-byte storage arrays. The loop summaries cover arbitrary loop prefixes and suffixes without fixed unwinding, but the current harness models do not constitute an arbitrary-length input proof.
Scope Assumptions
type_invariant_two_way_searcher.#[cfg(kani)]and do not affect non-Kani builds.Verification
All added Challenge 21 harnesses pass locally with Kani.
Resolves #278
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.