A Separation Logic for Non-determinism and Sequence Points in C Formalized in Coq Robbert Krebbers Radboud University Nijmegen May 14, 2014 @ TYPES, Paris, France 1
What is this program supposed to do? int main() { int x; int y = (x = 3) + (x = 4); printf("%d %d\n", x, y); } Let us try some compilers ◮ Clang prints 4 7 , seems just left-right ◮ GCC prints 4 8 , does not correspond to any evaluation order This program violates the sequence point restriction ◮ due to two unsequenced writes to x ◮ resulting in undefined behavior ◮ thus both compilers are right 2
Undefined behavior in C “Garbage in, garbage out” principle ◮ Programs with undefined behavior are not statically excluded ◮ Undefined behavior ⇒ all bets are off ◮ Allows compilers to omit (expensive) dynamic checks A compiler independent C semantics should account for undefined behavior 3
Examples ◮ Sequenced side-effects are allowed (x = f()) ? (x = x + 1) : 0; ◮ Side-effects are useful in a while , for , return , . . . while ((x = getchar()) != EOF) /* do something */ ◮ Non-determinism is subtle in innocent looking examples: *p = g (x, y, z); Here, g may change p , so the evaluation order matters ◮ Interleaving of subexpressions is possible, for example printf("a") + (printf("b") + printf("c")); may print “ bac ” 4
Contribution A compiler independent small step operational, and axiomatic, semantics for non-determinism and sequence points, supporting: ◮ expressions with function calls, assignments, conditionals ◮ undefined behavior due to integer overflow ◮ parametrized by integer types ◮ dynamically allocated memory ( malloc and free ) ◮ non-local control ( return and goto ) ◮ local variables (and pointers to those) ◮ mutual recursion ◮ separation logic ◮ soundness proof fully checked by Coq 5
Key idea Observation : non-determinism corresponds to concurrency Idea : use the separation logic rule for parallel composition { P 1 } e 1 { Q 1 } { P 2 } e 2 { Q 2 } { P 1 ∗ P 2 } e 1 ⊚ e 2 { Q 1 ∗ Q 2 } What does this mean: ◮ Split the memory into two disjoint parts ◮ Prove that e 1 and e 2 can be executed safely in their part ◮ Now e 1 ⊚ e 2 can be executed safely in the whole memory Disjointness ⇒ no sequence point violation 6
Definition of the memory Given a set of permissions P , we define: m ∈ mem := index → fin (val × P ) b ∈ index := N v ∈ val ::= indet | int τ n | ptr b | NULL Integer types: unsigned char, signed int, . . . 7
C permissions The C permissions contain: ◮ Locked flag to catch sequence point violations ◮ Assignment: lock memory location ◮ Sequence point: unlock memory locations ◮ A fraction (0 , 1] Q for share accounting of read only memory ◮ Block scope variable or allocated with malloc flag A permission system P abstracts from these details. ◮ ∪ : P → P → P and ⊥ : P → P → Prop ◮ kind : P → { Free , Write , Read , Locked } ◮ lock , unlock : P → P ◮ satisfying certain axioms We lift these operations to memories index → fin (val × P ) 8
The language Our language ⊚ ∈ binop ::= == | <= | + | - | * | / | % | . . . e ∈ expr ::= x i | [ v ] Ω | e 1 := e 2 | f ( � e ) | load e | alloc | free e | e 1 ⊚ e 2 | e 1 ? e 2 : e 3 | ( τ ) e s ∈ stmt ::= e | skip | goto l | return e | block c s | s 1 ; s 2 | l : s | while ( e ) s | if ( e ) s 1 else s 2 Values [ v ] Ω carry a set Ω of indexes (memory locations) to be unlocked at the next sequence point 9
Operational semantics Head reduction for expressions ( e , m ) � h ( e ′ , m ′ ) (9 rules) ◮ On assignments: locked index b added to Ω 1 ∪ Ω 2 ([ptr b ] Ω 1 :=[ v ] Ω 2 , m ) � h ([ v ] { b }∪ Ω 1 ∪ Ω 2 , lock b ( m [ b := v ])) ◮ On sequence points: Ω unlocked in memory ([ v ] Ω ? e 2 : e 3 , m ) � h ( e 2 , unlock Ω m ) provided . . . Gives a local treatment of sequence points Small step reduction S ( k , φ, m ) � S ( k ′ , φ ′ , m ′ ) (33 rules) ◮ ( k , φ ) gives the position in the whole program ◮ Uses evaluation contexts to lift head reduction ◮ Different φ s for expressions, statements, function calls 10
Assertions of separation logic Defined using a shallow embedding: P , Q ∈ assert := stack → mem → Prop Maps variables to indexes in memory Some assertions: ( P ∗ Q ) ρ m := ∃ m 1 m 2 . m = m 1 ∪ m 2 ∧ m 1 ⊥ m 2 ∧ P ρ m 1 ∧ Q ρ m 2 γ ( e 1 �− → e 2 ) ρ m := ∃ b v . [ [ e 1 ] ] ρ, m = ptr b ∧ [ [ e 2 ] ] ρ, m = v ∧ m = { ( b , ( v , γ )) } ( P ⊲ ) ρ m := P ρ (unlock (locks m ) m ) (with e 1 and e 2 side-effect free) 11
The Hoare “triples” Statement judgment ∆; J ; R ⊢ { P } s { Q } Function conditions Goto/return conditions Q : assert Expression judgment ∆ ⊢ { P } e { Q } Q : val → assert In the semantics: if P holds beforehand, then ◮ e does not crash ◮ Q v holds afterwards when terminating with v ◮ with framing memories that can change at each step 12
The axiomatic semantics 24 separation logic proof rules , e.g. : For assignments: γ Write ⊆ kind γ { P 1 } e 1 { Q 1 } { P 2 } e 2 { Q 2 } ∀ av . (( Q 1 a ∗ Q 2 v ) → (( a �− → –) ∗ R a v )) lock γ { P 1 ∗ P 2 } e 1 := e 2 { λ v . ∃ a . ( a �− − − − → v ) ∗ R a v } For dereferencing: γ kind γ � = Locked { P } e { λ a . ∃ v . Q a v ∗ ( a �− → v ) } γ { P } load e { λ v . ∃ a . Q a v ∗ ( a �− → v ) } For the conditional: {∃ v . istrue v ∧ P ′ v } e 2 { Q } { P } e 1 { λ v . v � = indet ∧ P ′ v ⊲ } . . . { P } e 1 ? e 2 : e 3 { Q } Common separation logic and more complex rules can be derived 13
Formalization in Coq ◮ Extremely useful for debugging ◮ Notations close to those on paper ◮ Various extensions of the separation logic ◮ Uses lots of automation ◮ 10 000 lines of code Lemma ax_load ∆ A γ e P Q : perm_kind γ � = Locked → ∆ \ A � e {{ P }} e {{ λ a, ∃ v, Q a v ∗ valc a �→ { γ } valc v }} → ∆ \ A � e {{ P }} load e {{ λ v, ∃ a, Q a v ∗ valc a �→ { γ } valc v }}. 14
The bigger picture / Future work Separation Interpreter for C11’ logic for C11’ Separation algebras for the C memory Sequence points & (Submitted) non-determinism (POPL, 2014) C-types & strict Non-local control & aliasing restrictions block scope variables (CPP, 2013) (FoSSaCS, 2013) 15
Questions Sources: http://robbertkrebbers.nl/research/ch2o/ ( http://xkcd.com/371/ ) 16
Recommend
More recommend