rocq/theories/MemLogic.v
Blocks of memory in the program logic of Logic.v, and the triples of libc's memory calls derived from Campaign 4's contracts.
A block is owned piece by piece: bytes b o xs, the bytes xs from offset o of the object b, and auxes b o cs, the capabilities cs in its aux slots from the same offset. A write by libc into a block needs both: memset clears every pointer slot it touches, a copy moves pointer slots, and a pointer slot is an aux piece.
Each triple is a contract read as a rule, stated at any call site:
wp_memset, frommemset_contract: owning the object's life and a block of n bytes from an 8-aligned offset, with their aux slots, the call fills the bytes with c's low byte and empties every slot that starts in the block (clear_slots);wp_memcpy, frommemcpy_contract: owning a destination block and a source block in another object, of the same length, both 8-aligned, so in phase, the destination takes the source's bytes, and each of its slots the source's when the copy covers all 8 of its bytes, or empties (copy_slots); the source is handed back;wp_malloc, frommalloc_contract: the fresh object's pieces (obj); no null case, since the selected malloc refuses with a resource ending, which is not a return;wp_strdup, fromstrdup_contract: owning astring, the call returns a fresh object owning a copy of it as astring.wp_string_copy, fromstring_copy_contract, withwp_strcpyandwp_stpcpyits instances: owning an object's life and bytes, one more than astring's in another object, the call makes them astringholding the copy and binds d to the destination or, for stpcpy, its NUL; it keeps every aux slot, so none is owned, and needs no alignment (copy_string_after);wp_memchrandwp_memchr_string, frommemchr_contract: a search of an owned block, or a search for the NUL of an ownedstringwith any bound, since memchr reads only through its first match (memchr_input_bounded,nul_found).
The contracts' exact destination frame (memset_frame, shared by the copies) keeps every piece outside the written block (block_frame, through Heap.v's frame_within); for strdup, whose writes lie in its fresh object, frame_other keeps every other object's pieces. The blocks' offsets are 8-aligned so that the slots a write touches start inside them; the contracts also allow unaligned starts, whose first touched slot begins before them.
Aux pieces
The pieces of the aux slots holding cs from offset o of the object b.
The first slot's piece, then the rest's.
Explore proof 5 tactics
Initial goal
unfold aux_ps
cbn [ length ]
rewrite Nat2Z.inj_succ seqZ_cons ; | lia
rewrite Z.pred_succ
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The pieces of cs ++ ds from o are cs's, then ds's after them.
Explore proof 7 tactics
Initial goal
revert o
induction cs as | c cs IH ; intros o ; cbn ; rewrite Z.add_0_r ; reflexivity |
cbn [ app ]
rewrite ! aux_ps_cons IH
cbn [ app length ]
replace o + Z.of_natSlengthcs with o + 1 + Z.of_natlengthcs by lia
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The pieces of n slots holding c, from o.
Explore proof 5 tactics
Initial goal
revert o
induction n as | n IH ; intros o ; reflexivity |
rewrite replicate_S aux_ps_cons IH Nat2Z.inj_succ seqZ_conso ; | lia
rewrite Z.pred_succ
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The pieces of n bytes holding x, from o.
Explore proof 5 tactics
Initial goal
revert o
induction n as | n IH ; intros o ; reflexivity |
rewrite replicate_S byte_ps_cons IH Nat2Z.inj_succ seqZ_conso ; | lia
rewrite Z.pred_succ
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The pieces of cs are aux pieces at the offsets from o.
Explore proof 9 tactics
Initial goal
revert o
induction cs as | c cs IH ; intros o ; reflexivity |
rewrite aux_ps_cons
cbn [ fmap list_fmap fst ]
rewrite IH
cbn [ length ]
rewrite Nat2Z.inj_succ seqZ_conso ; | lia
rewrite Z.pred_succ
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A byte piece of xs is the i-th byte's, at offset o + i.
Explore proof 7 tactics
Initial goal
intros H
apply list_elem_of_lookup in H as k Hk
unfold byte_ps in Hk
apply lookup_zip_with_Some in Hk as i & x & E & Hi & Hx
apply lookup_seqZ in Hi as → _
injection E as → →
eauto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
An aux piece of cs is the i-th slot's, at offset o + i.
Explore proof 7 tactics
Initial goal
intros H
apply list_elem_of_lookup in H as k Hk
unfold aux_ps in Hk
apply lookup_zip_with_Some in Hk as i & c & E & Hi & Hc
apply lookup_seqZ in Hi as → _
injection E as → →
eauto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The i-th slot's piece is among the keys of cs's pieces.
Explore proof 5 tactics
Initial goal
intros Hi
rewrite aux_ps_keys
apply list_elem_of_fmap_2
apply elem_of_seqZ
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The i-th byte's piece is among the keys of xs's pieces.
Explore proof 5 tactics
Initial goal
intros Hi
rewrite byte_ps_keys
apply list_elem_of_fmap_2
apply elem_of_seqZ
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Integer stores
The n little-endian bytes of the integer y.
As many bytes as asked for.
Explore proof 3 tactics
A store of the integer y over the bytes xs from offset o of the live object b: afterwards they are y's little-endian bytes, and every piece outside them keeps its agreement.
Explore proof 37 tactics
Initial goal
intros Hd Hl Hx
assert Hbyte : ∀i, i < lengthxs%nat → registeredm b ∧ 0 ≤ o + Z.of_nati ∧ b + o + Z.of_nati < r_endreg
2 openfocused goal
{intros i Hi
2 opendeferred goal
destruct lookup_lt_is_Some_2xs i Hi as x Hxi
2 opendeferred goal
destruct byte_ps_agree_ _ _ _ _ _ _ Hl Hx Hxi as _ & H1 & H2
2 opendeferred goal
split ; destruct Hl as E _ ; unfold registered ; congruence | lia
deferred goal
split
2 openfocused goal
–apply all_agree_intro
2 opendeferred goal
intros l v Hin
2 opendeferred goal
apply byte_ps_elem in Hin as i & x & Hxi & → & →
2 opendeferred goal
unfold le_bytes in Hxi
2 opendeferred goal
apply list_lookup_fmap_Some in Hxi as k & → & Hk
2 opendeferred goal
apply lookup_seq in Hk as → Hi
2 opendeferred goal
cbn [ Nat.add ]
2 opendeferred goal
destruct Hbytei Hi as Hreg & H1 & H2
2 opendeferred goal
cbn [ agree_piece ]
2 opendeferred goal
split ; exact Hreg |
2 opendeferred goal
intros reg′ E L
2 opendeferred goal
destruct Hl as E0 _
2 opendeferred goal
rewrite regions_store_bytes E0 in E
2 opendeferred goal
injection E as ←
2 opendeferred goal
split ; exact H1 |
2 opendeferred goal
split ; exact H2 |
2 opendeferred goal
replace b + o + Z.of_nati with b + o + Z.of_nati by lia
2 opendeferred goal
apply stored_byte
2 opendeferred goal
lia
deferred goal
–apply frame_within_ _ _ b reg ; reflexivity | exact Hd | exact Hl | |
2 openfocused goal
+intros a H
2 opendeferred goal
apply store_bytes_other
2 opendeferred goal
intros Ha
2 opendeferred goal
destruct HbyteZ.to_nata − b − o ltac:lia as _ & H1 & H2
2 opendeferred goal
apply H ; lia |
2 opendeferred goal
replace a − b with o + a − b − o by lia
2 opendeferred goal
apply byte_ps_key
2 opendeferred goal
lia
deferred goal
+intros a _
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Blocks
The pieces of a block: the life of the object b with the region reg, the bytes xs and the aux slots cs from offset o.
Blocks of the same lengths have the same keys.
Explore proof 6 tactics
Initial goal
intros Hy Hd
unfold block_ps
cbn [ fmap list_fmap fst ]
rewrite ! fmap_app
rewrite ! byte_ps_keys ! aux_ps_keys Hy Hd
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The slots cs from offset o once memset has touched each: those that start at a multiple of 8 are empty, the rest are as they were. For an object at a multiple of 8, these are the slots the contract clears.
Clearing keeps the number of slots.
Explore proof 3 tactics
Initial goal
unfold clear_slots
rewrite length_zip_with length_seqZ Nat2Z.id
(length cs `min` length cs)%nat = length cslia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The i-th cleared slot.
Explore proof 5 tactics
Initial goal
unfold clear_slots
intros H
apply lookup_zip_with_Some in H as k & c0 & → & Hk & Hc
apply lookup_seqZ in Hk as → _
eauto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
memset's exact frame (memset_frame) on the n bytes xs of the block at offset o of the live object b, at a multiple of 8, with its slots cs, keeps the agreement of every piece outside the block. memset and the copies share it.
Proof 46 steps Qed
A memset of the n bytes xs of the block at offset o of the live object b, at a multiple of 8, with its slots cs: afterwards its bytes are c's low byte and its slots clear_slots, and every piece outside it keeps its agreement (frame_ok).
Proof 76 steps Qed
The i-th byte and slot lie in b's bounds.
The slots cs of a destination block after a copy of n bytes from a source block whose slots are ds, both blocks at multiples of 8, so in phase: a slot at a multiple of 8 takes the source's if all 8 of its bytes were copied, and empties if only some were; the others are as they were.
Copying keeps the number of slots.
Explore proof 4 tactics
Initial goal
intros H
unfold copy_slots
rewrite length_zip_with length_seqZ Nat2Z.id length_zip
(length cs `min` (length ds `min` length cs))%nat = length cslia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The i-th copied slot.
Explore proof 8 tactics
Initial goal
unfold copy_slots
intros H
apply lookup_zip_with_Some in H as k & d0 c0 & → & Hk & Hdc
apply lookup_seqZ in Hk as → _
apply lookup_zip_with_Some in Hdc as x & y & E & Hd & Hc
injection E as → →
exists x, y
auto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
What a copy writes into the slot at offset i of a destination block at b + o, from a source block at b' + o', all at multiples of 8, for i a multiple of 8: the source's slot if the copy covers all 8 bytes, else nothing (copy_slot).
Explore proof 16 tactics
Initial goal
intros Hb Ho Hb′ Ho′ Hi Hi8
unfold copy_slot , copy_full_slot
cbn [ ptr_addr ]
replace b + o ≤? b + o + i with true by symmetry ; apply Z.leb_le ; lia
replace b + o + i + 8 ≤? b + o + n with i + 8 ≤? n by destruct Z.leb_speci + 8 n , Z.leb_specb + o + i + 8 b + o + n ; lia
replace b′ + o′ + b + o + i − b + o with b′ + o′ + i by lia
replace b′ + o′ + i mod 8 =? 0 with true
2 open–destruct i + 8 ≤? n ; reflexivity
–symmetry
apply Z.eqb_eq
pose proof Z.div_modb′ 8 ltac:lia
pose proof Z.div_modo′ 8 ltac:lia
pose proof Z.div_modi 8 ltac:lia
pose proof Z.div_modb′ + o′ + i 8 ltac:lia
pose proof Z.mod_pos_boundb′ + o′ + i 8 ltac:lia
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A copy of the n bytes ys of a source block at offset o' of the live object b' into the block at offset o of the live object b, both at multiples of 8: afterwards the destination's bytes are ys and its slots copy_slots, and every piece outside it keeps its agreement (block_frame).
Proof 81 steps Qed
The i-th byte and slot of the destination lie in b's bounds.
A change that keeps every region, and every byte and aux slot outside the live object b, keeps the agreement of every piece of another object: frame_within with every piece of b as keys.
Explore proof 23 tactics
Initial goal
intros Hr Hd Hl Hout Hid
set ks := PByteb <$> seqZ0 r_endreg − b ++ PAuxb <$> seqZ0 r_endreg − b
apply frame_withinks m m′ b reg Hr Hd Hl
3 openfocused goal
focused goal
–intros a H
3 opendeferred goal
deferred goal
apply Hout
3 opendeferred goal
deferred goal
intros Ha
3 opendeferred goal
deferred goal
apply HHa
3 opendeferred goal
deferred goal
apply elem_of_app
3 opendeferred goal
deferred goal
left
3 opendeferred goal
deferred goal
apply list_elem_of_fmap_2
3 opendeferred goal
deferred goal
apply elem_of_seqZ
3 opendeferred goal
deferred goal
lia
2 opendeferred goal
deferred goal
–intros a H
2 opendeferred goal
apply Hout
2 opendeferred goal
intros Ha
2 opendeferred goal
apply HHa
2 opendeferred goal
apply elem_of_app
2 opendeferred goal
right
2 opendeferred goal
apply list_elem_of_fmap_2
2 opendeferred goal
apply elem_of_seqZ
2 opendeferred goal
lia
deferred goal
–intros Hk
apply elem_of_app in Hk as Hk | Hk ; apply list_elem_of_fmap in Hk as o & → & _ ; exact HidI eq_refl
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A copy of the string ys at src, with its NUL, over the bytes xs from offset o of the live object b: afterwards those bytes are ys and the NUL, and every piece outside them keeps its agreement (frame_ok). A scalar copy keeps every aux slot.
Proof 41 steps Qed
A block of the bytes xs from offset o of the live object b, in its bounds and each a byte, is readable_bytes through b's read-write or read-only capability c, and holds xs.
Explore proof 18 tactics
Initial goal
intros Hl Hx Hfit Hc Ho Hend Hy
destruct Hfitb reg proj1Hl as Hb0 Hfit′
assert Hv : ∀i x, xs !! i = Somex → m_bytesm b + o + Z.of_nati = x
2 openfocused goal
{intros i x Hi
2 opendeferred goal
destruct byte_ps_agree_ _ _ _ _ _ _ Hl Hx Hi as Hv & _ & _
2 opendeferred goal
replace b + o + Z.of_nati with b + o + Z.of_nati by lia
2 opendeferred goal
exact Hv
deferred goal
split ; | exact Hv
split ; lia |
split ; exists b, reg ; split ; exact Hc | exact Hl |
split
2 open–apply access_read_ b reg ; exact Hc | exact proj1Hl | cbn ; lia | cbn ; lia | apply Z.mod_1_r
–intros i Hi
cbn [ ptr_addr ]
destruct lookup_lt_is_Some_2xs Z.to_nati ltac:lia as x Hxi
rewrite <- Z2Nat.idi by lia
rewrite Hv_ _ Hxi
exact Forall_lookup_1_ _ _ _ Hy Hxi
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Searching a string for its NUL
A search for the NUL reads a bounded_length string through its NUL when that lies before the bound, and the whole bound otherwise. So memchr_input holds for an int c whose low byte is the NUL, with nothing asked of the bytes after the NUL.
Explore proof 22 tactics
Initial goal
intros Hc H0 Hn & Hk & Hlife & Ha & Hpos & Hnul
split ; exact Hc |
split ; exact Hn |
exists k
split ; exact Hk |
rewrite H0
split
2 openfocused goal
–split ; destruct k <? n eqn: E ; apply Z.ltb_lt in E | apply Z.ltb_ge in E ; lia |
2 opendeferred goal
split ; exact Hlife |
2 opendeferred goal
split ; exact Ha |
2 opendeferred goal
destruct k <? n eqn: E ; intros i Hi
3 openfocused goal
deferred goal
+apply Z.ltb_lt in E
3 opendeferred goal
deferred goal
destruct Z.lt_ge_casesi k as Hik | Hik ; specialize Hposi ltac:lia ; lia |
3 opendeferred goal
deferred goal
replace i with k by lia
3 opendeferred goal
deferred goal
rewrite HnulE
3 opendeferred goal
deferred goal
lia
2 opendeferred goal
deferred goal
+specialize Hposi ltac:lia
2 opendeferred goal
lia
deferred goal
–split ; | exact Hnul
intros i Hi
specialize Hposi Hi
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
What a search for the NUL bounded by n finds in the string at p with contents bytes: its NUL, with p's capability, when the string is shorter than n, and null otherwise.
Where the string's pieces agree, memchr's memchr_result for an int whose low byte is the NUL is nul_found: no byte before the NUL matches, and the NUL does.
Explore proof 34 tactics
Initial goal
intros Hok Ha H0 Hq
pose proof string_bytesm p b reg bytes Hok Ha as Hb
pose proof Hok as _ & _ & _ & _ & _ & Hf
unfold memchr_result in Hq
rewrite H0 in Hq
unfold nul_found
assert Hnz : ∀i, 0 ≤ i < Z.of_natlengthbytes → m_bytesm ptr_addrp + i ≠ 0
2 openfocused goal
{intros i Hi
2 opendeferred goal
destruct lookup_lt_is_Some_2bytes Z.to_nati ltac:lia as x Hx
2 opendeferred goal
rewrite <- Z2Nat.idi by lia
2 opendeferred goal
rewrite HbZ.to_nati x ; | by apply lookup_app_l_Some
2 opendeferred goal
pose proof Forall_lookup_1_ _ _ _ Hf Hx as Hx0
2 opendeferred goal
cbv beta in Hx0
2 opendeferred goal
lia
deferred goal
assert Hz : m_bytesm ptr_addrp + Z.of_natlengthbytes = 0
2 openfocused goal
{apply Hblengthbytes 0
2 opendeferred goal
rewrite lookup_app_r ; | lia
2 opendeferred goal
rewrite Nat.sub_diag
2 opendeferred goal
reflexivity
deferred goal
destruct Hq as k & Hk & Hvk & Hbefore & → | Hmiss & →
2 openfocused goal
–assert Hkl : k = Z.of_natlengthbytes
3 openfocused goal
deferred goal
{destruct Z.lt_trichotomyk Z.of_natlengthbytes as Hl | Hl | Hl
5 openfocused goal
focused goal
deferred goal
deferred goal
–exfalso
5 opendeferred goal
deferred goal
deferred goal
deferred goal
apply Hnzk ; lia | exact Hvk
4 opendeferred goal
deferred goal
deferred goal
deferred goal
–exact Hl
3 opendeferred goal
deferred goal
deferred goal
–exfalso
3 opendeferred goal
deferred goal
apply HbeforeZ.of_natlengthbytes ; lia | exact Hz
2 opendeferred goal
deferred goal
subst k
2 opendeferred goal
rewrite proj2Z.ltb_lt_ _ proj2Hk
2 opendeferred goal
reflexivity
deferred goal
–destruct Z.of_natlengthbytes <? n eqn: E ; | reflexivity
apply Z.ltb_lt in E
exfalso
apply HmissZ.of_natlengthbytes ; lia | exact Hz
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The memory and aux triples over any runtime R.
bytes b o xs: owning the bytes xs from offset o of the object b.
auxes b o cs: owning the aux slots holding cs from offset o of the object b.
A block's pieces are its life, bytes and slots.
Explore proof 2 tactics
Owning the bytes xs ++ ys from offset o is owning xs there and ys after them.
Explore proof 2 tactics
Initial goal
rewrite / bytes byte_ps_app
apply pts_app
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Owning the slots cs ++ ds from offset o is owning cs there and ds after them.
Explore proof 2 tactics
Initial goal
rewrite / auxes aux_ps_app
apply pts_app
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Owning the bytes xs from o is owning their first n and the rest.
Explore proof 4 tactics
Owning the slots cs from o is owning their first n and the rest.
Explore proof 4 tactics
A fresh object's pieces are its life, a zero byte at each offset of its extent and an empty slot at each: a block from offset 0.
Explore proof 4 tactics
Initial goal
intros H
rewrite / obj / obj_ps / live / bytes / auxes byte_ps_replicate aux_ps_replicate Z2Nat.id ; | exact H
rewrite / pts big_sepL_cons big_sepL_app big_sepL_singleton
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The life of an object and a string's are of different objects: each is owned once.
Explore proof 7 tactics
Initial goal
rewrite / live / string / string_ps / pts ! big_sepL_cons
iIntros [Hl _] [_ [Hs _]]
iDestruct pointsto_ne with Hl Hs as %Hne
iPureIntro
intros ←
apply Hne
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
An object's life and its bytes from offset o, a string and its NUL, are that string.
Explore proof 7 tactics
Initial goal
intros Hok
rewrite / string / string_ps / live / bytes / pts ! big_sepL_cons big_sepL_nil
cbn [ ptr_addr ]
replace b + o − b with o by lia
iIntros [Hl _] Hx
iSplit ; iPureIntro ; exact Hok |
iFrame
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
A string lends n of its bytes from its k-th on, before its NUL, as a block, with its object's life, and takes them back.
Explore proof 22 tactics
Initial goal
intros Hk
iIntros [%Hok Hs]
assert E : ys ++ 0 = takek ys ++ taken dropk ys ++ dropk + n ys ++ 0
2 openfocused goal
{rewrite app_assoc app_assoc
2 opendeferred goal
f_equal
2 opendeferred goal
rewrite - { 1 } take_dropk ys
2 opendeferred goal
rewrite - app_assoc
2 opendeferred goal
f_equal
2 opendeferred goal
rewrite - { 1 } take_dropn dropk ys drop_drop
2 opendeferred goal
reflexivity
deferred goal
assert Lk : lengthtakek ys = k by rewrite length_take ; lia
rewrite / string_ps E ! byte_ps_app Lk
rewrite / live / bytes / pts big_sepL_cons ! big_sepL_app big_sepL_singleton
iDestruct Hs as (Hl & Hpre & Hw & Hpost)
iSplit ; done |
iFrame Hl Hw
iIntros Hl Hw
rewrite / string
iSplit ; done |
rewrite / string_ps E ! byte_ps_app Lk
rewrite / pts big_sepL_cons ! big_sepL_app
iFrame
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The Iris instance of the registry and wi.
The expressions of the language.
A raw store of the 32-bit int y, which v reads as, through the proven pointer p, which a reads as and which addresses offset o of the live object b at offset off, over the four bytes xs there, where the store keeps the world interpretation: they are now y's four little-endian bytes.
Proof 44 steps Qed
memset's triple, from memset_contract: owning the life of the object b, at a multiple of 8, with the region reg, and the block of the bytes xs and the slots cs from the 8-aligned offset o, inside its bounds, where vp reads as the block's pointer, vc as the int c and vn as its length, and where the fill keeps the world interpretation: the call fills the bytes with c's low byte, clears the slots it touches, and binds d to the block's pointer.
Proof 75 steps Qed
malloc's triple, from malloc_contract: for a size n that v reads as, where an allocation keeps the world interpretation, the call binds d to the base pointer of a fresh heap object of n bytes, rounded up to 16, whose pieces it owns (obj). The selected malloc never returns null: a refusal is a resource ending, which is not a return, so the triple has no null case.
Proof 52 steps Qed
memcpy's triple, from memcpy_contract: owning the destination block (the bytes xs and slots cs from offset o of the live object b) and the source block (the bytes ys and slots ds from offset o' of the live object b', another object, through the capability c', read-write or read-only), of the same length, all at multiples of 8, where vd, vs and vn read as their pointers and length, and where the copy keeps the world interpretation: the destination's bytes become ys, its slots copy_slots, and d is bound to the destination pointer. The source is handed back unchanged.
Proof 108 steps Qed
strdup's triple, from strdup_contract: owning the string at p, which v reads as, in an object at a multiple of 8, the call binds d to the base pointer of a fresh heap object holding a copy of the string, which it owns as a string (its life and the copied bytes; the fresh object's other bytes and its aux slots stay unowned), and hands the original back. A refusal is a resource ending.
Proof 131 steps Qed
The scalar string copies' triple, from string_copy_contract: owning the life of the object b, above 0, and its bytes xs from offset o, one more than the string's, and the string ys at src, where vd reads as the bytes' pointer and vs as src, and where the copy keeps the world interpretation: the bytes become the string and its NUL, a string the call hands over, and d is bound to the destination pointer, or for stpcpy (copy_return) to its NUL. The source is handed back. The two objects differ, since each life is owned once (live_string_ne), so the bytes do not overlap; no aux slot needs owning, since a scalar copy keeps them all.
Proof 93 steps Qed
strcpy's triple: wp_string_copy, binding d to the destination.
Explore proof 3 tactics
Initial goal
intros Hreg Hfn Hdata Hpd Hps Hb0 Ho Hend Hlen Hwi
iIntros Hl Hx Hs HK
iApply wp_string_copyfalse TAtom"strcpy" with Hl Hx Hs HK ; done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
stpcpy's triple: wp_string_copy, binding d to the copy's NUL.
Explore proof 3 tactics
Initial goal
intros Hreg Hfn Hdata Hpd Hps Hb0 Ho Hend Hlen Hwi
iIntros Hl Hx Hs HK
iApply wp_string_copytrue TAtom"stpcpy" with Hl Hx Hs HK ; done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
memcmp's triple, from memcmp_contract: owning the bytes xs from offset o of the live object b and the bytes ys, as many, from offset o' of the live object b', through read-write or read-only capabilities, where vp, vq and vn read as their pointers and length, the call binds d to an int. Which int does not matter to a caller that only branches on it.
Proof 28 steps Qed
memchr's triple, from memchr_contract: owning the bytes xs from offset o of the live object b, through a read-write or read-only capability c, where vp and vn read as their pointer and length, the call binds d to what it finds of the int k, which vk reads as (strchr_found of xs: the first byte equal to k's low byte, with c, or null).
Proof 52 steps Qed
memchr's triple on a string, from memchr_contract: owning the string at p, which vp reads as, a search for the int k, which vk reads as and whose low byte is the NUL, bounded by n, which vn reads as, binds d to nul_found p bytes n. The search reads only through the NUL (memchr_input_bounded), so the bound may run past the string's object.
Proof 25 steps Qed
MemLogicOn over a Logic instance of its own.