Challenge 29: Verify the safety of Box and ThinBox in alloc::boxed - #669
Open
kasimte wants to merge 1 commit into
Open
Challenge 29: Verify the safety of Box and ThinBox in alloc::boxed#669kasimte wants to merge 1 commit into
kasimte wants to merge 1 commit 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.
Towards #526. Solves Challenge 29: Safety of boxed. The challenge asks for safety contracts on 9
unsafefunctions and verification of at least 75% of the 46 safe functions; this covers all 9 and all 46, including theThinBox/WithHeaderfamily that has noBoxanalog. Generics are instantiated at primitive types, which the challenge allows, with theGlobalallocator. The work is 74 Kani harnesses in onemod verifyper file, all passing viascripts/run-kani.sh, as a purely additive change to three files. On every one, Kani also checks the challenge's four listed undefined behaviors — access through a dangling or misaligned pointer, misuse of intrinsics, mutation of immutable bytes, and production of an invalid value.The 9 unsafe functions
All nine carry safety contracts, and all nine are verified — six with
proof_for_contract, three by construction. The four raw-pointer constructors —from_raw,from_non_null, and their_invariants — are discharged at a sized instantiation and, for the_inpair, an unsized[u8]one. Bothassume_inits are discharged as well, with the target spelled through the impl's own generic parameters (Box::<MaybeUninit<T>, A>::assume_init): concrete turbofish arguments do not resolve against that impl's structured self-type, but the generic-parameter form does. The remaining three — thedowncast_uncheckeds — are verified by running the real function body on symbolic inputs and asserting the whole postcondition, the returned value and its pointer identity: at this repository's Kani pin,proof_for_contractresolves none of the three same-nameddyn-self impls, across five path spellings including the resolver's own printed implementation forms. Their contracts are annotated and checked in the harness rather than machine-linked, with an in-code note at each site. The root cause is that those impl blocks live in a different module thanBox, so the resolver renders them in an<impl …>path form no spelling can match; a resolver fix developed alongside this work (model-checking/kani#4777, fix in model-checking/kani#4778) verifies all three asproof_for_contracttargets under a patched Kani, and each note marks the mechanical upgrade for a pin that includes it.The challenge's function table also lists
<dyn Error>::downcast_uncheckedthree times, but no such method exists:impl dyn Errorexposes only the safedowncast. The three realdowncast_uncheckeds are onBox<dyn Any (+ Send)(+ Sync), A>, and those are the ones contracted here.The 46 safe functions
Most are heap round-trips — allocate, write, hand out a pointer, reconstruct — where the harness checks that the value and its allocation come back intact. A few carry a property worth verifying on its own terms, and the
ThinBox/WithHeaderfamily is the genuinely new work.new_in/try_new_*,write,into_boxed_slice,into_raw/into_non_null/into_unique/leak,into_pinLayout::arraywill acceptinto_array,from_slice,From<&str>,From<Box<str>>, and bothTryFromsTryFrom<Box<T>>row has no impl in the tree, so the two realTryFromsources stand in for itdowncastonBox<dyn Any…>anddyn Error…Drop,Default,CloneBox<str>clone, which asserts a fresh allocation for a non-empty string and the shared dangling pointer for an empty oneThinBox/WithHeadernew_unsize_zstis proven with no assumptions on a slice-metadata instantiationTwo habits keep the proofs honest. First, covers: each input-bearing harness carries a
kani::coverconfirming it reaches the operation under test rather than passing on a setup that silently failed, and where a function has two reachable outcomes, both arms are covered — so no proof passes while checking a dead or unreachable path. No harness constrains its inputs withkani::assume; every input restriction is a visibleany_wheredomain. Second, panics are proven rather than assumed away:should_panicharnesses send a panicking-drop sentinel throughBox<T>andBox<[T]>drop glue, and drive all four non-tryslice constructors pastisize::MAXinto their capacity-overflow guard.What this Kani pin can't reach
Two behaviors sit outside the model here, both noted in-code where they occur. Allocation never fails in Kani, so the
Errarm of everytry_new*is unreachable — those harnesses verify the success arm and mark the dead branch rather than covering it. Andnew_unsize_zst'sdyn Anyform fails inside itsconst-allocated metadata block, on a missingdrop_in_place::<dyn Any>and pointer-liveness checks on the const pointer, so that function is proven on its slice-metadata route instead. The last overflow guard, inWithHeaderlayout arithmetic, is reachable only by a near-isize::MAXtype and is covered through the slice constructors above.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.