Specifying and Verifying Device Drivers - PowerPoint PPT Presentation

1 / 23
About This Presentation
Title:

Specifying and Verifying Device Drivers

Description:

Check pre- and post-conditions, kernel API. But also specify resource management, etc. ... Abstract register used to verify programs. Like the 'memory register' ... – PowerPoint PPT presentation

Number of Views:41
Avg rating:3.0/5.0
Slides: 24
Provided by: Westley7
Category:

less

Transcript and Presenter's Notes

Title: Specifying and Verifying Device Drivers


1
Specifying and VerifyingDevice Drivers
  • Wes Weimer
  • George Necula
  • Gregoire Sutre

2
Overview
  • Background and Motivation
  • Safety Properties
  • Events and History Registers
  • Driver States
  • Specifications
  • Verification
  • Implementation

3
Background and Motivation
  • Why bother?
  • PCC-style background
  • Want a sound analysis
  • Decided on an all-paths analysis
  • Similar to PREfix, ESC, Slam, Magick
  • Check pre- and post-conditions, kernel API
  • But also specify resource management, etc.
  • Greg will recast as model checking later

4
Safety Properties
  • We aim to verify that
  • Allocated resources are freed
  • Over the lifetime of the driver
  • Enforce acquire order no deadlocks
  • Kernel functions are called correctly
  • Also includes ordering requirements and
    obligations, like schedule() and
    signal_pending().
  • Certain minimum actions are performed

5
Events
  • Three broad types
  • allocation, - deallocation, ! event
  • Applied to uninterpreted constructors
  • irq, dma, memory, schedule
  • And given program values as arguments
  • irq(5), -dma(3), !schedule
  • Function call and return may be treated as events

6
History Register
  • Abstract register used to verify programs
  • Like the memory register in PCC/VCGen
  • Never appears in user-written code
  • Keeps an ever-increasing list of events
  • E.g., irq(7) !schedule misc(qpmouse)
  • May refer to it in conditions
  • E.g., PRE(!historyContains(irq(i)))

7
Driver States
S0 Uninitialized
init_module0
cleanup_module0
S1 Initialized
misc_release0
misc_open0
S2 Opened
misc_read misc_write
Inv(S0) true Inv(S1) historyContains(misc(any
))
8
Driver States (contd)
  • Kernel API guarantees a certain state-like
    behavior for device drivers
  • E.g., misc_open will only be called if the driver
    is in S1
  • We associate an invariant with each state
  • Captures history register values
  • And global variables in the driver
  • Must check all paths from S0 to S0.
  • For resource leaks, etc.
  • Similar to data-flow analysis

9
Specifications
  • Write spec in C, just like driver
  • May use data and control non-determinism
  • May use pre-conditions like assert(), but will
    not fail
  • Spec is minimal
  • List only things that must be done
  • Looks like a template device driver
  • Normal device drivers often written by copying
    others
  • Side-conditions govern optional behavior
  • Like matching alloc/free
  • Or checking signals after sleeping

10
Specification Example
  • int init_module() / user code must look like
    this /
  • PRE(!historyContains(misc(any)))
  • switch (__rand_int_range(0,3))
  • case 0 misc_register(any) / use kernel API /
  • return 0
  • case 1 return 5 / EIO /
  • case 2 return 6 / ENXIO /
  • case 3 return 19 / ENODEV /

11
Specification Example (2)
  • int misc_fasync(void fd, void filp, int on)
  • PRE(fd ! 0 filp (on 1 on 0)
  • current-gtstate TASK_RUNNING
  • historyCount(!misc_open) gt
  • historyCount(!misc_release))
  • int retval fasync_helper(fd, filp, on, any)
  • if (retval lt 0) return retval
  • else return 0

12
Kernel API Spec
  • Model behavior of kernel functions
  • Like kmalloc(), schedule(), kfree()
  • May modify history register
  • Example
  • int request_irq(int i, void f, )
  • PRE(historyCount(irq(i)) historyCount(-irq(i))
    implements(f, irq_handler) )
  • if (__rand_int_range(0,1)) return 5
  • else historyAdd(irq(i)) return 0

13
Kernel API Spec (2)
  • int kmalloc(int size, int prio)
  • PRE(size gt 0 size lt (1281024) (prio
    GFP_KERNEL prio GFP_ATOMIC) !(prio
    GFP_KERNEL prio GFP_ATOMIC))
  • if (prio GFP_KERNEL)
  • int old_state current-gtstate
  • current-gtstate TASK_INTERRUPTIBLE
  • schedule()
  • current-gtstate old_state
  • historyAdd(!signal_pending)
  • if (__rand_int_range(0,1)
  • void retval __rand_int_range(1, MAX_INT/4)4
  • historyAdd(memory(retval))
  • return retval
  • else return NULL

14
Side Conditions
  • Used to verify optional behavior
  • Like resource allocation, scheduling
  • Conditions on the history register
  • Examples
  • Every !schedule must match a !signal_pending
  • After !must_return(-512), must return 512
  • All irq() before any dma()
  • All irq() occur after !misc_open

15
Story so far
  • We have the user source code
  • We have the history register abstraction
  • Keep track of the past, etc.
  • We have a spec for the user code
  • Minimal, lists things that must be done
  • We have a spec for the kernel API
  • We have side conditions

16
Verification
  • When does the user code meet the spec?
  • Each user function F matches up (defined next)
    with our spec for function F
  • If the user calls a kernel function with a
    pre-condition, the condition will always be
    satisfied
  • All of the side conditions hold over the life of
    the device driver

17
Matching Up
  • Can be phrased as a trace inclusion problem
  • More on that with the next speaker
  • Well do it as an all-paths analysis
  • Symbolically execute all paths in user code
  • Keep track of history register
  • Remove resource(X) and resource(X)
  • Make sure other important events match

18
How to Verify
  • Symbolically execute all paths in user code
  • End up with a set of final states U
  • Verify pre-conditions, use theorem prover
  • Symbolically execute all paths in spec
  • End up with a set of final states S
  • For every uÎU, find an sÎS such that
  • They have the same return value
  • Their history registers match up
  • If s proves T then u proves T (for a few global
    Ts)

19
How to Verify States
  • Start in S0, then check init_module()
  • Match each final state against the spec
  • Check all side-conditions in each final state
  • Gather all states R with return value 0
  • Their LUB becomes the invariant for S1
  • Analyze all paths out of S1 ( symbolically
    execute those functions with that invariant)
  • Repeat until this terminates.

20
What can go wrong?
  • User code can call a kernel function and fail to
    meet the pre-condition
  • User code can fail to meet a side condition
  • User code can fail to match up to the spec
  • If so, report an error!
  • And hope that its not a false positive.

21
Loop Invariants
  • Used in symbolic execution to model loops
  • Currently, we guess true
  • or use any other heuristic (e.g., on for loops)
  • Except for the history register
  • Gather all history changes in the loop body
  • Add a special history element
  • X_copies_of(history_element_list)

22
Does it work?
  • Sample implementation
  • Tested on init_module() in 46 misc device drivers
    (about 300k post-processed each)
  • 30 successful terminations (25 meet spec)
  • Others look fine, spec needs to be refined
  • 16 no answer after 45 seconds
  • All-paths analysis is expensive O(2n)

23
Conclusions and Future Work
  • Method for specifying and verifying
  • Spec looks like original code
  • Sound, incomplete, expensive
  • Uses abstract events
  • Captures allocation, function ordering, other API
    and behavioral restrictions
  • Next steps
  • folding similar paths
  • handling concurrency (e.g., irq handlers)
Write a Comment
User Comments (0)
About PowerShow.com