Automated verification of systems software with Serval Xi Wang - - PowerPoint PPT Presentation

automated verification of systems software with serval
SMART_READER_LITE
LIVE PREVIEW

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


slide-1
SLIDE 1

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

slide-2
SLIDE 2

Today: eliminating bugs in low-level systems software

2

OS Kernel / security monitor Process Process Process

  • Low-level bugs: buffer overflow, div by zero
  • Logic bugs: implementation does something unintended
  • Design bugs: unintended design is insecure
slide-3
SLIDE 3

Example: undefined behavior

Question: what’s the result of mul(60000, 60000)?

  • (a) 3,600,000,000
  • (b) 18,446,744,073,014,584,320
  • (c) something else

3

uint64_t mul(uint16_t a, uint16_t b) { uint32_t c = a * b; return c; }

slide-4
SLIDE 4

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)

slide-5
SLIDE 5

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)

  • Strong correctness guarantees
  • Require manual proofs
  • CertiKOS 200k lines of proof
  • Multiple person-years
slide-6
SLIDE 6

Prior work: automated (push-button) verification

6

Nickel OSDI'18

Finite implementation Decidable specification Automated verifier

Hyperkernel SOSP'17 Yggdrasil OSDI’16

slide-7
SLIDE 7

Prior work: automated (push-button) verification

7

Specification Implementation Automated verifier

  • No proofs on implementation
  • Requires finite implementation
  • Restricts specification

SMT solver

✔ ✘

slide-8
SLIDE 8

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

✔ ✘

slide-9
SLIDE 9

Contributions

  • Serval: a framework for writing automated verifiers
  • ARM, RISC-V, x86, LLVM, BPF
  • Scaling via symbolic optimizations
  • Experience
  • Retrofitted CertiKOS and Komodo for Serval
  • Found 30+ new bugs in Linux BPF JIT and 3 in Keystone

9

no guarantees on concurrency or side channels

slide-10
SLIDE 10

Verifying a system with Serval

10

Z3

SMT solver Rosette Serval

RISC-V verifier System specification RISC-V instructions

slide-11
SLIDE 11

Verifying a system with Serval

11

Z3

SMT solver Rosette Serval

RISC-V verifier RISC-V instructions System specification

slide-12
SLIDE 12

Verifying a system with Serval

12

SMT solver Rosette Serval

RISC-V verifier RISC-V instructions

Z3

System specification

slide-13
SLIDE 13

Verifying a system with Serval

13

Z3

SMT solver Rosette Serval

RISC-V verifier RISC-V instructions System specification

slide-14
SLIDE 14

Verifying a system with Serval

14

Z3

SMT solver Rosette Serval

RISC-V verifier RISC-V instructions System specification

slide-15
SLIDE 15

Verifying a system with Serval

15

Z3

SMT solver Rosette Serval

RISC-V verifier RISC-V instructions System specification

slide-16
SLIDE 16

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

slide-17
SLIDE 17

Verifier = interpreter + symbolic optimization

17

  • 1. Write a verifier

as interpreter

  • 2. Symbolic profiling

to find bottleneck

  • 3. Apply symbolic
  • ptimizations

slide-18
SLIDE 18

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)))] ...))

slide-19
SLIDE 19

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)))] ...))

slide-20
SLIDE 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

20

slide-21
SLIDE 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

21

slide-22
SLIDE 22

(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

  • Easy to write
  • Reuse CPU test suite
slide-23
SLIDE 23

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

slide-24
SLIDE 24

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

🤰

slide-25
SLIDE 25

Verifier [2/3]: identifying bottlenecks in symbolic evaluation

25

slide-26
SLIDE 26

Verifier [2/3]: identifying bottlenecks in symbolic evaluation

26

fetch

slide-27
SLIDE 27

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)))] ...))

slide-28
SLIDE 28

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

slide-29
SLIDE 29

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)

... ...

slide-30
SLIDE 30

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

slide-31
SLIDE 31

Verifier [3/3]: Repairing with symbolic optimizations

31

  • Symbolic optimization: "peephole" on symbolic state
  • Fine-tune symbolic evaluation
  • Use domain knowledge
slide-32
SLIDE 32

Verifier [3/3]: Repairing with symbolic optimizations

32

