Challenge 4: Verify memory safety of BTreeMap node module with Kani - #684
Challenge 4: Verify memory safety of BTreeMap node module with Kani#684v3risec wants to merge 1 commit into
Conversation
feliperodri
left a comment
There was a problem hiding this comment.
Thanks @v3risec. Reviewed against Challenge 4. This is much stronger than the other Ch4 attempts — 26/26 listed base fns each with a dedicated harness, all 8 recursion/balancing fns present, symbolic occupancy (len = kani::any_where(|l| *l <= CAPACITY) with symbolic keys/values built through the real push path), and #[kani::unwind(13)] fully unwinds the ≤12-edge relink loops — so for the CAPACITY-bounded loops a full-occupancy symbolic harness is genuinely complete. Clean soundness: no cfg body swaps, no trivial invariants, the one contract (LeafNode::init) has a matching proof_for_contract, no assume-the-conclusion.
Requesting changes on two real coverage gaps versus the criteria:
insert_recursingrecursion is never exercised.harness_insert_recursinginserts into an empty leaf root with spare capacity, soself.insertreturns(None, handle)and returns before theloop(node.rs:1068) — your own comment says "completed without splitting." The recursive/splitting path (the whole point of listing it as an unbounded fn) is dead. Please drive a full node that forces a split and ascends.- Balancing fns cover the leaf arm only.
do_merge,merge_tracking_child_edge,steal_left/right,bulk_steal_left/rightbuildnew_leafchildren, so the internal-node edge-array-copy + parent-relink arm is never taken. (#666's full-occupancy internaldo_mergeharness is worth borrowing for this.)
This is the prioritized Ch4 solution — close those two gaps (recursion + internal-node balancing arms) and it's approvable. Nice work on the symbolic-occupancy fixtures.
Summary
This PR adds Kani-based verification artifacts for operations in
library/alloc/src/collections/btree/node.rsfor Challenge 4.The change introduces:
LeafNode::init, including writable-memory preconditions andinitialization postconditions;
NodeRef,Handle, andBalancingContextoperations;and insertion paths used by the implementation;
their legal capacity ranges;
kani::coverproperties for the verified operation paths;No non-verification runtime behavior is changed in normal builds.
Verification Coverage Report
Initialization and basic node operations
The verification module includes harnesses for:
LeafNode::initLeafNode::newInternalNode::newNodeRef::as_internal_mutNodeRef::lenNodeRef::first_edgeNodeRef::last_edgeNodeRef::first_kvNodeRef::last_kvNodeRef::into_leafNodeRef::keysNodeRef::as_leaf_mutNodeRef::into_leaf_mutNodeRef::as_leaf_dyingNodeRef::pop_internal_levelNodeRef::pushRecursive and balancing operations
The following Challenge 4 operations have bounded harnesses:
NodeRef::new_internalHandle::insert_recursingBalancingContext::do_mergeBalancingContext::merge_tracking_child_edgeBalancingContext::steal_leftBalancingContext::steal_rightBalancingContext::bulk_steal_leftBalancingContext::bulk_steal_rightThe balancing harnesses use a shared fixture containing an internal parent
with two leaf children. Child lengths are symbolic over the node capacity
range, and each operation constrains its inputs according to the operation's
preconditions:
left_len + 1 + right_len <= CAPACITY;steal_leftrequires a non-empty left child and room in the right child;steal_rightrequires a non-empty right child and room in the left child;and destination capacity;
Approach
The verification strategy combines contracts with executable bounded harnesses.
Contracts for unsafe initialization
LeafNode::initis verified throughproof_for_contract, checking thatwrites target valid memory and establish the required metadata.
Shared symbolic fixtures
Nodes are constructed through
new_leaf,new_internal, andpush.This preserves the implementation's initialization and parent-linking
behavior instead of fabricating arbitrary raw pointers.
Behavioral postconditions
Harnesses check metadata, pointer relationships, node lengths, returned
handles, and parent links.
kani::coveris placed after the assertions toshow that the target path is reachable.
Bounded verification of recursive operations
Recursive and loop-heavy operations use bounded unwinding. The harnesses
cover symbolic legal node occupancies and operation parameters, while
keeping the fixed node capacity explicit.
Verification
All added Challenge 4 harnesses pass locally with Kani.
Resolves #77
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.