rocq/theories/HelloConv.v
GNU Hello's conversion of its greeting through the program logic of Logic.v (docs/logic.md): libc's mbrtowc, and gnulib's rpl_mbrtowc and rpl_mbsrtowcs around it, which main calls to turn the greeting into wide characters.
mbrtowc(out, src, n, state) writes three cells the program owns: the wide character at out, the conversion state and errno. Its triple (wp_mbrtowc) takes the three as whole blocks, with their life, bytes and aux slots, and hands them back holding whatever the memory holds after the call, which conv_result describes: Campaign 4's mbrtowc_result read through the owned bytes, under the audited codec. ContractFrame.v's prefix_agrees_out keeps every other piece agreeing, though the call's roots now meet the program's objects.
The world is Libc.v's libc_world. The triple is generic in the world interpretation wi, given that wi makes libc ready, fixes the codec and the errno cell, keeps libc's private roots, releases and capabilities apart from the program's objects (libc_private), and survives the call's writes into the three blocks. The audited codec premise (Haudit) stays explicit, as docs/logic.md records: it is not derived from the locale.
Memory read as lists
The n bytes of the memory m from the address a.
The n aux slots of the memory m from the address a.
As le_value(Bytes, U): the little-endian integer of the bytes xs.
As many bytes as asked for.
Explore proof 3 tactics
Source script
As many slots as asked for.
Explore proof 3 tactics
Source script
The memory reads back the bytes xs it holds from a.
Explore proof 12 tactics
Initial goal
intros H
apply list_eq
intros i
unfold mem_bytes
rewrite list_lookup_fmap
destruct xs !! i as x | eqn: E
2 open–rewrite lookup_seq_lt ; | exact lookup_lt_Some_ _ _ E
2 opencbn
2 openf_equal
2 openexact Hi x E
–rewrite lookup_seq_ge ; reflexivity |
exact lookup_ge_None_1_ _ E
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A four-byte load is the little-endian value of the four bytes.
Explore proof 4 tactics
Initial goal
unfold load_bytes , le_value , mem_bytes
change Z.to_nat4 with 4%nat
cbn
ring
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The state bytes of mbrtowc_pre are the eight bytes at state.
Explore proof 1 tactics
Initial goal
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Spans of live objects
Spans inside two different live objects are apart.
Explore proof 4 tactics
Initial goal
intros Hd H1 H2 Hne
unfold spans_apart
cbn
destruct Hd_ _ _ _ H1 H2 Hne ; lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A span apart from an object's whole extent is apart from every span inside it.
Explore proof 3 tactics
Initial goal
unfold spans_apart
cbn
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
Apart spans share no address.
Explore proof 2 tactics
Initial goal
unfold spans_apart , span_contains
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The bytes xs from offset o of the live object b, where they agree: in its bounds, and held by the memory.
Explore proof 13 tactics
Initial goal
intros Hl Ha Hne
destruct xs as | x0 xs′ ; congruence |
destruct byte_ps_agreem b reg o x0 :: xs′ 0 x0 Hl Ha eq_refl as _ & H0 & _
destruct lookup_lt_is_Some_2x0 :: xs′ lengthxs′ ltac:cbn ; lia as y Hy
destruct byte_ps_agree_ _ _ _ _ _ _ Hl Ha Hy as _ & _ & H1
cbn [ length ] in *
split ; lia |
split ; lia |
intros i x Hx
destruct byte_ps_agree_ _ _ _ _ _ _ Hl Ha Hx as Hv & _
rewrite <- Hv
f_equal
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A block after a call
The block of k bytes from offset o of the object b, live in m' with the region reg and in its bounds, holding what m' holds there, agrees with m'.
Explore proof 36 tactics
Initial goal
intros Hl Ho He Hin
unfold block_ps in Hin
apply elem_of_cons in Hin as Hin | Hin
2 open{injection Hin as → →
2 openexact Hl
assert Hreg : registeredm′ b by destruct Hl as E _ ; unfold registered ; congruence
apply elem_of_app in Hin as Hin | Hin
2 openfocused goal
–apply byte_ps_elem in Hin as i & x & Hx & → & →
2 opendeferred goal
unfold mem_bytes in Hx
2 opendeferred goal
apply list_lookup_fmap_Some in Hx as i′ & → & Hi
2 opendeferred goal
apply lookup_seq in Hi as → Hi
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 E0 in E
2 opendeferred goal
injection E as ←
2 opendeferred goal
split ; lia |
2 opendeferred goal
split ; lia |
2 opendeferred goal
f_equal
2 opendeferred goal
lia
deferred goal
–apply aux_ps_elem in Hin as i & c & Hc & → & →
unfold mem_auxes in Hc
apply list_lookup_fmap_Some in Hc as i′ & → & Hi
apply lookup_seq in Hi as → Hi
cbn [ agree_piece ]
split ; exact Hreg |
intros reg′ E L
destruct Hl as E0 _
rewrite E0 in E
injection E as ←
split ; lia |
split ; lia |
left
f_equal
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A byte or aux piece of a live object, inside its bounds at an address inside the k bytes from offset o of the live object b, is among the keys of the block there.
Explore proof 8 tactics
Initial goal
intros Hd Hl Ho He Hc Hk Hl′ Ho′ He′ Ha
assert b′ = b as →
2 open{destruct Z.eq_decb′ b as E | Hne ; exact E |
2 opendestruct Hd_ _ _ _ Hl′ Hl Hne ; lia
rewrite fmap_app
apply elem_of_app
replace o′ with o + o′ − o in Hk by lia
destruct Hk as → | → ; left ; apply byte_ps_key | right ; apply aux_ps_key ; lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
What owning a block of the bytes xs tells about the memory: its object is live, the bytes are in its bounds, and the memory holds them.
Explore proof 18 tactics
Initial goal
intros Ha Hlk Hne
assert Hl : live_atm b reg
2 openfocused goal
{apply HaPLiveb VLivereg , Hlk
2 opendeferred goal
apply elem_of_cons
2 opendeferred goal
left
2 opendeferred goal
reflexivity
deferred goal
assert Hx : all_agreem byte_psb o xs
2 open{apply all_agree_intro
2 openintros l v Hin
2 openapply Ha , Hlk
2 openunfold block_ps
2 openapply elem_of_cons
2 openright
2 openapply elem_of_app
2 openleft
2 openexact Hin
split ; exact Hl |
exact byte_block_factsm b reg o xs Hl Hx Hne
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A list of length k + 1 is not empty.
Explore proof 2 tactics
Initial goal
intros H →
discriminate H
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A block's byte and aux keys hold no life piece.
Explore proof 3 tactics
Initial goal
rewrite fmap_app byte_ps_keys aux_ps_keys
intros Hin
apply elem_of_app in Hin as Hin | Hin ; apply list_elem_of_fmap in Hin as ? & E & _ ; discriminate E
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A block's byte and aux keys are keys of the block after a call, of as many bytes and slots.
Explore proof 7 tactics
Initial goal
intros Hx Hc Hin
rewrite block_ps_keysb reg o xs cs ; | rewrite length_mem_bytes ; auto | rewrite length_mem_auxes ; auto
unfold block_ps
rewrite fmap_cons
apply elem_of_cons
right
exact Hin
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
libc's private state
libc's private state keeps apart from the program's objects: no piece of h is of an object libc may release, and of every live object owning a piece of h, every private root lies outside its extent and libc's private sources hand out none of its capabilities.
What the program learns from mbrtowc(out, src, n, state) returning r, for 0 < n and the audited codec, read through the bytes it owns (mbrtowc_result): ss are the source bytes and xo the output's bytes before the call; xo', ys' and xe' the output's, state's and errno's bytes after it. A NUL converts to 0 and resets the state; a character of r bytes, none of them NUL, to a nonzero wide character; an invalid sequence sets errno to EILSEQ (84); an incomplete one, under the audited codec, is shorter than 16 bytes with no NUL. Under the audited C codec (CodecC) an invalid sequence keeps the state's bytes ys and starts with a byte of 128 or more (mbrtowc_c_error).
gnulib's rpl_mbrtowc
rpl_mbrtowc(pwc, s, n, ps): mbrtowc into pwc, or into a local if pwc is null; on an invalid or incomplete sequence, outside a hard locale, the first byte stands for itself.
A block of rpl_mbrtowc.
rpl_mbrtowc's entry binds its four arguments.
Explore proof 1 tactics
Initial goal
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A signed byte load, then its low byte unsigned: the byte.
Explore proof 17 tactics
Initial goal
intros Hx
cbn [ extension ]
unfold sign_extension
assert E : x mod 256 = x by apply Z.mod_small ; lia
rewrite E
destruct Z.leb_spec128 x as Hh | Hh
2 openfocused goal
–assert E2 : x − 256 mod 2 ^ 32 = x − 256 + 2 ^ 32 by symmetry ; apply Z.mod_unique with −1 ; lia
2 opendeferred goal
rewrite E2
2 opendeferred goal
assert E3 : x − 256 + 2 ^ 32 mod 256 = x by symmetry ; apply Z.mod_unique with 2 ^ 24 − 1 ; lia
2 opendeferred goal
rewrite E3
2 opendeferred goal
apply Z.mod_small
2 opendeferred goal
lia
deferred goal
–assert E2 : x mod 2 ^ 32 = x by apply Z.mod_small ; lia
rewrite E2
rewrite E
apply Z.mod_small
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
What rpl_mbrtowc's caller learns, in conv_result's terms: mbrtowc's own answer r', or, after an invalid or incomplete sequence outside a hard locale, 1, with the first byte x stored as the wide character.
mbrtowc's answers are 64-bit words, and the two at the top are the invalid and the incomplete sequence, which leave the output alone.
Explore proof 1 tactics
Initial goal
intros → & _ | Hr & Hr2 & _ | → & _ & Hx & _ | → & _ & Hx & _ ; split ; try lia ; auto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
gnulib's rpl_mbsrtowcs
rpl_mbsrtowcs(dest, srcp, len, ps): converts the string at *srcp into at most len wide characters at dest, one rpl_mbrtowc call per character, each given a window of n bytes that reaches the NUL or is 16 bytes long.
A block of rpl_mbsrtowcs.
A nonzero byte loaded signed is not zero.
Explore proof 7 tactics
Initial goal
intros Hx
apply Z.eqb_neq
destruct Z.leb_spec128 x
2 openfocused goal
–assert E : x − 256 mod 2 ^ 32 = x − 256 + 2 ^ 32 by symmetry ; apply Z.mod_unique with −1 ; lia
2 opendeferred goal
rewrite E
2 opendeferred goal
lia
deferred goal
–rewrite Z.mod_small ; lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
In a string's bytes and NUL, a zero byte is the NUL.
Explore proof 12 tactics
Initial goal
intros Hbs Hk
pose proof proj1Forall_lookup_ _ Hbs as Hb
apply lookup_app_Some in Hk as Hk | Hl Hk
2 openfocused goal
–pose proof Hb_ _ Hk as Hx
2 opendeferred goal
cbv beta in Hx
2 opendeferred goal
apply lookup_lt_Some in Hk
2 opendeferred goal
split ; lia |
2 opendeferred goal
split ; intros E ; lia | auto
deferred goal
–apply list_lookup_singleton_Some in Hk as Hk Ex
subst x
split ; lia |
split ; intros _ ; lia | intros E ; lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A window of four bytes and a count from the fifth: what strnlen1's answer plus 4 comes to.
Explore proof 2 tactics
Initial goal
(4 <= l)%nat → ((l - 4)%nat + 1) `min` 12 + 4 = (l + 1) `min` 16intros Hl
((l - 4)%nat + 1) `min` 12 + 4 = (l + 1) `min` 16lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A byte stored as a four-byte word reads back as itself.
Explore proof 15 tactics
Initial goal
intros Hx
unfold le_bytes , le_value , byte_of
cbn
change 8 × Z.of_nat1 with 8
change 8 × Z.of_nat2 with 16
change 8 × Z.of_nat3 with 24
change 8 × Z.of_nat0 with 0
rewrite Z.pow_0_r
rewrite Z.div_1_r
rewrite Z.mod_smallx 256 ; | lia
rewrite Z.div_smallx 2 ^ 8 ; | lia
rewrite Z.div_smallx 2 ^ 16 ; | lia
rewrite Z.div_smallx 2 ^ 24 ; | lia
rewrite Zmod_0_l
lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
A run of nonzero bytes at the start of a string and its NUL ends before the NUL.
Explore proof 9 tactics
Initial goal
intros Hr H
destruct Z.le_gt_casesr Z.of_natlengthbs as Hle | Hgt ; exact Hle |
exfalso
rewrite Forall_lookup in H
apply Hlengthbs 0 ; | reflexivity
rewrite lookup_take_lt ; | lia
rewrite lookup_app_r ; | lia
rewrite Nat.sub_diag
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
One rpl_mbrtowc call of rpl_mbsrtowcs's converting loop, on the suffix bs of the string, with the window that reaches the NUL or is 16 bytes long, from a valid state in its initial shift state: never the incomplete answer, which would abort; EILSEQ stores 84 in errno; the NUL stores a zero character; anything else consumes r bytes before the NUL and stores a nonzero character, leaving a valid initial state.
Explore proof 20 tactics
Initial goal
Forall (λ x : Z, 0 < x < 256) bs
→ valid codec ys
→ le_value (take 4 ys) = 0
→ rpl_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo' ys' xe' r
→ 0 <= r < 2 ^ 64
∧ r ≠ 2 ^ 64 - 2
∧ (r = 2 ^ 64 - 1 → le_value xe' = 84)
∧ (r = 0 → le_value xo' = 0)
∧ (r ≠ 0
→ r ≠ 2 ^ 64 - 1
→ 0 < r <= length bs
∧ 0 < le_value xo' < 2 ^ 32
∧ valid codec ys' ∧ le_value (take 4 ys') = 0)intros Hbs Hval Hinit Hres
Hres :
rpl_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo' ys' xe' rassert No2 : ∀ys1 xo1 r1, ¬r1 = 2 ^ 64 − 2 ∧ validcodec ys1 ∧ xo1 = xo ∧ Z.minZ.of_natlengthbs + 1 16 < 16 ∧ Forallλx ⇒ x ≠ 0 takeZ.to_natZ.minZ.of_natlengthbs + 1 16 bs ++ 0
2 openHres :
rpl_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo' ys' xe' r∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))focused goal
Hres :
rpl_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo' ys' xe' rNo2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0]))){intros ys1 xo1 r1 _ & _ & _ & Hn16 & Hnz
2 openHres :
rpl_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo' ys' xe' rHn16 : (length bs + 1) `min` 16 < 16Hnz :
Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0]))deferred goal
Hres :
rpl_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo' ys' xe' rNo2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))apply before_nul in Hnz ; lia
deferred goal
Hres :
rpl_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo' ys' xe' rNo2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))destruct Hres as Hc | → & r0 & Hr0 & Hc0 & x & Hx & →
2 openHc :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo' ys' xe' rNo2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))focused goal
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))–destruct Hc as → & _ & _ & Ho & _ | Hr & Hr2 & Hv & Ho & Hs & Hnz | → & He & _ | H2
5 openNo2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))focused goal
Hr : 0 < r <= (length bs + 1) `min` 16No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))focused goal
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))focused goal
H2 :
r = 2 ^ 64 - 2
∧ valid codec ys'
∧ xo' = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0]))No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))+repeat split ; try lia ; auto
4 opendeferred goal
Hr : 0 < r <= (length bs + 1) `min` 16No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
H2 :
r = 2 ^ 64 - 2
∧ valid codec ys'
∧ xo' = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0]))No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))+apply before_nul in Hnz ; | lia
4 openHr : 0 < r <= (length bs + 1) `min` 16No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
H2 :
r = 2 ^ 64 - 2
∧ valid codec ys'
∧ xo' = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0]))No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))repeat split ; try lia ; auto
3 opendeferred goal
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
H2 :
r = 2 ^ 64 - 2
∧ valid codec ys'
∧ xo' = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0]))No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))+repeat split ; try lia ; auto
2 opendeferred goal
H2 :
r = 2 ^ 64 - 2
∧ valid codec ys'
∧ xo' = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0]))No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))+exfalso
2 openH2 :
r = 2 ^ 64 - 2
∧ valid codec ys'
∧ xo' = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0]))No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))deferred goal
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))exact No2_ _ _ H2
deferred goal
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))–destruct nul_index_ _ _ Hbs Hx as Hxr & _ & Hn
Hc0 :
conv_result valid c codec ((length bs + 1) `min` 16)
(bs ++ [0]) xo ys xo ys' xe' r0No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))destruct Hc0 as → & _ | _ & Hr2 & _ | _ & _ & _ & _ & Hlaw | H2 ; try lia ; | exfalso ; exact No2_ _ _ H2
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))destruct HlawHval Hinit as Hv′ & Hs′ & x′ & Hx′ & Hx0
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))rewrite Hx in Hx′
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))injection Hx′ as ←
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))pose proof HnHx0 as Hlen
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))rewrite le_value_bytex Hxr
No2 :
∀ (ys1 xo1 : list Z) (r1 : Z),
¬ (r1 = 2 ^ 64 - 2
∧ valid codec ys1
∧ xo1 = xo
∧ (length bs + 1) `min` 16 < 16
∧ Forall (λ x : Z, x ≠ 0)
(take (Z.to_nat ((length bs + 1) `min` 16)) (bs ++ [0])))repeat split ; try lia ; auto
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
The four-byte words of ws are wide characters, none of them zero.
No words, no wide characters.
Explore proof 3 tactics
A nonzero wide character after i of them.
Explore proof 10 tactics
Initial goal
intros Hw Lw Lx Hx k Hk
rewrite length_app in Hk
destruct decide4 × k + 4 ≤ lengthws%nat as Hlt | Hge
2 open–rewrite drop_app_le ; | lia
2 openrewrite take_app_le ; | rewrite length_drop ; lia
2 openapply Hw
2 openexact Hlt
–assert Ek : 4 × k = lengthws%nat by lia
rewrite Ek drop_app_length
rewrite take_ge ; exact Hx | lia
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
mbrtowc's triple
The Iris instance of the registry and wi.
The expressions of the language.
mbrtowc has Campaign 4's contract, whose state predicate valid admits the zero state and holds only of bytes.
The world interpretation makes libc ready, with the errno cell errno, keeps the codec piece current (codec_current) and libc's private state apart. A conversion learns the codec from the codec piece, which setlocale's setter set (wp_setlocale_set).
The audited codec premise: the world's codec is one of the audited profiles, C or C.UTF-8.
The world interpretation survives a return of mbrtowc into the output, state and errno blocks the program owns, holding whatever the memory holds there afterwards.
Owning two blocks, they are of different objects: each life is owned once.
Explore proof 7 tactics
Initial goal
rewrite / block_ps / pts ! big_sepL_cons
iIntros [Hl _] [Hl′ _]
iDestruct pointsto_ne with Hl Hl′ as %Hne
iPureIntro
intros ←
apply Hne
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
The arguments are admitted (mbrtowc_pre) where the program owns the output, state and errno blocks and the source's life and bytes, of four different objects, the state's bytes valid.
Proof 125 steps Qed
A return of the call keeps every piece but the three blocks' bytes and slots agreeing (prefix_agrees_out), so that, the blocks holding what the memory holds after it, the ghost heap agrees; the live objects stay apart and the runtime ready.
Proof 65 steps Qed
Campaign 4's mbrtowc_result, for 0 < n and the audited codec, read through the owned bytes: conv_result.
Proof 106 steps Qed
mbrtowc(out, src, n, state)'s triple, from mbrtowc_contract: owning the output, state and errno blocks, of 4, 8 and 4 bytes with their slots, and the source's life and bytes ss, of four different objects, where vo, vs, vn and vt read as out, src, n and state, n is positive and within ss, and the state's bytes are valid, the call binds d to its result r and hands the blocks back holding new bytes and slots, which conv_result describes.
Proof 71 steps Qed
rpl_mbrtowc's triple
The genv links Hello, whose world interpretation keeps every lent string where lent_at says: hard_locale's premises (HelloLocale.v).
setlocale's query, as hard_locale needs it.
strlen, memcpy and strcmp have their contracts.
The world interpretation survives a copy, a byte store and a four-byte store into a block the program owns, and an allocation.
Enter a block of rpl_mbrtowc.
A pure instruction.
rpl_mbrtowc from its block L11, where pr.1 is the output pointer out, whichever entry chose it: the call to mbrtowc, then the fallback outside a hard locale.
Proof 119 steps Qed
An invalid or incomplete sequence.
Not a hard locale: the first byte stands for itself.
A hard locale: mbrtowc's answer.
A character, a NUL or nothing to stand for: mbrtowc's answer.
rpl_mbrtowc(out, src, n, state) for a non-null out, from wp_mbrtowc and HelloLocale.v's hard_locale_spec: owning the output, state and errno blocks, the source's n bytes or more, the codec piece, the literals "C" and "POSIX", and the name of LC_CTYPE (category 0) lent, the call returns what rpl_result says, and hands every block back.
Proof 17 steps Qed
rpl_mbsrtowcs's window
The memchr contract, for strnlen1.
Enter a block of rpl_mbsrtowcs.
Read byte k of the window in block blk, after the first: a flight to q + k, a half check against the first check, a load and its test. Leaves the branch on the test, with the byte named x.
The temporaries the converting loop keeps, and their values: the destination cursor, the source cursor, the count left, the state, srcp, its check, and the destination's address.
End the window at L37, with the window's length n in pr.8.
rpl_mbsrtowcs's window, blocks L29 to L57: with the source cursor q at a string bs, the loop reads up to four bytes, stopping at the NUL, and then asks strnlen1 for at most 12 more; pr.8 is then min(len + 1, 16), and the loop's other temporaries are as they were.
Proof 119 steps Qed
The first byte, through a check.
The second byte.
The third byte.
The fourth byte.
Four bytes before the NUL: strnlen1 counts the rest, up to 12.
A string's pieces are a block from its offset: its life and its bytes with the NUL, and no aux slots.
Explore proof 4 tactics
Initial goal
rewrite / SL.string / string_ps / block_ps
cbn [ aux_ps ]
rewrite app_nil_r
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
An object's life, bytes and aux slots from offset o are its block from o.
Explore proof 2 tactics
Initial goal
rewrite / block_ps / pts big_sepL_cons big_sepL_app / live / bytes / auxes / pts big_sepL_singleton
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
errno's lookup has Campaign 4's contract.
The world interpretation survives a pointer store into a cell the program owns.
__errno_location() returns the errno cell and changes nothing: HelloClose.v's wp_close_errno over libc_world. A local rule, for promotion to a libc logic module.
Proof 30 steps Qed
What rpl_mbsrtowcs's caller learns when it returns r, with dest's final bytes ws, errno's bytes xe and the length l of the source: EILSEQ, -1 with 84 in errno; or r wide characters, none zero, then a zero one.
Owning the bytes xs from o is owning their first n and the rest.
Explore proof 4 tactics
Initial goal
intros Hn
assert E : Z.of_natn = Z.of_natlengthtaken xs by rewrite length_take ; lia
rewrite E - bytes_app take_drop
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Owning the slots cs from o is owning their first n and the rest.
Explore proof 4 tactics
Initial goal
intros Hn
assert E : Z.of_natn = Z.of_natlengthtaken cs by rewrite length_take ; lia
rewrite E - auxes_app take_drop
done
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
rpl_mbsrtowcs's converting loop, from block L28, by Löb induction: after i wide characters from the first j bytes of the string at p, with the state valid and in its initial shift state, every path returns what mbs_post says; the abort after an incomplete sequence is never reached.
Proof 190 steps Qed
Characters are left to write.
The window over the suffix from byte j.
The next output word, and the source as a block.
Never incomplete: the abort is not reached.
EILSEQ: the cursor goes back through srcp, errno gets 84, and -1 returns.
The NUL: null goes back through srcp, and the count of characters returns.
A character: both cursors move on, and the loop goes round.
rpl_mbsrtowcs
rpl_mbsrtowcs's entry binds its four arguments.
Explore proof 1 tactics
Initial goal
reflexivity
No remaining goals.
Qed
✓ Proof checked and closed by Rocq.
Source script
rpl_mbsrtowcs(dest, srcp, len, ps) as main calls it: dest a fresh buffer of len wide characters, srcp a cell holding the string p, len the string's length and its NUL, and ps a non-null state, valid and in its initial shift state. The entry allocates a local it never uses, keeps ps, loads the string through srcp, a read-only literal or not, and goes to the converting loop (mbs_loop) with nothing written yet; the counting loop, for a null dest, is not reached. The call returns what mbs_post says.
Proof 54 steps Qed
The local for the counting loop, never used here.
ps is not null.
The string through srcp.
dest is not null: the converting loop.
HelloConvOn over a Logic instance of its own.