Challenge 7: Verify safety of Atomic types and Atomic Intrinsics with Kani - #683
Open
v3risec wants to merge 2 commits into
Open
Challenge 7: Verify safety of Atomic types and Atomic Intrinsics with Kani#683v3risec wants to merge 2 commits into
v3risec wants to merge 2 commits into
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 a Kani-based solution for Challenge 7: Safety of Methods for Atomic Types & Atomic Intrinsics.
The verification is organized around the three parts of the challenge:
from_ptrmethods — add safety contracts andproof_for_contractharnesses for the atomicfrom_ptrAPIs.The PR also covers the challenge's optional ordering-validation requirement by adding contracts to safe atomic APIs whose documented ordering restrictions otherwise result in panics.
No atomic runtime implementation is replaced. The changes consist of safety contracts, Kani proof harnesses, verification helpers, and the
safetycontract dependency.Challenge Coverage
Part 1: Unsafe
from_ptrMethodsThis PR adds safety contracts to:
AtomicBool::from_ptrAtomicPtr::from_ptrAtomicI8::from_ptrAtomicU8::from_ptrAtomicI16::from_ptrAtomicU16::from_ptrAtomicI32::from_ptrAtomicU32::from_ptrAtomicI64::from_ptrAtomicU64::from_ptrAtomicI128::from_ptrAtomicU128::from_ptrThe contracts encode the safety obligations of the raw pointer used to construct the atomic reference, including:
Each contract is exercised with
#[kani::proof_for_contract].For the integer atomic types, the harnesses construct symbolic values in raw backing storage and select a valid aligned location from that storage before invoking
from_ptr.For
AtomicPtr<T>::from_ptr, the stored pointer value itself remains arbitrary. Only the outer pointer containing that value is required to point to valid storage, matching the distinction made by the Challenge 7 specification.The required representative pointee sizes for
AtomicPtr<T>are covered with:()u8u16u32[u8; 3]This directly covers the Challenge 7 requirement for pointee sizes 0, 1, 2, 4, and at least one non-power-of-two size.
Part 2: Unsafe Atomic Operation Helpers
Safety contracts are added to the internal unsafe helpers used to implement atomic operations, including:
atomic_storeatomic_loadatomic_swapatomic_addatomic_subatomic_compare_exchangeatomic_compare_exchange_weakatomic_andatomic_nandatomic_oratomic_xoratomic_maxatomic_minatomic_umaxatomic_uminThe contracts describe the local memory obligations of each operation.
For example:
kani::modifies;Ordering restrictions are also encoded where the internal helper accepts a runtime
Ordering:Relaxed,Acquire, orSeqCst;Relaxed,Release, orSeqCst;Relaxed,Acquire, orSeqCst.The proof harnesses instantiate these helpers over the atomic integer widths available on the target and, where applicable, pointer-sized integers and arbitrary pointer values. Platform-specific cases are guarded by the corresponding
target_has_atomic/target_has_atomic_load_storeconfiguration.This keeps the verification aligned with the atomic capabilities exposed by each compilation target instead of assuming that every width is universally available.
Optional: Safe-API Ordering Contracts
Challenge 7 lists contracts preventing invalid atomic orderings as optional because an invalid ordering causes a panic rather than undefined behavior.
This PR includes that additional layer.
Ordering contracts are added to safe atomic APIs such as
load,store, compare-exchange operations, and update-style APIs so their documented restrictions are explicit in the verification interface.In particular:
loadexcludesReleaseandAcqRel;storeexcludesAcquireandAcqRel;ReleaseandAcqRel;Dedicated proof harnesses exercise the safe abstractions with valid symbolic orderings across
AtomicBool, integer atomics, pointer-sized atomics, andAtomicPtrwhere applicable.The verification module also contains explicit panic-oriented cases for invalid compare-exchange/update failure orderings, documenting the distinction between the safe contract domain and the underlying panic behavior.
Part 3: Atomic Intrinsics
The Challenge 7 intrinsic list contains ordering-specific compiler intrinsics for:
A direct contract cannot currently be attached to the bodyless
rustc_intrinsicdeclarations in a way usable by Kani's function-contract machinery.To work around that limitation without changing the intrinsic implementation, this PR introduces small Kani-facing const-generic wrappers around the atomic intrinsics.
The wrappers carry the same local memory obligations as the operations they represent:
The wrappers exist solely as verification targets; the underlying atomic intrinsic remains the operation being executed.
Ordering coverage
The proof harnesses instantiate every ordering required by the Challenge 7 intrinsic table:
For compare exchange and weak compare exchange, all 15 legal success/failure pairs listed by the challenge are instantiated independently rather than replacing them with a single representative ordering.
Each intrinsic proof also contains a reachability witness after the call, preventing an unsatisfiable contract domain from making the proof silently vacuous.
Type coverage
The intrinsic harness macros instantiate the operation families according to the target's supported atomic widths.
Depending on the operation, this includes:
i8/u8i16/u16i32/u32i64/u64i128/u128isize/usizeSigned extrema and unsigned extrema are intentionally instantiated over their respective signed and unsigned type families.
Verification Strategy
Memory predicates
The contracts use the repository/Kani memory predicates to express the pointer-side UB obligations required by the challenge.
In particular, the contracts distinguish between:
This is important for atomics because an arbitrary numeric or pointer value is valid input, while the memory location through which the atomic access occurs still has strict validity, initialization, and alignment requirements.
Symbolic values
The operation operands and initial atomic values are symbolic wherever possible.
For
AtomicPtr, pointer payloads are constructed from arbitrary addresses rather than restricting them to dereferenceable pointers. Atomic pointer operations manipulate the pointer value; they do not dereference the pointee.Platform-specific atomic support
Proof generation follows Rust's target configuration using:
target_has_atomic_load_storetarget_has_atomicThis ensures that the proof suite does not instantiate an atomic width on targets where that atomic operation is unavailable.
Non-vacuity
The proof harnesses use
kani::coverafter the verified call so that the expected call path is demonstrated to be reachable.This provides an explicit check against accidentally proving a contract only because its preconditions are impossible to satisfy.
Scope and Limitations
Intrinsic contracts use wrappers
Kani currently cannot attach usable function contracts directly to the bodyless
rustc_intrinsicdeclarations targeted by this challenge.The const-generic wrappers in
core::intrinsics::verifytherefore serve as the Kani contract boundary.They do not replace or reimplement the atomic operation: their bodies immediately call the corresponding intrinsic.
Generic types
The intrinsic declarations themselves are generic, but Kani ultimately verifies concrete monomorphizations.
The harness macros therefore instantiate the integer, pointer-sized, signed, unsigned, and pointer cases permitted by each intrinsic and by the target configuration.
For
AtomicPtr::from_ptr, the specific representative pointee sizes are exactly the coverage permitted by Challenge 7.Data Races
The current Kani harnesses are single-threaded and do not model concurrent thread interleavings. Consequently, this PR does not claim to discharge the Challenge 7 data-race obligation or prove the absence of races.
Verification
All added Challenge 7 harnesses pass locally with Kani.
Resolves #83
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.