rocq/theories/SlotLogic.v
rocq/programs/slot.c through the program logic of Logic.v: a callee that owns memory across calls, in the shape of gnulib's quotearg.
fill keeps one heap buffer in its static slot, with its size in slot_size. When a request does not fit it frees the buffer and mallocs a bigger one; then it memsets the request, terminates it, and returns the buffer. The caller may read the buffer until its next call of fill, which reuses it or frees it.
That ownership is the module invariant slot_inv: the two static objects, the pointer and size they hold, and the buffer's object with all its bytes and aux slots (buffer): null and size 0 before the first call. fill's triple (fill_spec) takes the invariant and lends the filled bytes to the caller (lent), with a wand that, given them back, gives the invariant back. The caller cannot keep the bytes across the next call: the next call needs the invariant, which needs them.
The grow path uses wp_free on the old buffer, whose life token then ends, and wp_malloc, whose fresh object becomes the new buffer (obj_block); both paths then go through the fill in fill_L6, with wp_memset and a byte store. use calls fill three times, through wp_call_bind and fill_spec, and reads the buffer after each call (use_spec).
slot_logic_safe is the labelled machine's safety from any memory whose static objects hold null and 0, over any runtime, whose free succeeds on null and the buffer (R.free_succeeds), and interp_slot_logic_safe instantiates it from the module's initial memory under the interpreter's runtime.
Integer arithmetic on an unknown stays folded when a premise is computed (Symbolic.v).
The module
The checked module nanofil makes of slot.c (slot_term).
The fixture decodes to slot_module.
Explore proof 2 tactics
Initial goal
vm_compute
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The module's i-th function, or an empty one.
fill: grow the buffer if need be, fill it, lend it.
use: three calls of fill, each read after it.
The identity of the static slot, which holds the buffer.
The identity of the static slot_size, which holds its size.
slot's region.
slot_size's region.
The module's symbols.
Laying out the module gives slot_globals.
Explore proof 2 tactics
Initial goal
vm_compute
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Block l of f, or an empty one.
fill's entry: does the request fit?
The grow path: free the old buffer, malloc a new one.
The fill: memset, the terminator, return the buffer.
use's one block.
fill has its entry.
Explore proof 2 tactics
Initial goal
vm_compute
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
fill has its grow path.
Explore proof 2 tactics
Initial goal
vm_compute
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
fill has its fill.
Explore proof 2 tactics
Initial goal
vm_compute
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
use has its block.
Explore proof 2 tactics
Initial goal
vm_compute
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
fill called with the int c and the size n.
A call of fill starts it at fill_entry.
Explore proof 1 tactics
Initial goal
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
use called.
slot.c's contracts: malloc's and memset's, over no world.
A fresh object's pieces, all zero bytes and empty slots, are a block from offset 0 of its whole extent.
Explore proof 6 tactics
Initial goal
intros Hq
unfold obj_ps , block_ps , byte_ps , aux_ps
rewrite ! length_replicate Z2Nat.id ; | exact Hq
rewrite zip_with_replicate_r_eq_ Z.to_natreq_extentq ; | apply length_seqZ
rewrite zip_with_replicate_r_eq_ Z.to_natreq_extentq ; | apply length_seqZ
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Prove an address premise: drop the wraps of raw_address and of a flight's sum where arithmetic shows them the identity, then arithmetic.
Prove a premise about a static object's address: by computation.
Prove a premise that names words: by computation, with the environment's facts.
The pieces a run of use starts owning: the static objects, slot holding null and slot_size holding 0.
No piece of slot_ps0 is listed twice.
Explore proof 4 tactics
Initial goal
cbv [ slot_ps0 cell fmap list_fmap fst map app slot_b size_b ]
cbn [ Z.add ]
repeat apply NoDup_cons_2 ; intros Hin ; repeat apply elem_of_cons in Hin as Hin | Hin ; discriminate Hin | ; apply elem_of_nil in Hin ; exact Hin |
apply NoDup_nil_2
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
slot.c over any runtime R.
The linked genv: the module, its symbols, R's free.
fill is a function of the module.
Explore proof 2 tactics
Initial goal
vm_compute
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The Iris instance of slot.c: malloc's and memset's contracts, no world.
The expressions of slot.c's language.
The module invariant
What slot holding p and slot_size holding sz own: nothing for null and size 0; else the heap object p's capability names, at a multiple of 16, live, with every byte and aux slot of its first length xs bytes, at least sz of them.
The module invariant: both static objects, the pointer and size they hold, and the buffer they describe.
What fill lends its caller: the life of the buffer b and its first n bytes, all c's low byte, with the byte after them inside its bounds; and, for those given back, the module invariant.
Bytes
The bytes ys, then x, then zs, are ys's, x's, then zs's.
Explore proof 3 tactics
Initial goal
rewrite / bytes / ibyte / pts byte_ps_app byte_ps_cons big_sepL_app big_sepL_cons big_sepL_singleton
replace o + Z.of_natlengthys + 1 with o + Z.of_natlengthys + 1 by lia
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The slots cs1 then cs2 are cs1's, then cs2's.
Explore proof 12 tactics
Initial goal
rewrite / auxes / pts - big_sepL_app
f_equiv
revert o
induction cs1 as | c cs1 IH ; intros o
2 openaux_ps b o ([] ++ cs2) ≡ₚ aux_ps b o [] ++ aux_ps b (o + length []) cs2focused goal
–cbn
2 openaux_ps b o cs2 ≡ₚ aux_ps b o [] ++ aux_ps b (o + 0%nat) cs2deferred goal
rewrite Z.add_0_r
2 openaux_ps b o cs2 ≡ₚ aux_ps b o [] ++ aux_ps b o cs2deferred goal
reflexivity
deferred goal
–cbn [ app ]
rewrite ! aux_ps_cons IH
IH :
∀ o : Z,
aux_ps b o (cs1 ++ cs2)
≡ₚ aux_ps b o cs1 ++ aux_ps b (o + length cs1) cs2(PAux b o, VAux c)
:: aux_ps b (o + 1) cs1 ++ aux_ps b (o + 1 + length cs1) cs2
≡ₚ ((PAux b o, VAux c) :: aux_ps b (o + 1) cs1) ++
aux_ps b (o + length (c :: cs1)) cs2cbn [ app length ]
IH :
∀ o : Z,
aux_ps b o (cs1 ++ cs2)
≡ₚ aux_ps b o cs1 ++ aux_ps b (o + length cs1) cs2(PAux b o, VAux c)
:: aux_ps b (o + 1) cs1 ++ aux_ps b (o + 1 + length cs1) cs2
≡ₚ (PAux b o, VAux c)
:: aux_ps b (o + 1) cs1 ++ aux_ps b (o + S (length cs1)) cs2replace o + Z.of_natSlengthcs1 with o + 1 + Z.of_natlengthcs1 by lia
IH :
∀ o : Z,
aux_ps b o (cs1 ++ cs2)
≡ₚ aux_ps b o cs1 ++ aux_ps b (o + length cs1) cs2(PAux b o, VAux c)
:: aux_ps b (o + 1) cs1 ++ aux_ps b (o + 1 + length cs1) cs2
≡ₚ (PAux b o, VAux c)
:: aux_ps b (o + 1) cs1 ++ aux_ps b (o + 1 + length cs1) cs2reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The bytes xs lend their i-th, x, and take it back.
Explore proof 7 tactics
Initial goal
intros Hx
rewrite / bytes / ibyte / pts big_sepL_singleton
iIntros Hs
iDestruct big_sepL_lookup_acc with Hs as [Hb Hk] ; exact byte_ps_lookup_ _ _ _ _ Hx |
iSplitL Hb ; iExact Hb |
iIntros Hb
iApply Hk with Hb
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The buffer
The buffer lends the life of the object its pointer's capability names, if any (live_cap), and takes it back; its pointer's address is in range.
Explore proof 17 tactics
Initial goal
iIntros [[%Hp %Hz] | (%b & %reg & %xs & %cs & %Hf & Hl & Hx & Hcs)]
2 openfocused goal
–iExists slot_reg
2 opendeferred goal
subst p
2 opendeferred goal
iSplit ; iPureIntro ; cbn ; lia |
2 opendeferred goal
iSplit ; done |
2 opendeferred goal
iIntros _
2 opendeferred goal
iLeft
2 opendeferred goal
done
deferred goal
–pose proof Hf as Hpe & Hb & _ & _ & _ & _ & Hend & Hend48
subst p
iExists reg
iSplit ; iPureIntro ; cbn ; lia |
iFrame Hl
iIntros Hl
iRight
iExists b, reg, xs, cs
by iFrame
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A call of free on the buffer's pointer, which v reads as, frees it if it is not null, and gives up the whole buffer.
Explore proof 6 tactics
Initial goal
intros Hv
iIntros [[%Hp _] | (%b & %reg & %xs & %cs & %Hf & Hl & _ & _)] HK
2 open–subst p
2 openiApply wp_free_null with HK ; exact R.free_succeeds | reflexivity | reflexivity | exact Hv
–destruct Hf as → & Hb & _ & Hk & _
iApply wp_free with Hl HK ; exact R.free_succeeds | reflexivity | reflexivity | exact Hv | exact Hk | lia | intros ; exact I
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
fill
The fill, from L6's instructions: owning the static objects holding the buffer b and its size sz, more than n, and the buffer's bytes xs and slots cs: memset its first n bytes, terminate them, and return the buffer, lending the bytes.
Proof 87 steps Qed
Split the buffer at n: the n bytes memset fills, the terminator, the rest.
fill's triple: with the module invariant, called with an int c and a size n below 2^47, it returns the buffer, lending its first n bytes, each c's low byte (lent). From the entry: load the size, and if it is at most n, free the old buffer (wp_free_buffer), malloc n + 1 bytes (wp_malloc) and store the new buffer and its size; then fill_L6.
Proof 93 steps Qed
Too small: free the buffer, malloc n + 1 bytes, and store them.
Big enough: the buffer is not null; fill it.
use is safe and returns an int: it calls fill three times through fill_spec, each time reading a byte of the bytes lent (bytes_byte) and handing them back for the next call.
Proof 69 steps Qed
fill('a', 8), then a7.
fill('b', 3), then b0.
fill('c', 16), then c15, and the sum.
use is safe: from a ready runtime, live objects apart, and a memory whose static objects hold null and 0, with any fuel, no labelled run stops with a failure_ending, and a run that returns returns an int. By heap_labelled_safe from use_spec, the ghost heap starting with slot_ps0.
Explore proof 21 tactics
Initial goal
intros Hr Hd Ha
apply heap_labelled_safeΣ slot_registry no_world slot_ge use_entry m s tt list_to_mapslot_ps0
5 openfocused goal
focused goal
focused goal
focused goal
–exact Hr
4 opendeferred goal
deferred goal
deferred goal
deferred goal
–exact Hd
3 opendeferred goal
deferred goal
deferred goal
–intros l v Hl
3 opendeferred goal
deferred goal
exact all_agree_elem_ _ _ _ Ha elem_of_list_to_map_2_ _ _ Hl
2 opendeferred goal
deferred goal
–exact I
deferred goal
–intros HinvG HheapG
rewrite big_sepM_list_to_map ; | exact slot_ps0_nodup
iIntros Hps
iApply use_spec
rewrite / slot_ps0 / pts big_sepL_cons big_sepL_cons big_sepL_app
iDestruct Hps as (Hls & Hlz & Hp & Hz)
rewrite / slot_inv / live / pts ! big_sepL_singleton
iFrame Hls Hlz
iExists null, 0
iSplit ; iPureIntro ; lia |
iSplitL Hp ; iExact Hp |
iSplitL Hz ; iExact Hz |
iLeft
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The interpreter's runtime
slot.c over the interpreter's runtime.
The module's initial memory: the two static objects, every byte 0 and every aux slot empty.
Laying out the module gives slot_memory.
Explore proof 2 tactics
Initial goal
vm_compute
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The interpreter is ready in slot_memory at its start: both objects lie below it.
Explore proof 6 tactics
Initial goal
split ; vm_compute ; split ; congruence |
split ; | intros a _ ; split ; reflexivity
intros b reg E
change object_regionb size_reg; slot_reg = Somereg in E
cbn - [ Z.eqb ] in E
destruct 65600 =? b eqn: E1 ; | destruct 65568 =? b eqn: E2 ; | discriminate E ; apply Z.eqb_eq in E1 || apply Z.eqb_eq in E2 ; subst b ; injection E as ← ; split ; vm_compute ; split ; congruence | ; split ; vm_compute ; congruence | discriminate
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The two static objects are apart.
Explore proof 5 tactics
Initial goal
intros b1 r1 b2 r2 E1 _ E2 _ Hne
change object_regionb1 size_reg; slot_reg = Somer1 in E1
change object_regionb2 size_reg; slot_reg = Somer2 in E2
cbn - [ Z.eqb ] in E1 , E2
destruct 65600 =? b1 eqn: F1 ; | destruct 65568 =? b1 eqn: G1 ; | discriminate E1 ; destruct 65600 =? b2 eqn: F2 ; | destruct 65568 =? b2 eqn: G2 ; | discriminate E2;repeat match goal withH : _ =? _ = true |- _⇒apply Z.eqb_eq in Hend;injection E1 as ←;injection E2 as ←;cbn;lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
slot_ps0 agrees with slot_memory: its objects are live, every byte is 0 and every aux slot empty.
Explore proof 3 tactics
Initial goal
cbv [ slot_ps0 cell map app slot_b size_b ]
cbn [ all_agree agree_piece fst snd ]
repeat match goal with|- _ ∧ _⇒split|- True⇒exact I|- live_at_ _ _⇒split ; reflexivity|- registered_ _⇒unfold registered ; vm_compute ; discriminate|- in_live_ _ _ _⇒let reg ≔ fresh "reg" in let E ≔ fresh "E" in intros reg E _ ; vm_compute in E ; injection E as ← ; cbn [ r_end ] ; split ; lia | ; split ; lia | ; first left ; reflexivity | reflexivityend
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
use is safe under the interpreter's runtime, from the module's initial memory (slot_initial).
Explore proof 5 tactics
Initial goal
intros fuel
apply InterpSlotLogic.slot_logic_safehiΣ
3 openfocused goal
focused goal
–exact slot_memory_ready
2 opendeferred goal
deferred goal
–exact slot_memory_disjoint
deferred goal
–exact slot_memory_agrees
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.