Repository navigation
Challenge 8: bounded Kani proofs of SmallSort memory safety and sorting correctness, with helper contracts - #752
Open
Tiramisu-Cake wants to merge 1 commit into
Conversation
Add Kani harnesses in core::slice::sort::shared::smallsort::verify, and safety contracts (requires/modifies, under cfg(kani)) on the real swap_if_less, insertion_sort_shift_left, insert_tail, sort4_stable and bidirectional_merge. Existing function bodies and signatures are unchanged; smallsort.rs only gains contract attributes, comments and three cfg(kani) helper predicates used by the contracts. The write sets in these contracts list element and destination memory only, so they apply to comparators that write nowhere else. A comment above swap_if_less and the module docs in verify.rs state this, and a negative control checks that a comparator writing a captured counter is rejected. Order contracts are also checked on proof-owned profiles that forward a fixed element type and comparator to the real helpers, and on N=2 wrappers that call the three small_sort trait entry points. 235 harnesses are ordinary. The 13 negative controls stay behind cfg(kani_smallsort_controls), which CI never sets.
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.
This PR verifies the
smallsortmodule (#56) with Kani on the real implementations, within explicit bounds: memorysafety of the three
small_sorttrait entries and the helpers behind them at lengths that reach every dispatchboundary up to N32; sorting correctness (sortedness plus full-value multiset preservation) on the general, fallback
and network routes; and
requires/modifiescontracts on five real helpers, proved and then used at real call sites.235 harnesses run
SUCCESSFULon each OS in the repository's unchanged Kani workflow, and 13 negative controls checkthat the oracles and contracts reject what they should. Function bodies, signatures, workflows and tool pins are
unchanged.
Highlights
fallback and network routes behind them run as-is, at representative lengths that include every dispatch boundary up
to N32. Comparators return arbitrary answers or mutate through a
Cell; element types include a Drop type, a ZSTand sizes of 1, 8, 16, 17, 85 and 86 bytes, which select every size-dependent branch.
symbolic value: it occurs as often after the sort as before. Some families sort by a key (
x >> 1) under whichdistinct values tie, so a key-only oracle would miss payload corruption; the network and logical-model families use
the ordinary
<. Covered on the general and stable routes at u8 and u64 N0..9, on the fallback route at N0..6, andon the network route (sortedness up to N13, multiset preservation below the network width, and permutation of
distinct tags at N9 and N13 for an arbitrary comparator).
requires/modifiesonswap_if_less,insert_tail,insertion_sort_shift_left,sort4_stableandbidirectional_merge, proved by 16proof_for_contractharnesseswith an arbitrary-answer comparator, and used by four
stub_verifiedconsumers at real call sites with the sameelement type, comparator and allocation as the proof (the u8 networks at N9, N13 and N32; u64
sort8_stable).ensures(model-sorted output, preserved full-value count) onproof-owned profiles of the helpers and on wrappers of the three trait entries at N2. Contracts directly on the
trait methods are blocked by two Kani issues we reported with reproducers (Limitations).
swap_if_less, payload corruptions, wrongcomparators,
requiresviolations, a comparator writing outside the write set, a duplicated tag. Each fails exactlyits designated check on this library tree with the pinned Kani.
Challenge criteria
small_sorttrait entriesswap_if_less,insertion_sort_shift_left,sort4_stable,has_efficient_in_place_swapinsertion_sort_shift_leftbody at every offset for u64 N1..20; predicate on 12 typessmall_sortsortsensureson N2 wrappers of each entry; sorting proved by harness oracles at the lengths below; contracts on the trait methods themselves await kani#5116 and kani#4908What is checked (235 harnesses, organizer flags including
--no-assert-contracts)small_sort_general*)small_sort_fallbacksmall_sort_networkinsertion_sort_shift_leftfrom offset 9 or 13[u8; 16/17/85/86], a Drop type, ZST (stable trait entry,()),Cell; dispatch to N32; insertion at every offset for u64 N1..20 andCellN1..5, N20; stable route with minimal scratch at N2small_sortCell<u8>, Drop keyhas_efficient_in_place_swap,sort4_stable,swap_if_lessat equal positions, helper pairsswap_if_less14,sort4_stable2,insert_tail5,bidirectional_merge12, N2 forwarding wrappers 3proof_for_contract16,stub_verifiedconsumers 4requiresandmodifieson the executed domainFull-value multiset preservation uses an independent symbolic probe on the listed integer and byte-array fixtures;
Celland Drop fixtures use their logical models. The tag harnesses sort fixed distinct u8 tags with an arbitrarycomparator; that this carries over to arbitrary values (data moves only by comparator answers) is a source-level
argument.
Contracts
On the real helpers. Safety contracts:
requireson pointer validity and allocation geometry,modifieson theelement or destination memory. Executed domains:
swap_if_less: u8, u64 and[u8; 17]in a 4-element allocation; u8 in 9- and 13-element allocations; u8 and u64in a 32-element allocation with any base and any two positions, equal positions included.
insert_tail: u64, tail 1..4 in a 5-element allocation, and tail 1..15 from the start of a 17-element allocation.insertion_sort_shift_left: u64 arrays of lengths 2 and 3, every valid offset. The body itself is also checkeddirectly at every offset for lengths 1 through 20.
sort4_stable: u64, disjoint 4-element source and destination.bidirectional_merge: u64 of length 2 and 4, and of length 8 at the geometry of the 48-element stable scratch.Each consumer replaces the real call with the proved contract through
stub_verified, with the same element type,comparator and allocation domain as the contract proof: the unstable u8 network at N9, N13 and N32 with
swap_if_less, and u64sort8_stablewithbidirectional_merge. The same routes are also checked with their realbodies. Proofs on the real helpers use one comparator function item per harness that returns an arbitrary answer, so
no ordering law is assumed. Only the executed domains are claimed.
On profiles. Sorting contracts (
ensures: model-sorted output, preserved full-value count) are checked onproof-owned profiles that forward a fixed element type and one canonical function-pointer comparator to the real
helpers (a guard rejects any other comparator), and on three N2 wrappers that call the trait entry points.
Negative controls (13, behind
cfg(kani_smallsort_controls), which CI never sets)swap_if_lesscopy;requires: out-of-allocationswap_if_less,insert_tailwithbegin == tail, andbidirectional_mergeinto an overlapping destination;swap_if_lesscontract proof with a comparator that increments a captured counter, which must fail the write-setcheck;
We ran all thirteen on this library tree with the pinned Kani (
cd355b0b), outside GitHub Actions: each fails exactlyone check, the designated one (report).
CI
We ran the upstream workflows unchanged on this commit in our fork via
workflow_dispatch(fork runs, not upstream PRchecks):
successon Ubuntu and macOS, including all 8 partition jobs, autoharness andBuild JSON from logs, in attempt 1.successin attempt 1.In the autoharness runs all 235 SmallSort harnesses are
SUCCESSFULon each OS, with zero failed checks and978/978 satisfied covers (counted from the logs; the workflow does not enforce covers). No warning comes from
the SmallSort files. SmallSort's summed verification time is 7,802 s on Ubuntu and 10,534 s on macOS, 65.9% and 70.3%
of the job's manual-harness total; the longest proof takes 246 s on Ubuntu and 340 s on macOS, below the 600 s
timeout (per-harness timings). The harnesses are independent: any of them can be split or moved behind the
opt-in cfg without touching the rest.
At this base the autoharness job runs on a pull request only with the
run-autoharnesslabel (or in the merge queueor on dispatch); we would appreciate the label on this PR.
Limitations
Several of the remaining gaps come from Kani limitations that we reported or reproduced upstream; they are linked
below.
element and destination memory only, since Kani cannot state the footprint of an arbitrary
FnMut; the contractsare claimed for comparators that write nowhere else (a comment above
swap_if_lesssays so, and the counter controlchecks it).
default fnis not accepted on the current pin(kani#5116), nor is a
proof_for_contracton a trait methodwith its own generic
F(kani#4908);the N2 wrappers are the nearest contract-form statement this Kani verifies. Kani main has since fixed the second
case (kani#5135), which is not yet in the repository's pin; it
opens direct contracts on the non-default
small_sortimplementations, which we intend to add here once the pinincludes it.
tree also ran into
kani-drivermemory use (kani#5117).panic_on_ord_violationwith anassume(false)stub; the other164 do not replace that function. Comparator panics and unwind recovery are not verified.
instrumentation on these routes:
-Z uninit-checksreports unsupported operations here(kani#3300) and
-Z valid-value-checksdoes not yet supportcopy_nonoverlapping(kani#3762), which these routes use.Writes to immutable bytes and aliasing beyond Kani's pointer checks are not modelled.
still fails on the organizer Kani pin
cd355b0b(pin-check report);
the four array-valued fixtures
network::swap_contract_bytes16_n2,network::swap_contract_bytes17_n2,merge::sort4_contract_bytes16_n4andmerge::sort4_arbitrary_comparator_bytes16_n4retain that model caveat.(). The public sortentry points short-circuit ZSTs, and
select_nth_unstableskips selection work for them after validating theindex. ZST comparators read with
ptr::read, sinceread_volatileon a ZST gives spurious dereference failures(kani#5072). The stable Freeze route is checked with a separate
48-element scratch and with the minimal scratch at N2. Tie stability is not proved separately.
Beyond this PR: wider domains (longer networks, more insertion lengths) need more verification time or tool support;
we left them out rather than stretch the CI budget, and can propose them separately.
Note for maintainers. The
sort4_stablecontract makes source/destination non-overlap explicit. The existingSAFETY comment mentions validity for four reads and writes but does not state this condition; the implementation uses
copy_nonoverlapping.Source. One commit on
mainat233f96f66a(it merges cleanly with the currentmain): 17 files underlibrary/, +3941/−0. The harnesseslive in
core::slice::sort::shared::smallsort::verify(verify.rsopens with a module map).smallsort.rsgains onlythe contract attributes, a comment on their write sets, three
cfg(kani)geometry predicates used by the contracts andthe
mod verifyregistration;library/core/Cargo.tomlgets acheck-cfgentry for the controls cfg.This contribution was prepared with the help of AI assistants.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT
licenses.