(define (interpret c program)

  • (define pc (cpu-pc c))

(define insn (fetch pc program)) (match insn ...)) (define (interpret c program) + (serval:split-pc [cpu pc] c (define insn (fetch pc program)) (match insn ...)))

  • Match on symbolic structure of PC
  • Evaluate separately using each concrete PC value
  • Merge states afterwards
slide-33
SLIDE 33

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

slide-34
SLIDE 34

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:

  • Split PC to avoid state explosion
  • Merge other registers to avoid path explosion
slide-35
SLIDE 35

Symbolic optimizations are essential to scaling verification

  • Symbolic program counter
  • Symbolic memory address
  • Symbolic system register
  • ... and more

35

slide-36
SLIDE 36

Verifier summary

  • Verifier = interpreter + symbolic optimizations
  • Easy to test verifiers
  • Systematic way to scale symbolic evaluation
  • Caveats:
  • Symbolic profiling cannot identify expensive SMT operations
  • Repair requires expertise - recent work SymFix (VMCAI'20)

36

slide-37
SLIDE 37

Implementation

37

Z3

SMT solver Rosette Serval

RISC-V verifier x86 verifier LLVM verifier BPF verifier ARM verifier

slide-38
SLIDE 38

Experience

38

  • Can existing systems be retrofitted for Serval?
  • Are Serval’s verifiers reusable?
slide-39
SLIDE 39

Retrofitting previously verified systems

  • Port CertiKOS (PLDI’16) and Komodo (SOSP’17) to RISC-V
  • Retrofit to automated verification
  • Apply the RISC-V verifier to binary image
  • Prove functional correctness and noninterference
  • ≈4 weeks each

39

slide-40
SLIDE 40

Retrofitting overview

40

System specification System implementation

Is the specification expressible in Serval? Is the implementation free of unbounded loops?

slide-41
SLIDE 41

Example: retrofitting CertiKOS

  • OS kernel providing strict isolation
  • Physical memory quota, partitioned PIDs
  • Security specification: noninterference

41

CertiKOS Process Process Process

slide-42
SLIDE 42

Example: retrofitting CertiKOS

  • Implementation
  • Already free of unbounded loops
  • Tweak spawn to close two potential information leaks
  • Specification
  • Noninterference using traces of unbounded length
  • Broken down into 3 properties of individual “actions”

42

slide-43
SLIDE 43

Retrofitting summary

  • Security monitors good fit for automated verification
  • No unbounded loops
  • No inductive data structures

43

slide-44
SLIDE 44

Reusing verifiers to find bugs

  • Linux kernel's BPF JIT compilers
  • Found 30+ new bugs
  • Bug fixes and new tests upstreamed
  • Keystone
  • Open-source enclave platform
  • Found 3 new bugs in implementation and design

44

slide-45
SLIDE 45

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)

slide-46
SLIDE 46

Example bug found using Serval

  • Difficult to audit and test
  • Can have serious security

impact

  • Key: scale symbolic evaluation

for both JIT and emitted machine instructions

46

/* Do LSH operation */ if (val < 32) {

  • /* shl dreg_hi,imm8 */
  • EMIT3(0xC1, add_1reg(0xE0, dreg_hi), val);
  • /* mov ebx,dreg_lo */
  • EMIT2(0x8B, add_2reg(0xC0, dreg_lo, IA32_EBX));

+ /* 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);

  • /* IA32_ECX = 32 - val */
  • /* mov ecx,val */
  • EMIT2(0xB1, val);
  • /* movzx ecx,ecx */
  • EMIT3(0x0F, 0xB6, add_2reg(0xC0, IA32_ECX, IA32_ECX));
  • /* neg ecx */
  • EMIT2(0xF7, add_1reg(0xD8, IA32_ECX));
  • /* add ecx,32 */
  • EMIT3(0x83, add_1reg(0xC0, IA32_ECX), 32);
  • /* shr ebx,cl */
  • EMIT2(0xD3, add_1reg(0xE8, IA32_EBX));
  • /* or dreg_hi,ebx */
  • EMIT2(0x09, add_2reg(0xC0, dreg_hi, IA32_EBX));

}

slide-47
SLIDE 47

Conclusion

  • Writing automated verifiers as interpreters
  • A systematic method for scaling symbolic evaluation
  • Retrofit Serval to verify existing systems

47

http://serval. .org