Challenge 2: memory safety of raw-pointer intrinsics - #649
Open
stefanzetzsche wants to merge 1 commit into
Open
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.
Towards #16. Solves Challenge 2: Verify the memory safety of core intrinsics using raw pointers: safety contracts and Kani harnesses for the raw-pointer intrinsics (Part 1), proofs that the std library's uses of them are safe (Part 2), and contracts on the raw-pointer functions exposing them (Part 3). All 146 harnesses added by this PR pass in a single
kani verify-stdinvocation at the pinned Kani commit.Changes
intrinsics/mod.rsproof_for_contractharnesses for 20 intrinsics, via thin wrappers (Kani cannot attach contracts to body-less#[rustc_intrinsic]s, model-checking/kani#3325; same pattern as the in-treetransmute_unchecked_wrapper); also fixes the pre-existingcheck_copy_untypedspec helper, which comparedsrc[0]'s initialization state againstdst[elem]'s instead ofsrc[elem]'smem/mod.rsswapandalign_of_val; contract + harnesses foralign_of_val_raw(Part 3)mem/maybe_uninit.rszeroed(reacheswrite_bytes), byte-levelensuresfmt/num.rsu64::_fmt, the successor of the challenge-listedparse_u64_into(Part 2)ptr/mod.rsptr::swapandptr::write_bytes(Part 3)slice/mod.rscopy_from_slice(Parts 2/3)Part 1: the intrinsics
All 21 listed intrinsics are contracted and verified: 20 by this PR, and the 21st,
typed_swap, already carries a verified in-tree contract with passing harnesses (added by #37, before this challenge was tackled), so re-verifying it here would only duplicate upstream code. This PR still exercises that existing contract as part of themem::swapusage proof (Part 2). Two findings for the committee:parse_u64_intono longer exists anywhere in the tree, and five intrinsics (volatile_copy_memory,volatile_copy_nonoverlapping_memory,volatile_set_memory,unaligned_volatile_load,unaligned_volatile_store) are reported unsupported by Kani and have no fallback body. For those five, the contract is verified on a*_modelfunction whose body is the non-volatile equivalent: the two forms differ only in LLVM's volatile flag, an optimization barrier with no effect on the abstract memory state CBMC reasons about, and the docs state their safety requirements match the non-volatile counterparts. model-checking/kani#4672 and model-checking/kani#4673 (merged 2026-08-01/02, after the January pin) add support for these five; once the pin bumps past them, the models should be swapped for the real intrinsics.Contracts state exactly the documented
# Safetyconditions. Where the documented condition is per-case, so is the contract:size_of_val/align_of_valget aSizedwrapper with no precondition (documented "always safe", proven with dangling pointers) and a slice wrapper requiring only that the total size fitsisize. All remaining intrinsics are Kani-modeled; each harness pins the model with a concrete assertion (e.g.vtable_sizereturns exactlysize_of::<T>(), store harnesses read the value back).copy,copy_nonoverlapping,write_bytes, volatile/unaligned familyrequires; untyped-copyensures; byte-granular pointer offsets so misaligned inputs are generated, not avoidedvtable_size,vtable_align,size_of_val,align_of_valensures(size fitsisize, alignment is a power of two); per-type fidelity asserts in harnesses; the layout queries cover all three documented cases (Sized, slice tail, trait-object tail via real compiler-produced vtables)ptr_offset_from,ptr_offset_from_unsigned,arith_offsetrequires;arith_offsetchecked over a mid-range address window (CBMC does not model the address-space edges)compare_bytesrequires(docs stress chunked reads); checked instances bounded to 8 bytes (unwind(9)) — the bound lives in the harness, not the contractread_via_copy,write_via_movewrite_via_moveensures dereferenceability post-state (the moved value cannot be re-read for genericT)For the trait-object case, Kani has no "is a vtable" predicate, so (as for
vtable_size/vtable_align) dereferenceability of the three metadata words is the stated approximation of the documented condition; the harnesses supply real compiler-produced vtables, so the precondition is discharged non-vacuously.Part 2: std usage sites
mem::swapproof_for_contract(swap)+stub_verified(typed_swap_nonoverlapping), so the intrinsic'srequiresbecome explicit call-site obligations<[T]>::copy_from_slicemem::align_of_val&TMaybeUninit::zeroedwrite_bytesis safe and all backing bytes are 0fmt::parse_u64_intou64::_fmtis contracted and verified instead — the documented buffer-size safety condition asrequires, digits-only/lengthensures, plus a bounded value-fidelity harness. The contract lives on a#[cfg(kani)]wrapper because_fmtis macro-generated for every integer type; annotating it directly would touch them allPart 3: raw-pointer functions
Contracts + harnesses for
ptr::swap(overlap-permitted path exercised),ptr::write_bytes, andmem::align_of_val_raw(including dangling-pointer harnesses for the documented always-safeSizedcase).copy_from_slicedoubles as its own Part 3 item.Wherever a harness restricts inputs with
kani::assume, akani::covercheck confirms some input satisfies the restriction, so no proof passes vacuously; value-level properties that cannot be genericensuresclauses (noPartialEqbound) are asserted per concrete type.Relation to other work on this challenge
#37 (merged) contributed the
typed_swapcontract this PR builds on and exercises via themem::swapusage proof.#618 is an open partial solution covering 15 Part 1 intrinsics. This PR was developed independently and additionally covers the five Kani-unsupported intrinsics (via the models above), Part 2, and Part 3. Its review thread shaped two of this PR's contracts (per-case preconditions instead of a blanket
can_dereference), and its author's now-merged Kani fixes (model-checking/kani#4672, model-checking/kani#4673) are what will let the five models be replaced with real-intrinsic proofs once the pin bumps past them.#643 is another open full-table attempt, developed independently on the same wrapper pattern; this PR adopts its correct fix to the pre-existing
check_copy_untypedspec helper (src_datawas never offset by the chosen element), with all affected harnesses re-verified. Beyond that, this PR goes further on three fronts:u8-only (alignment trivially 1) with a single fixed overlap shift, so its alignment precondition is never non-trivially exercised.zeroedharnesses (Challenge 2: Kani contracts for raw-pointer core::intrinsics #643 has 2, integer-only),copy_from_sliceat symbolic lengths 0..=32 across 5 element types (Challenge 2: Kani contracts for raw-pointer core::intrinsics #643: oneu8harness at fixed length 4), and amem::swapproof thatstub_verifieds the intrinsic contract so itsrequiresbecome explicit call-site obligations (Challenge 2: Kani contracts for raw-pointer core::intrinsics #643 asserts value exchange only).ptr::swap(overlap-permitted path),ptr::write_bytes,mem::align_of_val_raw— as the challenge asks; in Challenge 2: Kani contracts for raw-pointer core::intrinsics #643 this part is essentially absent (plain fixed-size harnesses without contracts,align_of_val_rawnot covered).By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.