Fil-C on Nix

Filnix wiki

the formal campaign

rocq/glibc/LenLogic.v

rocq/glibc/len.c, linked with nanofil's IR of user glibc's string/strlen.c, through the program logic of Logic.v (docs/glibc-ir.md).

main returns strlen(msg) for the 13-byte literal "hello, glibc". No contract describes strlen here: the linked module defines __strlen, glibc's word-at-a-time scan, and the weak alias strlen that names it, so the call is an ordinary direct call (wp_call_bind) and the proof walks glibc's own IR. The module calls nothing outside itself; the registry is empty.

The call is taken by StrlenLogic.v's wp_glibc_strlen, the triple of glibc's strlen for any string: the literal's object is 8-aligned and 16 bytes, its 13 bytes rounded up to whole words (Run.v's layout; filcc's literal object is also 16 bytes, probed in docs/glibc-ir.md), and the program owns its life and both words (msg_ps), which is the triple's precondition. Its first zero is 12 bytes in, so strlen returns 12.

len_never_panics is never_panics for main, through GlibcLogic.v's pieces_never_panics: Iris adequacy where the program starts owning pieces of its initial memory, here the literal's.

From iris.proofmode Require Import proofmode
From iris.base_logic.lib Require Import gen_heap
From iris.program_logic Require Import language weakestpre
From Stdlib Require Import String ZArith Lia
From Nanofil Require Import Term Syntax Values Memory Runtime Ops Semantics Run Machine Panics StepRules ExternalCalls ExternalMachine Transport Pieces Heap Language Logic
From Glibc Require Import LenCode GlibcLogic StrlenBits StrlenLogic
Open Scope Z_scope
Open Scope list_scope

The module

The checked module: len.c's main and its literal, glibc's __strlen and its alias strlen (len_term).

39Definition len_module: module ≔
Somemd
⇒
md
None
⇒
[ ]

The fixture decodes to len_module.

43Lemma len_decodes
Proof 2 steps Qed
vm_compute
reflexivity

The address of the literal msg, renamed len#msg by link.pl.

47Definition msg_base: Z ≔
65552

The literal's capability, read-only.

50Definition msg_ptr: ptr ≔

The literal's region: its 13 bytes rounded up to 16.

53Definition msg_reg: region ≔

The module's symbols: strlen is __strlen's address.

56Definition len_globals: listname × ptr ≔
TAtom"strlen", mkPtr65584 FnTAtom"__strlen"; TAtom"__strlen", mkPtr65584 FnTAtom"__strlen"; TAtom"len#msg", msg_ptr; TAtom"main", mkPtr65536 FnTAtom"main"

The module's initial memory.

63Definition len_mem: mem ≔
Some_, m
⇒
m
None
⇒

Laying out the module gives len_globals and len_mem.

Proof 3 steps Qed
assert H : option_mapfst initial_memorylen_module = Somelen_globals by vm_compute ; reflexivity
unfold len_mem
destruct initial_memorylen_module as g m | ; inversion H ; reflexivity

The function named n of the module, or an empty one.

74Definition len_fnn : string : func ≔
SomeDefinedf
⇒
f
_
⇒
mkFuncTAtomn TAtom"export" RClassW [ ] [ ]

len.c's main.

81Definition main_fn: func ≔
Eval vm_compute in len_fn"main"

glibc's __strlen.

83Definition strlen_fn: func ≔
Eval vm_compute in len_fn"__strlen"

The literal's pieces

The literal's first word, "hello, g", little-endian.

88Definition msg_w0: Z ≔
7430988242233353576

Its second word, "libc" and the NUL, then the three zero bytes the layout's rounding adds.

91Definition msg_w1: Z ≔
1667393900

The pieces the program owns: the literal's life and its two words.

94Definition msg_ps: listpiece × pval ≔

The pieces' keys are distinct.

98Lemma msg_ps_nodup
NoDupmsg_ps.*1
Proof 3 steps Qed
cbn
repeat apply NoDup_cons_2 ; intros H ; apply list_elem_of_In in H ; cbn in H ; repeat destruct H as H | H ; first discriminate | injection H ; unfold msg_base ; lia | exact H |
apply NoDup_nil_2

The literal's object is registered with its region.

Proof 2 steps Qed
vm_compute
reflexivity

The initial memory holds the literal as msg_ps says.

111Lemma msg_ps_agree
Proof 5 steps Qed
unfold msg_ps , cell
cbn [ all_agree map app fst snd agree_piece ]
rewrite ! msg_region
repeat first|split|intro|discriminate|match goal withH : Some_ = Some_ |- _⇒injection H as ←end;cbn [ r_end msg_reg ];unfold msg_base;try lia;vm_compute;reflexivity

