Automated verification of systems software with Serval
Xi Wang
Joint work with Luke Nelson, James Bornholt, Ronghui Gu, Andrew Baumann, and Emina Torlak
University of Washington Columbia University Microsoft Research
1
Automated verification of systems software with Serval Xi Wang - - PowerPoint PPT Presentation
Automated verification of systems software with Serval Xi Wang Joint work with Luke Nelson, James Bornholt, Ronghui Gu, Andrew Baumann, and Emina Torlak University of Washington Columbia University Microsoft Research 1 Today: eliminating
Xi Wang
Joint work with Luke Nelson, James Bornholt, Ronghui Gu, Andrew Baumann, and Emina Torlak
University of Washington Columbia University Microsoft Research
1
Today: eliminating bugs in low-level systems software
2
OS Kernel / security monitor Process Process Process
Example: undefined behavior
Question: what’s the result of mul(60000, 60000)?
3
uint64_t mul(uint16_t a, uint16_t b) { uint32_t c = a * b; return c; }
Eliminating bugs with formal verification
4
OS Kernel / security monitor Process Process Process
seL4 (SOSP’09) Ironclad, Jitk (OSDI’14) CertiKOS (PLDI’16) Komodo (SOSP’17)
Eliminating bugs with formal verification
5
OS Kernel / security monitor Process Process Process
seL4 (SOSP’09) Ironclad Apps (OSDI’14) FSCQ (SOSP’15) CertiKOS (PLDI’16) Komodo (SOSP’17)
Prior work: automated (push-button) verification
6
Nickel OSDI'18
Finite implementation Decidable specification Automated verifier
Hyperkernel SOSP'17 Yggdrasil OSDI’16
Prior work: automated (push-button) verification
7
Specification Implementation Automated verifier
SMT solver
Challenges
8
How to lower effort of writing automated verifiers? How to find and fix performance bottlenecks? How to retrofit to existing systems?
Specification Implementation Automated verifier SMT solver
Contributions
9
no guarantees on concurrency or side channels
Verifying a system with Serval
10
Z3
SMT solver Rosette Serval
RISC-V verifier System specification RISC-V instructions
Verifying a system with Serval
11
Z3
SMT solver Rosette Serval
RISC-V verifier RISC-V instructions System specification
Verifying a system with Serval
12
SMT solver Rosette Serval
RISC-V verifier RISC-V instructions
Z3
System specification
Verifying a system with Serval
13
Z3
SMT solver Rosette Serval
RISC-V verifier RISC-V instructions System specification
Verifying a system with Serval
14
Z3
SMT solver Rosette Serval
RISC-V verifier RISC-V instructions System specification
Verifying a system with Serval
15
Z3
SMT solver Rosette Serval
RISC-V verifier RISC-V instructions System specification
Example: proving refinement for sign
16
Serval
RISC-V verifier
(define (sign x) (cond [(negative? x) -1] [(positive? x) 1] [(zero? x) 0]))
0: sltz a1 a0 1: bnez a1 4 2: sgtz a0 a0 3: ret 4: li a0 -1 5: ret
Verifier = interpreter + symbolic optimization
17
as interpreter
to find bottleneck
Verifier [1/3]: writing an interpreter
18
RISC-V verifier x86-32 verifier System specification x86-32 instructions RISC-V instructions
(struct cpu (pc regs ...) #:mutable) (define (interpret c program) (define pc (cpu-pc c)) (define insn (fetch pc program)) (match insn [('li rd imm) (set-cpu-pc! c (+ 1 pc)) (set-cpu-reg! c rd imm)] [('bnez rs imm) (if (! (= (cpu-reg c rs) 0)) (set-cpu-pc! c imm) (set-cpu-pc! c (+ 1 pc)))] ...))
Verifier [1/3]: writing an interpreter
19
RISC-V verifier x86-32 verifier System specification x86-32 instructions RISC-V instructions
(struct cpu (pc regs ...) #:mutable) (define (interpret c program) (define pc (cpu-pc c)) (define insn (fetch pc program)) (match insn [('li rd imm) (set-cpu-pc! c (+ 1 pc)) (set-cpu-reg! c rd imm)] [('bnez rs imm) (if (! (= (cpu-reg c rs) 0)) (set-cpu-pc! c imm) (set-cpu-pc! c (+ 1 pc)))] ...))
(struct cpu (pc regs ...) #:mutable) (define (interpret c program) (define pc (cpu-pc c)) (define insn (fetch pc program)) (match insn [('li rd imm) (set-cpu-pc! c (+ 1 pc)) (set-cpu-reg! c rd imm)] [('bnez rs imm) (if (! (= (cpu-reg c rs) 0)) (set-cpu-pc! c imm) (set-cpu-pc! c (+ 1 pc)))] ...))
Verifier [1/3]: writing an interpreter
20
(struct cpu (pc regs ...) #:mutable) (define (interpret c program) (define pc (cpu-pc c)) (define insn (fetch pc program)) (match insn [('li rd imm) (set-cpu-pc! c (+ 1 pc)) (set-cpu-reg! c rd imm)] [('bnez rs imm) (if (! (= (cpu-reg c rs) 0)) (set-cpu-pc! c imm) (set-cpu-pc! c (+ 1 pc)))] ...))
Verifier [1/3]: writing an interpreter
21
(struct cpu (pc regs ...) #:mutable) (define (interpret c program) (define pc (cpu-pc c)) (define insn (fetch pc program)) (match insn [('li rd imm) (set-cpu-pc! c (+ 1 pc)) (set-cpu-reg! c rd imm)] [('bnez rs imm) (if (! (= (cpu-reg c rs) 0)) (set-cpu-pc! c imm) (set-cpu-pc! c (+ 1 pc)))] ...))
Verifier [1/3]: writing an interpreter
22
Verifier [2/3]: identifying bottlenecks in symbolic evaluation
23
Serval
RISC-V verifier
(define (sign x) (cond [(negative? x) -1] [(positive? x) 1] [(zero? x) 0]))
0: sltz a1 a0 1: bnez a1 4 2: sgtz a0 a0 3: ret 4: li a0 -1 5: ret
0: sltz a1 a0 1: bnez a1 4 2: sgtz a0 a0 3: ret 4: li a0 -1 5: ret
(define (sign x) (cond [(negative? x) -1] [(positive? x) 1] [(zero? x) 0]))
Verifier [2/3]: identifying bottlenecks in symbolic evaluation
24
Serval
RISC-V verifier
slow/timeout
Verifier [2/3]: identifying bottlenecks in symbolic evaluation
25
Verifier [2/3]: identifying bottlenecks in symbolic evaluation
26
Verifier [2/3]: identifying bottlenecks in symbolic evaluation
27
0: sltz a1 a0 1: bnez a1 4 2: sgtz a0 a0 3: ret 4: li a0 -1 5: ret
(struct cpu (pc regs) #:mutable) (define (interpret c program) (define pc (cpu-pc c)) (define insn (fetch pc program)) (match insn [('li rd imm) (set-cpu-pc! c (+ 1 pc)) (set-cpu-reg! c rd imm)] [('bnez rs imm) (if (! (= (cpu-reg c rs) 0)) (set-cpu-pc! c imm) (set-cpu-pc! c (+ 1 pc)))] ...))
Merge states to avoid path explosion
28
0: sltz a1 a0 1: bnez a1 4 2: sgtz a0 a0 3: ret 4: li a0 -1 5: ret
PC → 0 a0 → X a1 → Y PC → 1 a0 → X a1 → 1 PC → 1 a0 → X a1 → 0 PC → 1 a0 → X a1 → if(X < 0, 1, 0)
¬(X < 0) X < 0
0: sltz a1 a0 1: bnez a1 4 2: sgtz a0 a0 3: ret 4: li a0 -1 5: ret
Bottleneck: state explosion due to symbolic PC
29
Conditional jump PC → 1 a0 → X a1 → if(X < 0, 1, 0) PC → if(X < 0, 4, 2) a0 → X a1 → if(X < 0, 1, 0)
... ...
0: sltz a1 a0 1: bnez a1 4 2: sgtz a0 a0 3: ret 4: li a0 -1 5: ret
Bottleneck: state explosion due to symbolic PC
30
Conditional jump PC → if(...) a0 → X a1 → if(...) PC → 1 PC → 3 PC → 4 PC → 2 PC → 5 PC → 0
Verifier [3/3]: Repairing with symbolic optimizations
31
Verifier [3/3]: Repairing with symbolic optimizations
32
(define (interpret c program)
(define insn (fetch pc program)) (match insn ...)) (define (interpret c program) + (serval:split-pc [cpu pc] c (define insn (fetch pc program)) (match insn ...)))
Verifier [3/3]: Repairing with symbolic optimizations
33
PC → if(X < 0, 4, 2) a0 → X a1 → if(...) PC → 4 PC → 2 PC → if(X < 0, 4, 2) a0 → X a1 → if(...) PC → 1 PC → 3 PC → 4 PC → 2 PC → 5 PC → 0 split-pc
Verifier [3/3]: Repairing with symbolic optimizations
34
PC → if(X < 0, 4, 2) a0 → X a1 → if(...) PC → 4 PC → 2 PC → if(X < 0, 4, 2) a0 → X a1 → if(...) PC → 1 PC → 3 PC → 4 PC → 2 PC → 5 PC → 0
Domain knowledge:
Symbolic optimizations are essential to scaling verification
35
Verifier summary
36
Implementation
37
Z3
SMT solver Rosette Serval
RISC-V verifier x86 verifier LLVM verifier BPF verifier ARM verifier
Experience
38
Retrofitting previously verified systems
39
Retrofitting overview
40
System specification System implementation
Is the specification expressible in Serval? Is the implementation free of unbounded loops?
Example: retrofitting CertiKOS
41
CertiKOS Process Process Process
Example: retrofitting CertiKOS
42
Retrofitting summary
43
Reusing verifiers to find bugs
44
Example: BPF in the Linux kernel
45
User Kernel Application BPF bytecode JITted code 1 2 3 decision Bugs in JIT can compromise the entire systems! Jitk (OSDI’14); JitSynth (CAV’20)
Example bug found using Serval
impact
for both JIT and emitted machine instructions
46
/* Do LSH operation */ if (val < 32) {
+ /* shld dreg_hi,dreg_lo,imm8 */ + EMIT4(0x0F, 0xA4, add_2reg(0xC0, dreg_hi, dreg_lo), val); /* shl dreg_lo,imm8 */ EMIT3(0xC1, add_1reg(0xE0, dreg_lo), val);
}
Conclusion
47