13 research outputs found
Featherweight VeriFast
VeriFast is a leading research prototype tool for the sound modular
verification of safety and correctness properties of single-threaded and
multithreaded C and Java programs. It has been used as a vehicle for
exploration and validation of novel program verification techniques and for
industrial case studies; it has served well at a number of program verification
competitions; and it has been used for teaching by multiple teachers
independent of the authors. However, until now, while VeriFast's operation has
been described informally in a number of publications, and specific
verification techniques have been formalized, a clear and precise exposition of
how VeriFast works has not yet appeared. In this article we present for the
first time a formal definition and soundness proof of a core subset of the
VeriFast program verification approach. The exposition aims to be both
accessible and rigorous: the text is based on lecture notes for a graduate
course on program verification, and it is backed by an executable
machine-readable definition and machine-checked soundness proof in Coq
Modular Formal Verification of Rust Programs with Unsafe Blocks
Rust is a modern systems programming language whose type system guarantees
memory safety. For the sake of expressivity and performance it allows
programmers to relax typing rules temporarily, using unsafe code blocks.
However, in unsafe blocks, the burden of making sure that the code does not end
up having undefined behaviour is on the programmer. Even most expert
programmers make mistakes and a memory safety bug in an unsafe block renders
all the type system guarantees void. To address this problem we are trying to
verify soundness of Rust unsafe code applying our Modular Symbolic Execution
algorithm. This text outlines our approach and the progress that has been made
so far.Comment: 22 pages, 13 listings, 3 figures, Technical report, Appendix by Bart
Jacob
Formalizing, Verifying and Applying ISA Security Guarantees as Universal Contracts
Progress has recently been made on specifying instruction set architectures
(ISAs) in executable formalisms rather than through prose. However, to date,
those formal specifications are limited to the functional aspects of the ISA
and do not cover its security guarantees. We present a novel, general method
for formally specifying an ISAs security guarantees to (1) balance the needs of
ISA implementations (hardware) and clients (software), (2) can be
semi-automatically verified to hold for the ISA operational semantics,
producing a high-assurance mechanically-verifiable proof, and (3) support
informal and formal reasoning about security-critical software in the presence
of adversarial code. Our method leverages universal contracts: software
contracts that express bounds on the authority of arbitrary untrusted code.
Universal contracts can be kept agnostic of software abstractions, and strike
the right balance between requiring sufficient detail for reasoning about
software and preserving implementation freedom of ISA designers and CPU
implementers. We semi-automatically verify universal contracts against Sail
implementations of ISA semantics using our Katamaran tool; a semi-automatic
separation logic verifier for Sail which produces machine-checked proofs for
successfully verified contracts. We demonstrate the generality of our method by
applying it to two ISAs that offer very different security primitives: (1)
MinimalCaps: a custom-built capability machine ISA and (2) a (somewhat
simplified) version of RISC-V with PMP. We verify a femtokernel using the
security guarantee we have formalized for RISC-V with PMP
Code-level model checking in the software development workflow at Amazon Web Services
This article describes a style of applying symbolic model checking developed over the course of four years at Amazon Web Services (AWS). Lessons learned are drawn from proving properties of numerous C‐based systems, for example, custom hypervisors, encryption code, boot loaders, and an IoT operating system. Using our methodology, we find that we can prove the correctness of industrial low‐level C‐based systems with reasonable effort and predictability. Furthermore, AWS developers are increasingly writing their own formal specifications. As part of this effort, we have developed a CI system that allows integration of the proofs into standard development workflows and extended the proof tools to provide better feedback to users. All proofs discussed in this article are publicly available on GitHub
Automated and foundational verification of low-level programs
Formal verification is a promising technique to ensure the reliability of low-level programs like operating systems and hypervisors, since it can show the absence of whole classes of bugs and prevent critical vulnerabilities. However, to realize the full potential of formal verification for real-world low-level programs one has to overcome several challenges, including: (1) dealing with the complexities of realistic models of real-world programming languages; (2) ensuring the trustworthiness of the verification, ideally by providing foundational proofs (i.e., proofs that can be checked by a general-purpose proof assistant); and (3) minimizing the manual effort required for verification by providing a high degree of automation. This dissertation presents multiple projects that advance formal verification along these three axes: RefinedC provides the first approach for verifying C code that combines foundational proofs with a high degree of automation via a novel refinement and ownership type system. Islaris shows how to scale verification of assembly code to realistic models of modern instruction set architectures-in particular, Armv8-A and RISC-V. DimSum develops a decentralized approach for reasoning about programs that consist of components written in multiple different languages (e.g., assembly and C), as is common for low-level programs. RefinedC and Islaris rest on Lithium, a novel proof engine for separation logic that combines automation with foundational proofs.Formale Verifikation ist eine vielversprechende Technik, um die Verlässlichkeit von grundlegenden Programmen wie Betriebssystemen sicherzustellen. Um das volle Potenzial formaler Verifikation zu realisieren, müssen jedoch mehrere Herausforderungen gemeistert werden: Erstens muss die Komplexität von realistischen Modellen von Programmiersprachen wie C oder Assembler gehandhabt werden. Zweitens muss die Vertrauenswürdigkeit der Verifikation sichergestellt werden, idealerweise durch maschinenüberprüfbare Beweise. Drittens muss die Verifikation automatisiert werden, um den manuellen Aufwand zu minimieren. Diese Dissertation präsentiert mehrere Projekte, die formale Verifikation entlang dieser Achsen weiterentwickeln: RefinedC ist der erste Ansatz für die Verifikation von C Code, der maschinenüberprüfbare Beweise mit einem hohen Grad an Automatisierung vereint. Islaris zeigt, wie die Verifikation von Assembler zu realistischen Modellen von modernen Befehlssatzarchitekturen wie Armv8-A oder RISC-V skaliert werden kann. DimSum entwickelt einen neuen Ansatz für die Verifizierung von Programmen, die aus Komponenten in mehreren Programmiersprachen bestehen (z.B., C und Assembler), wie es oft bei grundlegenden Programmen wie Betriebssystemen der Fall ist. RefinedC und Islaris basieren auf Lithium, eine neue Automatisierungstechnik für Separationslogik, die maschinenüberprüfbare Beweise und Automatisierung verbindet.This research was supported in part by a Google PhD Fellowship, in part by awards from Android Security's ASPIRE program and from Google Research, and in part by a European Research Council (ERC) Consolidator Grant for the project "RustBelt", funded under the European Union’s Horizon 2020 Framework Programme (grant agreement no. 683289)
Abstract Execution: Automatically Proving Infinitely Many Programs
Abstract programs contain schematic placeholders representing potentially infinitely many concrete programs. They naturally occur in multiple areas of computer science concerned with correctness: rule-based compilation and optimization, code refactoring and other source-to-source transformations, program synthesis, Correctness-by-Construction, and more. Mechanized correctness arguments about abstract programs are frequently conducted in interactive environments. While this permits expressing arbitrary properties quantifying over programs, substantial effort has to be invested to prove them manually by writing proof scripts. Existing approaches to proving abstract program properties automatically, on the other hand, lack expressiveness. Frequently, they only support placeholders representing all possible instantiations; in some cases, minor refinements are supported.
This thesis bridges that gap by presenting Abstract Execution (AE), an automatic reasoning technique for universal behavioral properties of abstract programs. The restriction to universal (no existential quantification) and behavioral (not addressing internal structure) properties excludes certain applications; however, it is the key to automation. Our logic for Abstract Execution uses abstract state changes to represent unknown effects on local variables and the heap, and models abrupt completion by symbolic branching. In this logic, schematic placeholders have names: It is possible to re-use them at several places, representing the same program elements in potentially different contexts. Furthermore, the represented concrete programs can be constrained by an expressive specification language, which is a unique feature of AE. We use the theory of dynamic frames to scale between full abstraction and total precision of frame specifications, and support fine-grained pre- and postconditions for (abrupt) completion.
We implemented AE by extending the program verifier KeY. Specifically for relational verification of abstract Java programs, we developed REFINITY, a graphical KeY frontend. We used REFINITY it in our signature application of AE: to model well-known statement-level refactoring techniques and prove their conditional safety. Several yet undocumented behavioral preconditions for safe refactorings originated in this case study, which is one of very few attempts to statically prove behavioral correctness of statement-level refactorings, and the only one to cover them to that extent.
AE extends Symbolic Execution (SE) for abstract programs. As a foundational contribution, we propose a general framework for SE based on the semantics of symbolic states. It natively integrates state merging by supporting m-to-n transitions. We define two orthogonal correctness notions, exhaustiveness and precision, and formally prove their relation to program proving and bug detection.
Finally, we introduce Modal Trace Logic (MTL), a trace-based logic to represent a variety of different program verification tasks, especially for relational verification. It is a “plug-in” logic which can be integrated on-demand with formal languages that have a trace semantics. The core of MTL is the trace modality, which allows expressing that a specification approximates an implementation after a trace abstraction step. We demonstrate the versatility of this approach by formalizing concrete verification tasks in MTL, ranging from functional verification over program synthesis to program evolution. To reason about MTL problems, we translate them to symbolic traces. We suggest Symbolic Trace Logic (STL), which comes with a sequent calculus to prove symbolic trace inclusions. This requires checking symbolic states for subsumption; to that end, we provide two generally useful notions of symbolic state subsumption. This framework relates as follows to the other parts of this thesis: We use the language of abstract programs to express synthesis and compilation, which connects MTL to AE. Moreover, symbolic states of STL are based on our framework for SE
Featherweight VeriFast
VeriFast is a leading research prototype tool for the sound modularverification of safety and correctness properties of single-threaded andmultithreaded C and Java programs. It has been used as a vehicle forexploration and validation of novel program verification techniques and forindustrial case studies; it has served well at a number of program verificationcompetitions; and it has been used for teaching by multiple teachersindependent of the authors. However, until now, while VeriFast's operation hasbeen described informally in a number of publications, and specificverification techniques have been formalized, a clear and precise exposition ofhow VeriFast works has not yet appeared. In this article we present for thefirst time a formal definition and soundness proof of a core subset of theVeriFast program verification approach. The exposition aims to be bothaccessible and rigorous: the text is based on lecture notes for a graduatecourse on program verification, and it is backed by an executablemachine-readable definition and machine-checked soundness proof in Coq