rocq/theories/StringLogic.v
C strings in the program logic of Logic.v, and the triples of the libc string calls derived from Campaign 4's contracts.
string p b reg bytes owns a C string: the life of the object b that p's capability names, read-write or read-only, and its bytes from p's offset through the terminating NUL (byte_ps). Owning it in an interpreted state gives the contracts' readings of a string, terminated_length and c_string (string_facts). One byte of it can be lent out to a load (string_byte).
The triples are each contract's precondition read from owned pieces, and its postcondition handed back as owned pieces and bound results:
wp_strlenandwp_strchr, fromstrlen_contractandstrchr_contract: pure calls, which change no memory and no world (pure_return), so the state interpretation holds again with the same ghost heap, over any registry and world;wp_puts, fromputs_contract: a call that writes its world's roots and may allocate, under the world interpretationstdio_wi: puts's rep and readiness, and the world's writable roots apart from the program's live objects. After a return, the frame for external calls (Logic.v'sprefix_agrees) keeps the program's pieces and puts's prefix the world (puts_return_frame).
Each triple is stated at any call site, so a program uses it as a rule.
Byte pieces
The pieces of the bytes xs from offset o of the object b.
The first byte's piece, then the rest's.
Explore proof 5 tactics
Initial goal
unfold byte_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 i-th piece is the i-th byte's.
Explore proof 10 tactics
Initial goal
revert o i
induction xs as | y xs IH ; intros o i H ; discriminate |
rewrite byte_ps_cons
destruct i as | i ; cbn in H |- *
2 open–injection H as →
2 openrewrite Z.add_0_r
2 openreflexivity
–rewrite IHo + 1 i H
do 3 f_equal
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Where the pieces of xs from offset o of the live object b agree, each byte is in the object and holds its value.
Explore proof 4 tactics
Initial goal
intros E Lv Ha Hx
pose proof all_agree_elem_ _ _ _ Ha list_elem_of_lookup_2_ _ _ byte_ps_lookupb o xs i x Hx as _ Hin
destruct Hinreg E Lv as Ho & He & Hv
auto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The bytes, nonzero, followed by a NUL, as the memory holds them from a, are a string_at a.
Explore proof 16 tactics
Initial goal
revert a
induction bytes as | y bytes IH ; intros a Hf Hb
2 openfocused goal
–cbn
2 opendeferred goal
rewrite <- Z.add_0_ra
2 opendeferred goal
exact Hb0%nat 0 eq_refl
deferred goal
–apply Forall_cons_1 in Hf as Hy Hf
cbn
split ; exact Hy |
split
2 openfocused goal
+rewrite <- Z.add_0_ra
2 opendeferred goal
exact Hb0%nat y eq_refl
deferred goal
+apply IH ; exact Hf |
intros i x Hx
replace a + 1 + Z.of_nati with a + Z.of_natSi by lia
apply Hb
exact Hx
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The pieces of xs ++ ys are xs's, then ys's after them.
Explore proof 7 tactics
Initial goal
revert o
induction xs as | x xs IH ; intros o ; cbn ; rewrite Z.add_0_r ; reflexivity |
cbn [ app ]
rewrite ! byte_ps_cons IH
cbn [ app length ]
replace o + Z.of_natSlengthxs with o + 1 + Z.of_natlengthxs by lia
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The pieces of xs are byte pieces at the offsets from o.
Explore proof 9 tactics
Initial goal
revert o
induction xs as | x xs IH ; intros o ; reflexivity |
rewrite byte_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
Strings
The shape of a C string at p with contents bytes in the object b with the region reg: p's capability names b, read-write or read-only, at a positive base; the bytes and their NUL lie in the region, which ends within the address space; every byte is nonzero.
The pieces of the string: its object's life and its bytes from p's offset, NUL included.
Where the string's pieces agree, the memory holds its bytes and NUL from p.
Explore proof 5 tactics
Initial goal
intros _ Hl Ha i x Hx
cbn [ fst snd agree_piece ] in Hl
destruct byte_ps_agree_ _ _ _ _ _ _ Hl Ha Hx as Hv _
replace ptr_addrp + Z.of_nati with b + ptr_addrp − b + Z.of_nati by lia
exact Hv
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Where the string's pieces agree, p points at a terminated_length string of its length: Campaign 4's reading of a string.
Explore proof 22 tactics
Initial goal
intros Hok Ha
pose proof string_bytesm p b reg bytes Hok Ha as Hb
destruct Hok as Hc & H0 & H1 & H2 & H3 & Hf
destruct Ha as E Lv _
assert Hn : Z.of_natlengthbytes < 2 ^ 64 − 1 by lia
split ; | exact Hn
split ; lia |
split ; lia |
split ; exists b, reg ; auto |
replace Z.of_natlengthbytes <? 2 ^ 64 − 1 with true by symmetry ; apply Z.ltb_lt ; exact Hn
split ; apply access_read_ b reg ; exact Hc | exact E | lia | lia | apply Z.mod_1_r |
split
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
exact Forall_lookup_1_ _ _ _ Hf Hx
deferred goal
–intros _
apply Hblengthbytes 0
rewrite lookup_app_r ; | lia
rewrite Nat.sub_diag
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Where the string's pieces agree, p points at a c_string with contents bytes: puts's reading of a string.
Explore proof 7 tactics
Initial goal
intros Hok Ha
pose proof string_bytesm p b reg bytes Hok Ha as Hb
pose proof Hok as Hc & _ & H1 & H2 & _ & Hf
destruct Ha as E _ _
split
2 openfocused goal
–apply access_read_ b reg ; exact Hc | exact E | lia | lia | apply Z.mod_1_r
deferred goal
–apply string_at_of ; exact Hf | exact Hb
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
No piece of a string is listed twice.
Explore proof 8 tactics
Initial goal
unfold string_ps
cbn [ fmap list_fmap fst ]
rewrite byte_ps_keys
apply NoDup_cons_2
2 open–rewrite list_elem_of_fmap
2 openintros o & E & _
2 opendiscriminate E
–apply NoDup_fmap_2 ; intros x y E ; injection E as → ; reflexivity | apply NoDup_seqZ
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Every piece of a string is of its object.
Explore proof 8 tactics
Initial goal
unfold string_ps
rewrite elem_of_cons
intros H | H ; injection H as → _ ; reflexivity |
apply list_elem_of_fmap_2fst in H
cbn [ fst ] in H
rewrite byte_ps_keys in H
apply list_elem_of_fmap in H as o & → & _
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Every piece of a string is a piece of memory.
Explore proof 8 tactics
Initial goal
unfold string_ps
rewrite elem_of_cons
intros H | H ; injection H as → _ ; exact I |
apply list_elem_of_fmap_2fst in H
cbn [ fst ] in H
rewrite byte_ps_keys in H
apply list_elem_of_fmap in H as o & → & _
exact I
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The suffix of the string p from its i-th byte, at q: the pieces of the string are its life, the bytes before q, and the pieces of the suffix's bytes, which are the string at q's.
Explore proof 9 tactics
Initial goal
intros Hi q
split ; | reflexivity
unfold string_ps
f_equal
rewrite <- take_dropi bytes at 1
rewrite <- app_assoc , byte_ps_app , length_take
byte_ps b (ptr_addr p - b) (take i bytes) ++
byte_ps b (ptr_addr p - b + (i `min` length bytes)%nat) (drop i bytes ++ [0]) =
byte_ps b (ptr_addr p - b) (take i bytes) ++
byte_ps b (ptr_addr q - b) (drop i bytes ++ [0])cbn [ ptr_addr q ]
byte_ps b (ptr_addr p - b) (take i bytes) ++
byte_ps b (ptr_addr p - b + (i `min` length bytes)%nat) (drop i bytes ++ [0]) =
byte_ps b (ptr_addr p - b) (take i bytes) ++
byte_ps b (ptr_addr p + i - b) (drop i bytes ++ [0])replace ptr_addrp − b + Z.of_natNat.mini lengthbytes with ptr_addrp + Z.of_nati − b by lia
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The suffix of a string from a byte before its NUL is a string.
Explore proof 11 tactics
Initial goal
intros Hi Hc & H0 & H1 & H2 & H3 & Hf
unfold string_ok
cbn [ ptr_addr ptr_cap ]
rewrite length_drop
split ; exact Hc |
split ; exact H0 |
split ; lia |
split ; lia |
split ; exact H3 |
apply Forall_drop
exact Hf
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
The stdio world
The world interpretation of a program that calls puts: puts's representation rep and stdio_ready of the world w in m, and the world's writable roots apart from the live objects owning pieces of the ghost heap h.
C strings and the string calls over any runtime R.
A safepoint keeps stdio_wi where it keeps rep and readiness: the roots stay apart since no region changes (collector_live).
Explore proof 14 tactics
Initial goal
intros Hst
split
intros s m s′ m′ w h Hsp Hr & Hw & Ha
destruct Hst_ _ _ _ _ Hsp Hr Hw as Hr′ Hw′
split ; exact Hr′ |
split ; exact Hw′ |
intros sp a Hin Hs l & v & reg & Hl & Hm & E Hlive & Hb
apply Hasp a Hin Hs
rewrite R.collector_live_ _ _ _ Hsp in E
exists l, v, reg
split ; exact Hl |
split ; exact Hm |
split ; | exact Hb
split ; exact E | exact Hlive
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A puts call returns one int.
Explore proof 7 tactics
Initial goal
intros Hi
remember CallReturnvalues as en eqn: Een
destruct Hi as ? ? ? ? ? ? _ _ _ Hp | ? ? ? ? ? ? _ _ _ _ _ ; | discriminate Een
destruct Hp as p & bytes & acc & tail & n & _ & _ & _ & _ & _ & Hend & _
subst
injection Hend as →
eauto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
After a puts call that returns, from a state interpreted with the ghost heap h, the state is interpreted with h again: puts's prefix keeps the runtime ready, the frame keeps h agreeing and live objects apart, and the prefix gives rep and readiness of the new world, whose roots it delegated from the old roots and its born objects, so they stay apart from h.
Explore proof 12 tactics
Initial goal
intros Hi Ha Hd Hr Hrep & Hw & Hapart
destruct M.E.invoke_prefix_ _ _ _ _ _ _ _ _ _ _ _ _ Hi as born & Hp
destruct M.E.prefix_preserve_ _ _ _ _ _ _ _ _ _ _ Hp Hr as Hr′ _
destruct prefix_agrees_ _ _ _ _ _ _ _ _ _ _ h Hp Hr Hd Ha Hapart ltac:intros ? ? ? as Ha′ & Hd′ & Hb & Hapart′
destruct Hp as _ Hrep′ & Hw′ & Hdel & _
change roots_apartstdio_rootsw h m′ in Hapart′
split ; exact Ha′ |
split ; exact Hd′ |
split ; exact Hr′ |
split ; exact Hrep′ |
split ; exact Hw′ |
exact delegates_apart_ _ _ _ h Hdel Hapart′ Hb Hd′
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Owning a string in an interpreted state, p points there at a terminated_length string of its length and at a c_string.
Explore proof 4 tactics
Initial goal
iIntros Hσ [%Hok Hs]
iDestruct pts_facts with Hσ Hs as %Ha & _
iPureIntro
split ; exact string_terminated_ _ _ _ _ Hok Ha | exact string_c_string_ _ _ _ _ Hok Ha
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The string lends its object's life and its i-th byte, x, the NUL for i its length, and takes them back.
Explore proof 11 tactics
Initial goal
intros Hx
iIntros [%Hok [Hl Hs]]
rewrite / live / ibyte / pts /=
iDestruct big_sepL_lookup_acc with Hs as [Hb Hk] ; exact byte_ps_lookup_ _ _ _ _ Hx |
iSplit ; done |
iSplitL Hl ; iFrame |
iSplitL Hb ; iFrame |
iIntros [Hl _] [Hb _]
iSplit ; done |
iFrame Hl
iApply Hk with Hb
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A string lends the suffix from its i-th byte, before its NUL or at it, as the string at q, and takes it back.
Explore proof 14 tactics
Initial goal
intros Hi
destruct string_ps_suffixp b reg bytes i Hi as Ep Eq
iIntros [%Hok Hs]
rewrite / string Ep Eq / pts big_sepL_cons big_sepL_app
iDestruct Hs as (Hl & Hpre & Hsuf)
iSplitL Hl Hsuf
2 openfocused goal
–iSplit ; iPureIntro ; exact string_ok_suffix_ _ _ _ _ Hi Hok |
2 opendeferred goal
rewrite big_sepL_cons
2 opendeferred goal
iFrame
deferred goal
–iIntros [_ Hq]
rewrite big_sepL_cons
iDestruct Hq as [Hl Hsuf]
iSplit ; done |
iFrame
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Where the string's pieces agree, p points at a bounded_length string for any bound n: its length, or n if that is shorter. Campaign 4's reading of a string for strncmp.
Explore proof 22 tactics
Initial goal
intros Hok Ha Hn
pose proof string_bytesm p b reg bytes Hok Ha as Hb
bounded_length m p n (length bytes `min` n)destruct Hok as Hc & H0 & H1 & H2 & H3 & Hf
destruct Ha as E Lv _
bounded_length m p n (length bytes `min` n)split ; exact Hn |
0 <= length bytes `min` n <= n
∧ (∃ (base : Z) (reg0 : region),
(ptr_cap p = Obj base ∨ ptr_cap p = Ro base)
∧ object_region base (m_regions m) = Some reg0 ∧ r_life reg0 = Live)
∧ access_res m (ptr_addr p) (ptr_cap p)
(length bytes `min` n + (if length bytes `min` n <? n then 1 else 0))
Read 1 =
Some ok
∧ (∀ i : Z,
0 <= i < length bytes `min` n → 0 < m_bytes m (ptr_addr p + i) < 256)
∧ (length bytes `min` n < n
→ m_bytes m (ptr_addr p + length bytes `min` n) = 0)split ; lia |
(∃ (base : Z) (reg0 : region),
(ptr_cap p = Obj base ∨ ptr_cap p = Ro base)
∧ object_region base (m_regions m) = Some reg0 ∧ r_life reg0 = Live)
∧ access_res m (ptr_addr p) (ptr_cap p)
(length bytes `min` n + (if length bytes `min` n <? n then 1 else 0))
Read 1 =
Some ok
∧ (∀ i : Z,
0 <= i < length bytes `min` n → 0 < m_bytes m (ptr_addr p + i) < 256)
∧ (length bytes `min` n < n
→ m_bytes m (ptr_addr p + length bytes `min` n) = 0)split ; exists b, reg ; auto |
access_res m (ptr_addr p) (ptr_cap p)
(length bytes `min` n + (if length bytes `min` n <? n then 1 else 0)) Read
1 =
Some ok
∧ (∀ i : Z,
0 <= i < length bytes `min` n → 0 < m_bytes m (ptr_addr p + i) < 256)
∧ (length bytes `min` n < n
→ m_bytes m (ptr_addr p + length bytes `min` n) = 0)split
2 openaccess_res m (ptr_addr p) (ptr_cap p)
(length bytes `min` n + (if length bytes `min` n <? n then 1 else 0)) Read
1 =
Some okfocused goal
(∀ i : Z,
0 <= i < length bytes `min` n → 0 < m_bytes m (ptr_addr p + i) < 256)
∧ (length bytes `min` n < n
→ m_bytes m (ptr_addr p + length bytes `min` n) = 0)–apply access_read_ b reg ; exact Hc | exact E | lia | | apply Z.mod_1_r
2 openptr_addr p +
(length bytes `min` n + (if length bytes `min` n <? n then 1 else 0)) <=
r_end regdeferred goal
(∀ i : Z,
0 <= i < length bytes `min` n → 0 < m_bytes m (ptr_addr p + i) < 256)
∧ (length bytes `min` n < n
→ m_bytes m (ptr_addr p + length bytes `min` n) = 0)destruct Z.min_ n <? n ; lia
deferred goal
(∀ i : Z,
0 <= i < length bytes `min` n → 0 < m_bytes m (ptr_addr p + i) < 256)
∧ (length bytes `min` n < n
→ m_bytes m (ptr_addr p + length bytes `min` n) = 0)–split
2 open∀ i : Z, 0 <= i < length bytes `min` n → 0 < m_bytes m (ptr_addr p + i) < 256focused goal
length bytes `min` n < n → m_bytes m (ptr_addr p + length bytes `min` n) = 0+intros i Hi
2 openHi : 0 <= i < length bytes `min` ndeferred goal
length bytes `min` n < n → m_bytes m (ptr_addr p + length bytes `min` n) = 0destruct lookup_lt_is_Some_2bytes Z.to_nati ltac:lia as x Hx
2 openHi : 0 <= i < length bytes `min` ndeferred goal
length bytes `min` n < n → m_bytes m (ptr_addr p + length bytes `min` n) = 0rewrite <- Z2Nat.idi by lia
2 openHi : 0 <= i < length bytes `min` ndeferred goal
length bytes `min` n < n → m_bytes m (ptr_addr p + length bytes `min` n) = 0rewrite HbZ.to_nati x ; | by apply lookup_app_l_Some
2 openHi : 0 <= i < length bytes `min` ndeferred goal
length bytes `min` n < n → m_bytes m (ptr_addr p + length bytes `min` n) = 0exact Forall_lookup_1_ _ _ _ Hf Hx
deferred goal
length bytes `min` n < n → m_bytes m (ptr_addr p + length bytes `min` n) = 0+intros Hk
Hk : length bytes `min` n < nm_bytes m (ptr_addr p + length bytes `min` n) = 0assert Z.minZ.of_natlengthbytes n = Z.of_natlengthbytes as → by lia
Hk : length bytes `min` n < napply Hblengthbytes 0
Hk : length bytes `min` n < nrewrite lookup_app_r ; | lia
Hk : length bytes `min` n < nrewrite Nat.sub_diag
Hk : length bytes `min` n < nreflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The calls
The Iris instance of the registry and wi.
The expressions of the language.
A call that changes no memory and no world, from a ready runtime, keeps the state interpretation, with the same ghost heap, whatever the runtime state after it.
Explore proof 10 tactics
Initial goal
intros Hi
iIntros (%h & Hh & %Hf)
destruct Hf as Ha & Hd & Hr & Hw
destruct M.E.invoke_prefix_ _ _ _ _ _ _ _ _ _ _ _ _ Hi as born & Hp
destruct M.E.prefix_preserve_ _ _ _ _ _ _ _ _ _ _ Hp Hr as Hr′ _
iExists h
iFrame Hh
iPureIntro
cbn
auto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
strlen's triple, from strlen_contract: owning the string at p, which v reads as, the call binds d to its length.
Proof 28 steps Qed
What strchr finds in the string p with contents bytes for the int c, whose low byte is not the NUL: the first byte equal to it, at offset i, with p's capability, or null if there is none.
strchr's triple, from strchr_contract: owning the string at p, which v reads as, the call binds d to what it finds of the int c, which w reads as.
Proof 61 steps Qed
strcmp's triple, from strcmp_contract: owning the strings at p and q, which v and w read as, the call binds d to an int. Which int does not matter to a caller that only branches on it.
Proof 21 steps Qed
strncmp's triple, from strncmp_contract: owning the strings at p and q, which v and w read as, compared up to n bytes, which u reads as, the call binds d to an int.
Proof 22 steps Qed
strrchr's triple, from strrchr_contract: owning the string at p, which v reads as, the call binds d to null or to a pointer into the string before its NUL, with p's capability, for an int c, which w reads as, whose low byte is not the NUL.
Proof 40 steps Qed
puts
The Iris instance of the registry and stdio_wi.
The expressions of the language.
puts's triple, from puts_contract: owning the string at p, which v reads as, the call binds d to the int it returns, n modulo 2^32, and the string is still owned: puts writes only its world's roots, which stdio_wi keeps apart from it.
Proof 23 steps Qed
StringLogicOn over a Logic instance of its own.