Modular deductive verification is a well-known technique [Hoare1969] for formally proving that a program respects some user-defined properties. It consists in providing for each function of the program a contract, that basically contains a precondition describing what the function expects from its callers, and a postcondition indicating what it guarantees when it successfully returns. Logical formulas, known as verification conditions or proof obligations (POs), can then be generated and given to automated theorem provers. If all POs are validated, the body of the function fulfills its contract. Many deductive verification frameworks exist for various programming and formal specification languages. We focus here on Frama-C [framac] and its deductive verification plugin Wp, which allows proving a C code correct with respect to a formal specification expressed in ACSL [acsl].
However, encoding high-level properties spanning across the entire program in a set of Pre/Post-based contracts is not always immediate. In the end, such high-level properties get split among many different clauses in several contracts, without an explicit link between them. Therefore, even if each individual clause is formally proved, it might be very difficult for a verification engineer, a code reviewer or a certification authority to convince themselves that the provided contracts indeed ensure the expected high-level properties. Moreover, a software product frequently evolves during the software lifetime, leading to numerous modifications in the code and specifications. Maintaining a high-level (e.g. security-related) property is extremely complex without a suitable mechanism to formally specify and automatically verify it after each update. The purpose of the present work is to propose such a specification mechanism for high-level properties, which we call meta-properties, and to allow their automatic verification on C code in Frama-C thanks to a new plugin called MetAcsl.
This work was motivated by several previous projects. During the verification of a hypervisor, we observed the need for a mechanism of specification and automatic verification of high-level properties, in particular, for global properties related to isolation and memory separation. Isolation properties are known as key properties in many verification projects, in particular, for hypervisors and micro-kernels [KleinTCS14].
A similar need for specific high-level properties recently arose from a case study on a confidentiality-oriented page management system submitted by an industrial partner. In this example, each page and each user (process) are given a confidentiality level, and we wish to specify and verify that in particular:
() a user cannot read data from a page with a confidentiality level higher than its own;
() a user cannot write data to a page with a confidentialtiy level lower than its own.
This case study will be used as a running example in this paper. As a second case study (also verified, but not detailed in this paper), we consider a simple smart house manager with several interesting properties such as: “a door can only be unlocked after a proper authentication or in case of alarm” or “whenever the alarm is ringing, all doors must be unlocked”. Again, these examples involve properties that are hard to express with function contracts since they apply to the entire program rather than a specific function.111 These examples are publicly available at https://github.com/Firobe/metacsl_examples
The contributions of this paper include:
a new form of high-level properties, which we call meta-properties, and an extension of the ACSL language able to express them (Sect. 2),
a set of code transformations to translate meta-properties into native ACSL annotations that can be proved via the usual methods (Sect. 3),
a Frama-C plugin MetAcsl able to parse C code annotated with meta-properties and to perform the aforementioned code transformations (Sect. 4),
a case study: a confidentiality-oriented page system, where important security guarantees were expressed using meta-properties and automatically verified thanks to the code transformation with MetAcsl (Sect. 4).
2 Specification of Meta-properties
A meta-property is a property meant to express high-level requirements. As such, it is not attached to any particular function but instead to a set of functions. It is thus defined in the global scope and can only refer to global objects.
To define a meta-property, the user must provide (i) the set of functions it will be applied to, (ii) a property (expressed in ACSL) and (iii) the context, i.e. a characterization of the situations in which they want the property to hold in each of these functions (everywhere in the function, only at the beginning and the end, upon writing in a variable, etc.). Furthermore, depending on the context, the property can refer to some special variables which we call meta-variables. Figure 1 features a few examples of meta-properties applied to our case study. They will be explained in more details as we go along.
Let denote the set of functions defined in the current program, and the set of native ACSL properties. Formally, we can define a meta-property as a triple , where is a context, and . Intuitively, we can interpret this triple as “, holds for in the context ”. For the meta-property to be well-formed, must be a property over a subset of , where is the set of variables available in the global scope of the program and is the set of meta-variables provided by the context (see Section 2.2).
The actual MetAcsl syntax for defining a meta-property is
meta [specification of F] c, P;
An example is given by property (cf. lines 10–12 in
Figure 1), where , and is the predicate stating that the
status of any page should be either
2.1 Target Functions and Quantification
Meta-properties are applied to a given target set of functions defined as by providing explicit lists of considered and excluded functions . If not provided, and are respectively equal to and by default, i.e. the meta-property should hold for all functions of the program.
The MetAcsl syntax for the specification of uses the builtin ACSL constructions \forall, possibly followed by \subset with or without logic negation ! (to express and ). It can be observed in property (lines 13–16), where and exclude only one function.
2.2 Notion of Context
The context of a meta-property defines the states in which property must hold, and may introduce meta-variables that can be used in the definition of .
Beginning/Ending Context (Weak Invariant)
A weak invariant indicates that must hold at the beginning and at the end of each target function .
Everywhere Context (Strong invariant)
invariant is similar to a weak invariant, except that it ensures that
holds at every point222Temporal weakening for a specific
block is also supported; it can be useful e.g. to weaken the property
A==B inside the code fragment A=e;B=e;.
of each target function. For example,
property specifies that at every point of the program, the status
of any page must be either
It ensures that holds upon any modification of the memory (both stack and heap). It provides a meta-variable \written that refers to the variable (or, more generally, the memory location) being written to.
A simple usage of this context can be to forbid any direct modification
of some global variable, as in property . This property states
that for any function that is not page_encrypt,
the left-hand side of any assignment
must be separated from (that is, disjoint with) the
global variable metadata[page].level for any page with
ALLOCATED status. In other words, only the
function is allowed to modify the confidentiality level of an allocated
An important benefit of this setting is a non-transitive restriction of modifications that cannot be specified using the ACSL clause assigns, since the latter is transitive over function calls and necessarily permits to modify a variable when at least one callee has the right to modify it. Here, since we only focus on direct modifications, a call to page_encrypt (setting to pulic the level of the page it has encrypted) from another function does not violate meta-property . Furthermore, here we can forbid the modification under some condition (namely, that the page is allocated), while assigns has no such mechanism readily available.
Similar to the writing context, it ensures that the property holds whenever some memory location is read, and provides a meta-variable \read referring to the read location. It is used in property (lines 17–20), which expresses the guarantee of the case study (see Sec. 1) by imposing a separation of a read location and the contents of allocated confidential pages when the user does not have sufficient access rights. As another example, an isolation of a page can be specified as separation of all reads and writes from it.
These few simple contexts, combined with the native features of ACSL, turn out to be powerful enough to express quite interesting properties, including memory isolation and all properties used in our two motivating case studies.
3 Verification of Meta-properties
The key idea of the verification is the translation of meta-properties into native ACSL annotations, that are then verified using existing Frama-C analyzers. To that end, the property of a meta-property must be inserted as an assertion in relevant locations (as specified by context ) in each target function , and the meta-variables (if any) must be instantiated.
We define a specific translation for each context. For weak invariants,
property is simply added as both a precondition and a postcondition in the
contract of . This is also done for the strong invariant, for which
is additionally inserted after each instruction potentially modifying the
values of the free variables in . For example,
Figure 2(a) shows the translation of on
page_alloc. Our property (defined on lines 11–12 in Figure 1, denoted here)
is inserted after the modification of a
status field (line 6)
since the property involves these objects, but not after the modification of
level field (line 8).
For Writing (resp. Reading) contexts, is inserted before
any instruction potentially making a write (resp. read) access to
the memory, with the exception of
function calls. In addition, each meta-variable is replaced by its actual value.
For example, in the translation of on page_alloc
(Figure 2(b)), the property is inserted before the two
fp, and \written is replaced respectively by
fp->level. In this case does not hold.
While its first instantiation (lines 4–6) is easily proved,
it is not the case for the second
one (lines 8–10). Indeed, there exists a
page (the one being modified)
that has a status set to ALLOCATED because of the previous instruction (line 7) and
for which the \separated clause is obviously false.
Hence, the assertion fails, meaning that
the whole meta-property cannot be proved.
The fix consists in swapping lines 6 and 7 in Figure 2.
After that, all assertions generated from are proved.
A similar transformation for on
page_read shows that
the proof fails since the implementation allows an agent to read
from any page without any check. Adding proper guards allows the meta-property to be proved.
Conversely, if a meta-property is broken by an erroneous code update, a proof failure after automatically re-running MetAcsl helps to easily detect it.
4 Results on Case Studies and Conclusion
The support of meta-properties and the proposed methodology for their verification were fully implemented in OCaml as a Frama-C plugin called MetAcsl. We realized a simple implementation of the two case studies mentioned in Sect. 1) and were able to fully specify and automatically verify all aforementioned properties (in particular and ) using MetAcsl. The transformation step is performed in less than a second while the automatic proof takes generally less than a minute.
We proposed a new specification mechanism for high-level properties, as well as an automatic transformation-based technique to verify these properties by a usual deductive verification approach. Meta-properties provide a useful extension to function contracts offering the possibility to express a variety of high-level safety- and security-related properties. They also provide to a verification engineer an explicit global view of high-level properties being really proved, avoiding the risk to miss some part of an implicit property which is not formally linked to relevant parts of several function contracts, thus facilitating code review and certification. Another benefit of the new mechanism is the possibility to easily re-execute a proof after a code update, diminishing the risk of making invalid an implicit high-level property spanned over several contracts. Initial experiments confirm the interest of the proposed solution.
We plan to establish a formal soundness proof for our transformation technique, thereby allowing MetAcsl to be reliably used for critical code verification. Other future work directions include further experiments to evaluate the proposed approach on real-life software and for more complex properties.
The work of the first author was partially funded by a Ph.D. grant of the French Ministry of Armed Forces.