The initial memory's one live object is apart from itself.

121Lemma len_disjoint
Proof 8 steps Qed
intros b1 r1 b2 r2 E1 _ E2 _ Hne
assert Hr : m_regionslen_mem = msg_reg by vm_compute ; reflexivity
rewrite Hr in E1 , E2
cbn [ object_region r_base msg_reg ] in E1 , E2
destruct Z.eqbmsg_base b1 eqn: H1 ; | discriminate
destruct Z.eqbmsg_base b2 eqn: H2 ; | discriminate
apply Z.eqb_eq in H1 , H2
congruence

Over any runtime

The proof over the runtime R.

133Module LenLogic R : Runtime
134Module LG ≔ Logic R
135Module GL ≔ GlibcLogicOn R LG
136Module SO ≔ StrlenOn R LG
137Module L ≔ LG.L
138Module P ≔ LG.P
Import L LG GL

The linked genv, whose free is R's.

The expressions of its language.

144Notation lexpr ≔ language.exprnanofil_langno_contracts lge

A call of strlen runs __strlen, through the alias.

147Lemma find_strlen
find_fnlge TAtom"strlen" = SomeDefinedstrlen_fn
Proof 2 steps Qed
vm_compute
reflexivity

main is a function of the module.

151Lemma find_main
find_fnlge TAtom"main" = SomeDefinedmain_fn
Proof 2 steps Qed
vm_compute
reflexivity
154Section wp
155Context `{!invGS_genhlc Σ, !gen_heapGSpiece pval Σ}

The Iris instance of the module: no contracts, no world.

158Instance len_irisGS: irisGS_genhlc nanofil_langno_contracts lge Σ ≔
nanofil_irisGSno_contracts no_world lge

glibc's strlen, called with the literal, owning its pieces: wp_glibc_strlen with its two words, returning 12 and handing the pieces back.

164Lemma strlen_msg_spec
∀ Φ
ptsmsg_ps −∗ ptsmsg_ps −∗ ΦRetWInt12 −∗ WPERunmkFramestrlen_fn TAtom"v0", WPtrmsg_ptr [ ] None EnterTAtom"start" None : lexpr {Φ}
Proof 30 steps Qed
iIntros Hps HΦ
rewrite / msg_ps ! pts_app
iDestruct Hps as (Hl & H0 & H1)
–right
reflexivity
–vm_compute
reflexivity
–vm_compute
reflexivity
–vm_compute
congruence
–vm_compute
congruence
–vm_compute
congruence
–repeat constructor ; vm_compute ; congruence
–split ; vm_compute ; reflexivity |
intros u Hu
do 12 destruct u as | u ; vm_compute ; congruence |
lia
–cbn [ words_ps ]
rewrite app_nil_r pts_app
iFrame
–iIntros Hl Hps
cbn [ words_ps ]
rewrite app_nil_r pts_app
iDestruct Hps as [H0 H1]
iApply HΦ
iFrame

main, owning the literal, returns 12: glibc's strlen of it.

186Lemma main_spec
ptsmsg_ps −∗ WPERunmkFramemain_fn [ ] [ ] None EnterTAtom"start" None : lexpr {o. ⌜o = RetWInt12⌝}
Proof 12 steps Qed
iIntros Hps
repeat step
iApply wp_call_bind ; side | exact find_strlen | side |
iNext
iApply strlen_msg_spec with Hps
iIntros _
iExists _
iSplit ; iPureIntro ; reflexivity |
iNext
iApply wp_ret ; side | side |
iNext
done

len.c linked with glibc's strlen never panics: called with any arguments, with any fuel and whatever the runtime does at its safepoints, no state of its run fails. By pieces_never_panics from main_spec, the program owning the literal's pieces.

203Theorem len_never_panics
P.never_panicslen_module TAtom"main" λ_ ⇒ True
Proof 12 steps Qed
–exact len_initial
–exact find_main
–exact len_disjoint
–exact msg_ps_agree
–exact msg_ps_nodup
–intros xs _
eexists
split ; reflexivity |
intros HinvG HheapG
iIntros Hps
iApply wp_wand with [Hps] ; iApply main_spec with Hps | by iIntros o _

The theorem for the interpreter's runtime.

218Module InterpLenLogic ≔ LenLogic InterpRuntime

len.c with glibc's strlen never panics under the interpreter's runtime.

222Theorem interp_len_never_panics
Proof 1 step Qed