Extensible Verification of Untrusted Code Bor-Yuh Evan Chang, Adam Chlipala, Kun Gao, George Necula, and Robert Schneck May 14, 2004 OSQ Retreat Santa Cruz, California Type Safety as an Assurance Mechanism type checker code OR untrusted trusted • Type safety is an accepted assurance mechanism • Today – Good: Type check source code in a strongly-typed high-level language (e.g. ML, C#, Java) – Better: Type check intermediate code (e.g., MS-CLI, JVML) 5/14/2004 2 Type Systems Today (JVML,MS-CLI,TAL) • Hard-wired in the verifier • High-level and tailored to particular source languages – E.g, built-in object-oriented features • Hard to compile other source languages – Unnatural, loss of performance or expressiveness – E.g, ML ! JVML or Java ! TAL • Under constant pressure to become more complex – To handle more source languages – To be able to check more complex code (e.g. optimizations) 5/14/2004 3 Generality by Customization • MS-CLI is designed for a multiple languages, but – quite complex (e.g. 6-9 versions of function call) – not complex enough (the ILX project adds more calls) • Still not general enough! – JVML: native code interface – MS-CLI: unverifiable subset of the language • Proposal: Allow multiple type systems and other verification methods (e.g. PCC) to co-exist – Fix only the safety policy (e.g. memory safety) – Not the enforcement mechanism (e.g. a type system) 5/14/2004 4 Design Goals of the Open Verifier • Should be easy to develop “verifier extensions” – Only incrementally more complicated than a conventional verifier for the same language – Be able to retrofit existing compilers or conventional verifiers • Client should have complete control over the type system or other conventions – Calling conventions, exceptions, stack usage, data layout 5/14/2004 5 Bytecode Verification Example 1 rrv à 0 len : list ! int 0 T 2 rarg = 0 ::= int | list | nelist F 3 rrv à rrv + 1 4 rarg à m[rarg] 5 jump [rra] 5/14/2004 6 Bytecode Verification Example arg : list Why? 1 rrv à 0 arg : list Æ rv : int T 2 rarg = 0 F arg : list Æ rv : int arg : nelist Æ rv : int 3 rrv à rrv + 1 4 rarg à m[rarg] arg : nelist Æ rv : int 5 jump [rra] 5/14/2004 7 Bytecode Verifier Correctness • How do we know the verifier didn’t forget any checks? – Should not need to provide proofs involving the model of the machine semantics • Use strongest post-condition generation to eliminate proof obligations about machine transitions – Show at each program point i with next states N, post(Ii) ) Çj2N Ij 5/14/2004 8 Verified Bytecode Verification I1 = arg : list Why? 1 rrv à 0 I2 = arg : list Æ rv : int T 2 rarg = 0 post(I1) F pc = 2 Æ arg : list Æ rv = 0 3 rrv à rrv + 1 post(I1) ) I2 4 rarg à m[rarg] new I2 = post(I1) with add (rv : int) by imm_int with drop (rv = 0) 5 jump [rra] 5/14/2004 9 Branch arg : list Why? 1 rrv à 0 I2 = arg : list Æ rv : int T 2 rarg = 0 F I3 = arg : nelist Æ rv : int I5 = arg : list Æ rv : int 5 jump [rra] 5/14/2004 post(I2) pc = 3 Æ arg : list Æ rv : int Æ arg 0 Ç pc = 5 Æ arg : list Æ rv : int Æ arg = 0 3 rrv à rrv + 1 4 post(I2) ) I3 Ç I5 case post(I2) of where ](pc = 3) ) new I3 = Ifalse with false m[r rarg Ià arg let h,h’ = find (arg : list), find (arg 0) in add (arg : nelist) by (cons h h’) with drop (arg : list) with drop (arg 0) | Itrue where (pc = 5) ) new I5 = Itrue with drop (arg = 0) 10 Arithmetic arg : list Why? 1 rrv à 0 arg : list Æ rv : int T 2 rarg = 0 F I3 = arg : nelist Æ rv : int arg : list Æ rv : int 5 jump [rra] 5/14/2004 3 rrv à rrv + 1 post(I3) 9rv3. pc = 4 Æ arg : nelist Æ rv3 : int Æ rv = rv3 + 1 I4 = arg : nelist Æ rv : int 4 rarg à m[rarg] post(I3) ) I4 new I4 = post(I3) with let h = find (rv3 : int) in add (rv : int) by (plus h (imm_int)) with drop (rv3 : int) with drop (rv = rv3 + 1) 11 Memory Read arg : list F = pc = err Æ ? Why? lem1: If (arg : nelist) then (addr arg) lem2: If (arg : nelist) then (m[arg] : list) 1 rrv à 0 I2 = arg : list Æ rv : int T 2 rarg = 0 F arg : list Æ rv : int 5 jump [rra] 5/14/2004 post(I4) 9 arg4. pc = 2 Æ arg4 : nelist Æ rv : int Æ addr arg4 Æ arg = m[arg4] Ç arg : nelistpcÆ=rverr: int Æ arg : nelist Æ rv : int Æ : (addr arg) 3 rrv à rrv + 1 post(I4) ) I2 Ç F I4 = arg case : nelist Æ rv) :of int post(I 4 rarg à m[rarg] 4 Iok where _ ) Iok with let h = find (arg4 : nelist) in add (arg : list) by (lem2 h) with … | Ierr where : (addr arg) by bad ) Ierr with let h = … in add ? by (bad (lem1 h)) with … 12 Customizability Yields Controlled Trust Trust Any Binary (*.exe) Trust Compilers (javac) Trust Specialized Verifiers (JVM) Trust Typing Rules Trust Model of Machine Semantics Trust Meter 5/14/2004 13 Summary • Provide a “natural” language for the verifier to describe in a checkable form the reasoning it makes – Close to conventional type-based verifiers – Amenable to other safety enforcement mechanisms, such as PCC • PCC – Most of the time, just goes along with post(¢) – When proof is required, give the proof attached with the code – Proof representation can be chosen by the client 5/14/2004 14 Summary post(¢) type checker code OR “preservation and progress” untrusted trusted • Towards practical extensible verification of untrusted code – Ask only for reasoning already done by conventional verifiers 5/14/2004 15 Conclusion • We have verifier extensions for – Cool (“mini-Java”) [Aiken et al.] • can verify output of existing Cool compilers – TAL (functional typed assembly language) • retrofitted TALx86 [Morrisett et al.] – PCC • Philosophy: – To verify complete software systems, • not a general, super-expressive type system • rather a framework to allow existing ones to work together 5/14/2004 16 Return R = pc = ra0 Æ rv : int arg : list Why? 1 rrv à 0 arg : list Æ rv : int T 2 rarg = 0 F I5 = arg : list Æ rv : int post(I5) arg : nelist Æ rv : int 3 rrv à rrv + 1 4 rarg à m[rarg] pc = ra0 Æ arg : list Æ rv : int arg : nelist Æ rv : int post(I5) ) R post(I5) with drop (arg : list) 5 jump [rra] 5/14/2004 18
© Copyright 2026 Paperzz