From 40e17041887eed3e6fcde597ba891f79fa4e2a27 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Tue, 11 Jan 2022 14:53:25 -0800 Subject: [PATCH 01/20] added a README describing the Heapster implication rules --- .../src/Verifier/SAW/Heapster/README.md | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 heapster-saw/src/Verifier/SAW/Heapster/README.md diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md new file mode 100644 index 0000000000..f76beb13eb --- /dev/null +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -0,0 +1,183 @@ + +# Heapster Developer Documentation + +This directory contains an implementation of the Heapster portion of SAW. + +## Overall Code Structure + +The central components of Heapster are in the following files: + +* Permissions.hs: This file defines the language of _permissions_, which are the types in Heapster. Heapster permissions are defined by the `ValuePerm` datatype, which is defined mutually with the type `PermExpr` of Heapster expressions. + +* Implication.hs: This file defines the concept of _permission implication_ in Heapster, which is a form of subtyping on the Heapster permission types. Permission implication is defined by the `PermImpl` type, which represents a proof that one permission implies, or is a subtype of, another. This file also contains the implication prover, which is the algorithm that attempts to build permission implication proofs. The implication prover is described in more detail below. + +* TypedCrucible.hs: This file defines a version of Crucible control-flow graphs (CFGs) that have been type-checked by Heapster. Each Crucible data type used to define CFGs, including the type `CFG` itself, has a corresponding data type in TypedCrucible.hs with `"Typed"` prefixed to its name. This includes the `TypedCFG` type used to represent an entire typed-checked CFG. This file also contains the function `tcCFG` that performs type-checking on a Crucible CFG, along with helper functions used to type-check the various component pieces of Crucible CFGs. + +* SAWTranslation.hs: This file defines the translation from type-checked Crucible CFGs to SAW core terms that represent their specifications. + +FIXME: describe the other files + + +## Heapster Permission Implication + +Heapster permission implication is a form of _affine logic_, which in turn is a form of the better-known concept of linear logic. Linear logic is a logic where each proposition assumed by a proof must be used exactly once in that proof. Propositions in linear logic can thus be viewed as a form of "resource" that gets used up in building a proof. For example, consider the rule + +``` +dollar * dollar * dollar |- candy_bar +``` + +to represent the concept that a particular candy bar costs $3. Intuitively, the `dollar` proposition represents possession of a dollar, `candy_bar` represents possession of a (reasonably fancy) candy bar, and `*` represents the conjunction of two propositions. A "proof" using this rule consumes three `dollar` propositions and generates one `candy_bar` proposition, intuitively representing the purchase of this candy bar. Importantly, unlike standard classical or even intuitionistic logic, where `P and P` is equivalent to `P`, the conjunction `P * P` in linear logic represents two copies of the proposition `P`, which in general is different than `P` by itself; e.g., if we could prove `dollar |- dollar * dollar` then we could generate all the money we wanted. This is not to say that `P |- P * P` is never true, just that it is only true for some `P`, which correspond to resources that can be duplicated. See any introduction to linear logic for more details. + +Affine logic is a version of linear logic where propositions can be "thrown away", that is, where the rule `P * Q |- P` holds for all `P` and `Q`. The reason we use affine logic here is that it is useful for describing a notion of _permissions_, where each `P` intuitively corresponds to permission to perform a particular action. It is always justified to forget some permission if you are not going to use it, but you can't in general give yourself more permissions. One of the central permissions used in Heapster is the permission to access memory through a particular pointer. The simplest form of this is the pointer permission `x:ptr((rw,off) |-> p)`, that represents a permission to read — and possibly write, depending on `rw` — memory at offset `off` from `x`, along with permission `y:p` to whatever value `y` is currently stored there. The `array` and `memblock` permissions also represent different forms of permission to read and possibly write memory, with different stipulations on the permissions held for the values currently stored there. Read-only permissions are copyable, meaning that `x:ptr((R,off) |-> p) |- x:ptr((R,off) |-> p) * x:ptr((R,off) |-> p)` can be proved in Heapster, as long as `p` does not contain any write permissions, while write permissions `x:ptr((W,off) |-> p)` are not. This corresponds to the one-writer or multiple readers paradigm of, e.g., Rust. + +The remainder of this section explains Heapster implication in more detail and then describes the permission implication prover algorithm used by Heapster used to prove these implications. + + +### Permission Implication Rules + +At any given point during type-checking and/or implication proving, Heapster maintains a _permission set_ that describes the current permissions to various objects that are currently held by the program. Permission sets are defined by the `PermSet` type in Permissions.hs, and have two components: the _permissions map_, which maps each variable `x` in scope to what is called the _primary_ permission held on `x`; and the _permissions stack_, which represents the permissions that are actively being used or manipulated. We write a permission set as: + +``` +x1 -> Px1 * ... * xm -> Pxm; y1:P1 * ... * yn:Pn +``` + +The portion before the semicolon represents the permissions map, which maps each `xi` to its primary permission `Pxi`, while the portion after the semicolon represents the permissions stack, containing permissions `y1:P1` through `yn:Pn` in sequence. The final permissions `yn:Pn` is the top of the stack. We often write `PS` for a permission set. + +The general form of permission implication is the judgment + +``` +PS |- (z1_1, ..., z1_k1 . PS1) \/ ... \/ (zn_1, ..., zn_kn . PSn) +``` + +which says that, starting with permission set `PS` on the left-hand side of the turnstyle `|-`, we can prove one of the permission sets `PSi` on the right-hand side. Each disjunctive case could introduce 0 or more existential variables `zi_1, ..., zi_ki`, which can be used in the corresponding permission set `PSi`. We often omit the permissions map and/or the existential variables when they are not necessary; e.g., we write `PS1 |- PS2` instead of `PS1 |- ( [] . PS2)`. We also tend to omit the permissions map from implications, as permissions maps almost never change; thus, e.g., we might write `x:p |- y:q` instead of `x1 -> Px1 * ... * xm -> Pxm; x:p |- x1 -> Px1 * ... * xm -> Pxm; y:q`. + +Permission implication in Heapster is actually a sort of "partial implication". The above-displayed implicaiton judgment in fact says that, if we hvae permission set `PS`, we can _try_ to get one of the permission sets `PSi`, though we can't control which one we get, and we might fail. What this failure means exactly is a little hard to define without going into the details of the translation to SAW core / Coq and relationship between the resulting term and the original program. As one way to understand what failure means here, consider that each permission set `PS` actually describes a set of possible states, one for each substitution of values for all the free variables in `PS`. For some of these states, we can exchange the permissions in `PS` for the permissions in one of the `PSi`, though in some of those states, trying to do this leads to undefined behavior, or at least behavior we are not going to reason about. Another way to think about Heapster implication is to always add an extra disjunction `\/ dunno` to each right-hand side, so an implication `PS |- PS1 \/ ... \/ PSn` becomes `PS |- PS1 \/ ... \/ PSn \/ dunno`, meaning that from permissions `PS` we either can get one of the `PSi` or we get a result that says that we have to give up on modeling the current execution of the program. At a slightly more technical level, failure means means that the translation of a failure is just the `errorM` computation, which, again, doesn't mean that the original computation actually has an error, just that we don't know how to reason about it. Either way, we will simply say "prove" or "implies" below instead of something like "partially prove in some states". + +Permission implications are built up from two rules, the identity rule and the step rule. The identity rule is just a proof that `PS |- PS`. The step rule looks like this: + +``` +PS |-(1) (zs1.PS1) \/ ... \/ (zsn.PSn) +PS1 |- (zs1_1.PS1_1) \/ ... \/ (zs1_k1.PS1_k1) +... +PSm |- (zsm_1.PSm_1) \/ ... \/ (zsm_km.PSm_km) +----------------------------------------------------- +PS |- (zs1_1.PS1_1) \/ ... \/ (zs1_k1.PS1_k1) \/ ... \/ (zsm_1.PSm_1) \/ ... \/ (zsm_km.PSm_km) +``` + +Intuitively, this says that we can start with an implication and then apply further implications to each of the output permission sets of the original implication, yielding a bigger implication of all of the disjuncts returned by all of the further implications. The notation `|-(1)` denotes a single step of implication, which is built using one of the single-step rules that we describe below. Intuitively, this means that a permission implication can be viewed as a tree, whose leaves are identity rules and whose internal nodes are step rules whose shapes are defined by the single step `|-(1)` implication. + +Permission implications are represented in Haskell by the type `PermImpl r ps`. The type variable `ps` is a Haskell datakind that specifies a sequence of Crucible types for the variables and permissions on the stack at the beginning of the proof. For example, the representation of an implication `x1:p1 * ... * xn:pn |- PS1 \/ ... \/ PSn` will have type `PermImpl r (RNil :> t1 :> ... :> tn)` in Haskell, where each `xi` has Crucible type `ti` and each `pi` has the corresponding Crucible type `ValuePermType ti` (which is the type of a permission that applies to an element of type `ti`). The datakind `RNil` is the empty sequence. (The "R" stands for "right-associated list", because the cons operator `:>` adds new list elements on the right instead of the left; this datakind is defined in the Hobbits library, but is identical to the one defined in Data.Parameterized.Ctx.) + +In addition to describing the form of an implication, the `PermImpl` representation in Haskell also contains a "result" for each output permission set. That is, permission implications are a form of tree, as described above, and the `PermImpl` type stores results `r1, ..., rn` at each leaf `PS1, ..., PSn` in an implication of `PS |- PS1 \/ ... \/ PSn`. The result type is given by the `r` argument to `PermImpl`, and this type is parameterized by the datakind corresponding to the types of the permissions on the stack at that point. That is, a permission implication `PS |- PS1 \/ ... \/ PSn` will contain elements of type `r ps1` through `r psn`, assuming that each `psi` is the Haskell datakind that represents the stack for each `PSi`. Intuitively, the result does something with the permissions `PSi`. The most common example of this is in the typed representation of functions used in TypedCrucible.hs, where a function can contain a permission implication, using the `TypedImplStmt` constructor, to coerce the permissions it currently holds to some form that is needed to perform an operation. For instance, a `load` instruction requires the permissions currently held by a program to be coerced to a `ptr` permission. Whenever an implication `PS |- PS1 \/ ... \/ PSn` occurs in a typed Crucible representation, the remaining instructions must be type-checked relative to each of the permission sets `PSi`. This is represented by having the `PermImpl` representation contain one copy of the remaining instructions for each output `PSi`, type-checked relative to that permission set. + +The one-step implication rules defined by the `|-(1)` judgment are defined by the `PermImpl1 ps_in ps_outs` type, which represents a rule with input stack described by datakind `ps_in` and 0 or more disjunctive output stacks given by `ps_outs`, which is a list of 0 or more disjuncts that bind 0 or more existential variables and leave 0 or more types on the stack. (See the documentation of `PermImpl1` for more details.) These include the following rules (along with a few more that we do not discuss here): + +| Rule name | Rule description | Rule implication | +----------|-------------|-----------------| +| `Impl1_Fail` | Failure of implication | `ps \|-(1) empty` (where `empty` is 0 disjuncts) | +| `Impl1_Catch` | Try one implication and then a second if the first fails | `ps \|-(1) ps \/ ps` | +| `Impl1_Push` | Push the primary permission for `x` onto the stack | `..., x -> p; ps \|-(1) ..., x -> true; ps * x:p` | +| `Impl1_Pop` | Pop the top of the stack back to the primary permission for `x` | `..., x -> true; ps * x:p \|-(1) ..., x -> p; ps` | +| `Impl1_ElimOr` | Eliminate a disjunction on the top of the stack | `ps * x:(p1 or p2) \|-(1) (ps * x:p1) \/ (ps * x:p2)` | +| `Impl1_ElimExists` | Eliminate an existential on the top of the stack | `ps * x:(exists z.p) \|-(1) (z. ps * x:p)` | +| `Impl1_Simpl` | Apply a simple implication of the form `ps1 \|- ps2` | `ps * ps1 \|-(1) ps * ps2` | +| `Impl1_ElimLLVMFieldContents` | Extract the contents of an LLVM pointer permission | `ps * x:ptr((rw,off) -> p) \|-(1) y. ps * x:ptr((rw,off) -> eq(y)) * y:p` | + +FIXME: explain the above rules! + +The most common implication rule is `Impl1_Simpl`, which applies a "simple" implication rule that exactly only one disjunctive output permission and binds no variables. The simple implication rules are described by the type `SimplImpl ps_in ps_out`. A rule of this type assumes that permissions `ps_in` are on the top of the stack, though there can be more permissions below these on the stack. It then consumes `ps_in`, replacing them with permissions `ps_out`. (As above, the `ps_in` and `ps_out` type arguments in Haskell are actually datakinds capturing the types of the input and output permissions of the rule.) These include too many rules to list here, so we only describe enough of them to give a flavor of what they do. + +Some of the simple implication rules are structural. These include the following: + +| Rule name | Rule description | Rule implication | +----------|-------------|-----------------| +| `SImpl_Drop` | Drop a permission | `x:p \|- .` | +| `SImpl_Copy` | Copy any permission that is copyable, i.e., satisfies `permIsCopyable` | `x:p \|- x:p * x:p` | +| `SImpl_Swap` | Swap the top two permissions on the stack | `x:p1 * y:p2 \|- y:p2 * x:p1` | +| `SImpl_MoveUp` | Move a permission towards the top of the stack | `x:p * ps1 * ps2 \|- ps1 * x:p * ps2` | +| `SImpl_MoveDown` | Move a permission away from the top of the stack | `ps1 * x:p * ps2 \|- x:p * ps1 * ps2` | +| `SImpl_IntroConj` | Prove an empty conjunction (which is the same as `true`) | `. \|- x:true` | +| `SImpl_ExtractConj` | Extract the `i`th conjunct of a conjunction | `x:(p0 * ... * p(n-1)) \|- x:pi * x:(p0 * ... p(i-1) * p(i+1) ... * p(n-1))` | +| `SImpl_CopyConj` | Copy the `i`th conjunct of a conjunction, assuming it is copyable | `x:(p0 * ... * p (n-1)) \|- x:pi * x:(p0 * ... * p(n-1))` | +| `SImpl_InsertConj` | Insert a permission into a conjunction | `x:p * x:(p0 * ... * p(n-1)) \|- x:(p0 * ... * p(i-1) * p * pi * ... * p(n-1))` | +| `SImpl_AppendConjs` | Combine the top two conjunctions on the stack | `x:(p1 * ... * pi) * x:(pi+1 * ... * pn) \|- x:(p1 * ... * pn)` | +| `SImpl_SplitConjs` | Split the conjunctive permissions on the top of the stack in two | `x:(p1 * ... * pn) \|- x:(p1 * ... * pi) * x:(pi+1 * ... * pn)` | + + +The elimination rules for disjunctions and existentials are `PermImpl1`s, because the former has multiple disjuncts and the latter introduces local variables, but their introduction rules are simple implications, as are both the introduction and elimination rules for named permissions: + +| Rule name | Rule description | Rule implication | +----------|-------------|-----------------| +| `SImpl_IntroOrL` | Prove a disjunctive permission from its left disjunct | `x:p1 \|- x:(p1 or p2)` | +| `SImpl_IntroOrR` | Prove a disjunctive permission from its right disjunct | `x:p2 \|- x:(p1 or p2)` | +| `SImpl_IntroExists` | Prove an existential permission from a substitution instance | `x:[e/z]p \|- x:(exists z.p)` | +| `SImpl_FoldNamed` | Prove a named permission from its unfolding | `x:(unfold P args) \|- x:P` | +| `SImpl_UnfoldNamed` | Eliminate a named permission by unfolding it | `x:P \|- x:(unfold P args)` | + + +Equality permissions are manipulated with the following simple implication rules: + +| Rule name | Rule description | Rule implication | +----------|-------------|-----------------| +| `SImpl_IntroEqRefl` | Prove any `x` equals itself | `. \|- x:eq(x)` | +| `SImpl_InvertEq` | Prove if `x` equals `y` then `y` equals `x` | `x:eq(y) \|- y:eq(x)` | +| `SImpl_InvTransEq` | Prove that if `x` and `y` equal the same `e` then they equal each other | `x:eq(e) * y:eq(e) \|- x:eq(y)` | +| `SImpl_LLVMWordEq` | If `y` equals `e` then `llvmword(y)` equals `llvmword(e)` | `x:eq(llvmword(y)) * y:eq(e) \|- x:eq(llvmword(e))` | +| `SImpl_LLVMOffsetZeroEq` | Offsetting an LLVM value by `0` preserves equality | `. \|- x:eq(x &+ 0)` | +| `SImpl_InvertLLVMOffsetEq` | Subtract an offset from both sides of an LLVM value equality | `x:eq(y+off) \|- y:eq(x-off)` | +| `SImpl_Cast` | Cast the variable of a permission using an equality | `x:eq(y) * y:p \|- x:p` | +| `SImpl_CastPerm` | Cast a permission `p` to `p'`, assuming that `x1=e1`, ..., `xn=en` imply that `p=p'` | `x1:eq(e1) * ... * xn:eq(en) * x:p \|- x:p'` | + + + +- Implementation of the rules: `simplImplIn` and `simplImplOut`, `applyImpl1`: these all check for correct perms + +- Explain overall pattern of the simplimpl rules: intro vs elim rules for most constructs + + +### The Implication Prover Algorithm + +The goal of the implication prover is to search for a permission implication proof using the implication rules discussed above. + + +Explain algorithm: + +- `ImplM` monad captures the form and types of perms on the stack; also has a notion of existential variables that need to be instantiated, some of which already are; display the `ImplM` type vars + +- Main entrypoints: `proveVarImpl`, `proveVarsImpl`, and `proveVarsImplAppend`; all written in terms of `proveVarsImplAppend` + +- `proveVarsImplAppend`: first tries to do the proof without ending any lifetimes, and if that fails it tries to end lifetimes that could potentially help with the proof + +- The "internal-only" functions `proveVarsImplInt` and `proveVarsImplAppendInt`: repeatedly call `findProvablePerm` to choose the "best" permission on the RHS to attempt to prove; briefly explain that will only select a permission whose needed variables are all instantiated + +- Next level down is `proveExVarImpl`, which handles the possibility that we are trying to prove a permission `x:p` where `x` itself is an existential variable; this is not really all that common, except for some very specific cases that come up in situtations that are a little too detailed to go into here. + +The main logic for proving most of the permission constructs is in the next function down, `proveVarImplInt`, which proves a single permission on a variable `x`. It does this by first pushing the primary permsisions `p` for `x` onto the top of the stack and then proving an implication `x:p |- x:mb_p`, where `mb_p` is the desired permission that we want to prove for `x`. The "mb" prefix indicates that `mb_p` is a permission inside a multi-binding, i.e., a binding of 0 or more existential variables that could be used in the permission we are trying to prove. The `proveVarImplInt` then proceeds by case analysis on `p` and `mb_p`, in most cases using either an introduction rule for proving a construct in `mb_p` on the right or an elimination rule for eliminating a construct in `p` on the left combined with a recursive call to `proveVarImplInt` to prove the remaining implication for smaller `p` and/or smaller `mb_p`. The most complex cases are for proving an equality permission on the right or for proving an implication `x:p1 * ... * pn |- x:p1' * ... * pm'` between conjuncts permissions. For an equality permission on the right, the special-purpose helper function `proveVarEq` is used to prove equality permissions `x:eq(e)` based on the structure of `e`. For proving an implication of conjuncts, the function `proveVarConjImpl` is used, which is structured in a similar manner to `proveVarsImplAppendInt`, in that it repeatedly chooses the "best" permission on the right to prove, proves it, and then recursively proves the remaining permissions on the right until they have all been proved. + +The `proveVarAtomicImpl` function is called by `proveVarConjImpl` to prove each conjunct. This function performs + + + +FIXME: put this discussion about needed and determined variables into the description of proveVarsImplAppendInt + +There are a number of difficulties in doing this proof search which must be addressed by the implication prover. The first is that existential permissions mean we do not have most general types; e.g., there are two distinct ways to prove + +``` +x:ptr((R,0) |-> true) * x:ptr((R,8) |-> true) |- exists off:bv 64. x:ptr((R,off) |-> true) +``` + +The difficulty is that if we choose the wrong value for `off` we might have to backtrack, potentially leading to an exponential search. The Heapster implication prover addresses this problem by requiring that all existential variables that could lead to this sort of problem in a permission `P` are assigned a uniquely determined value before it attempts to satisfy permission `P`. These variables are called the _needed_ variables of `P`, defined by the `neededVars` function in Permissions.hs, and include any variables in the offsets and lengths of pointer, array, and block permissions, among others. In our example above, `off` is a needed variable on the right-hand side, so the implication prover will not + +so, because there is no the implication prover will not prove this implication, but will instead raise an error. (NOTE: ) + +The only way to prove a permission with needed variables is if there is some other permission which is proved first that _determines_ the value of that variable. A permission `P` determines an existential variable `x` iff there is only one possible value of `x` for which `P` can possibly be proved. The canonical example of determination is the permission `eq(x)`: the only possible way to prove `y:eq(x)` is if `x` is set to `y`. + +Returning to + +## Crucible Type-checker + +FIXME + +## SAW Translator + +FIXME From 89a9a2200bce543894262b337e15dffe5e75a37e Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Mon, 17 Jan 2022 15:51:30 -0800 Subject: [PATCH 02/20] started filling out the description of the implication prover --- .../src/Verifier/SAW/Heapster/README.md | 82 +++++++++++++++++-- 1 file changed, 75 insertions(+), 7 deletions(-) diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md index f76beb13eb..315db716ea 100644 --- a/heapster-saw/src/Verifier/SAW/Heapster/README.md +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -137,20 +137,88 @@ Equality permissions are manipulated with the following simple implication rules ### The Implication Prover Algorithm -The goal of the implication prover is to search for a permission implication proof using the implication rules discussed above. +The goal of the implication prover is to search for a permission implication proof using the implication rules discussed above. The implication prover is not complete, however, meaning that there are proofs that can be made with the implication rules that the implication prover will not find. To date, work on the implication prover has focused on heuristics to make it work in practice on code that comes up in practice. +#### The Implication Prover Monad -Explain algorithm: +The implication prover runs in the `ImplM` monad, whose type parameters are as follows: -- `ImplM` monad captures the form and types of perms on the stack; also has a notion of existential variables that need to be instantiated, some of which already are; display the `ImplM` type vars +``` +ImplM vars s r ps_out ps_in a +``` + +An element of this type is an implication prover computation with return type `a`. The type variable `vars` lists the types of the existential variables, or _evars_, in scope. These represent "holes" in the permissions we are trying to prove. The type variables `s` and `r` describe the calling context of this implication computation at the top level: `s` describes the monadic state maintained by this calling context, while `r` describes the top-level result type required by this context. These types are left abstract in all of the implication prover. + +The type variables `ps_in` and `ps_out` describe the permission stack on the beginning and end of the computation. The existence of these two variables make `ImplM` a _generalized_ monad instead of just a standard monad, which means that these types can vary throughout an implication computation. The bind for `ImplM` is written `>>>=` instead of `>>=`, and has type + +``` +(>>>=) :: ImplM vars s r ps' ps_in a -> (a -> ImplM vars s r ps_out ps' b) -> ImplM vars s r ps_out ps_in b +``` + +That is, the bind `m >>>= f` first runs `m`, which changes the permissions stack from `ps_in` to `ps'`, and then it passes the output of `m` to `f`, which changes the permissions stack from `ps'` to `s_out`, so the overall computation changes the permissions stack from `ps_in` to `ps_out`. As a more concrete example, the computation for pushing a permission onto the top of the stack is declared as + +``` +implPushM :: HasCallStack => NuMatchingAny1 r => ExprVar a -> ValuePerm a -> + ImplM vars s r (ps :> a) ps () +``` + +meaning that `implPushM` takes in a variable `x` and a permission `p` and returns a computation that starts in any permission stack `ps` and pushes permission `x:p` of type `a` onto the top of the stack. + + +#### The Top-Level Implication Prover Algorithm + +The main top-level entrypoints of the implication prover is `proveVarsImplAppend`, with type + +``` +proveVarsImplAppend :: Mb vars (DistPerms ps) -> ImplM vars s r (ps_in :++: ps) ps_in () +``` + +This function attempts to prove `n` permisisons `x1:p1, ..., xn:pn`, adding those permissions to the top of the permissions stack. These permissions are inside of a binding for the existential variables specified by `vars`, which represent "holes" or unknown expressions that will be solved by building the proof. As an example, the type-checker for the pointer read instruction calls the implication prover with the existentially quantified permission + +``` +(rw,l,z). [l]ptr((rw,0) |-> eq(z)) +``` + +expressing that it requires a pointer permission at offset 0 with any lifetime `l`, any read/write modality `rw`, that points to any value `z`. + +There are a number of wrapper functions that call `proveVarsImplAppend`, including: + +* `proveVarsImpl`, which assumes the input permission stack is empty; +* `proveVarImpl`, which proves one permission; and +* `proveVarsImplVarEVars`, which is like `proveVarsImpl` but where all existential variables are instantiated with fresh variables. + +The top-level implication prover algorithm is then implemented as a descending sequence of "levels", each of is implemented as a function that performs some particular function and then calls the next level: + +| Function Name | Purpose | +--------------|----------| +| `proveVarsImplAppend` | Try to prove the required permissions, and, if that failos, non-deterministically end some lifetimes that could help in the proof | +| `proveVarsImplAppendInt` | Repeatedly call `findProvablePerm` to find the permission on the right that is most likely to be provable and then try to prove that permission | +| `proveExVarImpl` | Handle the case of a right-hand permission `x:p` where `x` itself is an evar by instantiating `x`, if possible | +| `proveVarImplInt` | Wrapper function that pushes the primary permissions for `x` onto the top of the stack, performs debug tracing, calls `proveVarImplH`, and then checks that the proved permission is correct | + + +#### Proving a Single Permissino + +The main logic for proving a permission is in the function `proveVarImplH`. (The implication prover uses the convention of using "`H`" as a suffix for helper functions.) As with many functions in the implication prover, this function takes in: a variable `x` that we are trying to prove a permission on; a permission `p` for `x` which is currently on top of the stack; and a permission `mb_p` inside a context of evars that we are trying to prove for `x`. (The prefix "`mb`" refers to "multi-binding", a binding of 0 or more evars.) The function then works by pattern-matching on `p` (the left-hand side) and `mb_p` (the right-hand side), using the following cases, many of which call out to helper functions described below: + +| Left-hand side | Right-hand side | Algorithmic steps taken | +|------------|--------------|--------------------| +| `p` | `true` | Pop `p` and Introduce a vacuous proof of `true` | +| `p` | `eq(e)` | Call `proveVarEq` to prove the equality | +| `p1 or p2` | `mb_p` | Eliminate the disjunction and recurse | +| `exists z. p` | `mb_p` | Eliminate the existential and recurse | +| `eq(y)` | `mb_p` | Prove `y:mb_p` and then cast the proof to `x:mb_p` | +| `eq(y &+ off)` | `mb_p` | Prove `y:(offsetPerm mb_p off)` and then case the proof to `x:mb_p` | +| `p` | `mb_p1 or mb_p2` | Nondeterminsitically try to prove either `mb_p1` or `mb_p2` | +| `p` | `exists z. mb_p` | Add a new evar for `z`, prove `x:mb_p`, and then use the value determined for `x` to introduce an existential permission | +| `P` | `P` | For reachabilitiy permission `P`, nondeterministically prove the RHS by either reflexivity, meaning `x:eq(mb_e)`, or transitivity, meaning `e:P` | +| `P` | `P` | For non-reachabilitiy named permission `P`, prove `args` _weakens to_ `mb_args`, where write modalities weaken to read, bigger lifetimes weaken to smaller ones, and otherwise arguments weaken to themselves | +| `p1 * ... * pi-1 * P * pi+1 * ... pn` | `P` | Similar to above | -- Main entrypoints: `proveVarImpl`, `proveVarsImpl`, and `proveVarsImplAppend`; all written in terms of `proveVarsImplAppend` -- `proveVarsImplAppend`: first tries to do the proof without ending any lifetimes, and if that fails it tries to end lifetimes that could potentially help with the proof -- The "internal-only" functions `proveVarsImplInt` and `proveVarsImplAppendInt`: repeatedly call `findProvablePerm` to choose the "best" permission on the RHS to attempt to prove; briefly explain that will only select a permission whose needed variables are all instantiated +FIXME HERE NOW -- Next level down is `proveExVarImpl`, which handles the possibility that we are trying to prove a permission `x:p` where `x` itself is an existential variable; this is not really all that common, except for some very specific cases that come up in situtations that are a little too detailed to go into here. The main logic for proving most of the permission constructs is in the next function down, `proveVarImplInt`, which proves a single permission on a variable `x`. It does this by first pushing the primary permsisions `p` for `x` onto the top of the stack and then proving an implication `x:p |- x:mb_p`, where `mb_p` is the desired permission that we want to prove for `x`. The "mb" prefix indicates that `mb_p` is a permission inside a multi-binding, i.e., a binding of 0 or more existential variables that could be used in the permission we are trying to prove. The `proveVarImplInt` then proceeds by case analysis on `p` and `mb_p`, in most cases using either an introduction rule for proving a construct in `mb_p` on the right or an elimination rule for eliminating a construct in `p` on the left combined with a recursive call to `proveVarImplInt` to prove the remaining implication for smaller `p` and/or smaller `mb_p`. The most complex cases are for proving an equality permission on the right or for proving an implication `x:p1 * ... * pn |- x:p1' * ... * pm'` between conjuncts permissions. For an equality permission on the right, the special-purpose helper function `proveVarEq` is used to prove equality permissions `x:eq(e)` based on the structure of `e`. For proving an implication of conjuncts, the function `proveVarConjImpl` is used, which is structured in a similar manner to `proveVarsImplAppendInt`, in that it repeatedly chooses the "best" permission on the right to prove, proves it, and then recursively proves the remaining permissions on the right until they have all been proved. From 8c9cd6025bdc54326e9bfe095b53cc4d6e6602a1 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Wed, 19 Jan 2022 17:26:51 -0800 Subject: [PATCH 03/20] more work explaining the implication prover --- .../src/Verifier/SAW/Heapster/README.md | 77 ++++++++++++++----- 1 file changed, 59 insertions(+), 18 deletions(-) diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md index 315db716ea..6c95c8b857 100644 --- a/heapster-saw/src/Verifier/SAW/Heapster/README.md +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -15,7 +15,7 @@ The central components of Heapster are in the following files: * SAWTranslation.hs: This file defines the translation from type-checked Crucible CFGs to SAW core terms that represent their specifications. -FIXME: describe the other files +[comment]: <> (FIXME: describe the other files) ## Heapster Permission Implication @@ -83,7 +83,7 @@ The one-step implication rules defined by the `|-(1)` judgment are defined by th | `Impl1_Simpl` | Apply a simple implication of the form `ps1 \|- ps2` | `ps * ps1 \|-(1) ps * ps2` | | `Impl1_ElimLLVMFieldContents` | Extract the contents of an LLVM pointer permission | `ps * x:ptr((rw,off) -> p) \|-(1) y. ps * x:ptr((rw,off) -> eq(y)) * y:p` | -FIXME: explain the above rules! +[comment]: <> (FIXME: explain the above rules!) The most common implication rule is `Impl1_Simpl`, which applies a "simple" implication rule that exactly only one disjunctive output permission and binds no variables. The simple implication rules are described by the type `SimplImpl ps_in ps_out`. A rule of this type assumes that permissions `ps_in` are on the top of the stack, though there can be more permissions below these on the stack. It then consumes `ps_in`, replacing them with permissions `ps_out`. (As above, the `ps_in` and `ps_out` type arguments in Haskell are actually datakinds capturing the types of the input and output permissions of the rule.) These include too many rules to list here, so we only describe enough of them to give a flavor of what they do. @@ -164,6 +164,26 @@ implPushM :: HasCallStack => NuMatchingAny1 r => ExprVar a -> ValuePerm a -> meaning that `implPushM` takes in a variable `x` and a permission `p` and returns a computation that starts in any permission stack `ps` and pushes permission `x:p` of type `a` onto the top of the stack. +FIXME HERE: explain that ImplM is a state-continuation monad, whose final output is always a `PermImpl` + + +#### Needed and Determined Variables + +One difficulty in doing proof search which must be addressed by the implication prover is that existential variables mean we do not have most general types. For instance, there are two distinct ways to prove + +``` +x:ptr((R,0) |-> true) * x:ptr((R,8) |-> true) |- exists off:bv 64. x:ptr((R,off) |-> true) +``` + +The difficulty is that if we choose the wrong value for `off` we might have to backtrack, potentially leading to an exponential search. The Heapster implication prover addresses this problem by requiring that all existential variables that could lead to this sort of problem in a permission `P` are assigned a uniquely determined value before it attempts to satisfy permission `P`. These variables are called the _needed_ variables of `P`, defined by the `neededVars` function in Permissions.hs, and include any variables in the offsets and lengths of pointer, array, and block permissions, among others. In our example above, `off` is a needed variable on the right-hand side, so the implication prover will not + +FIXME HERE + +so, because there is no the implication prover will not prove this implication, but will instead raise an error. (NOTE: ) + +The only way to prove a permission with needed variables is if there is some other permission which is proved first that _determines_ the value of that variable. A permission `P` determines an existential variable `x` iff there is only one possible value of `x` for which `P` can possibly be proved. The canonical example of determination is the permission `eq(x)`: the only possible way to prove `y:eq(x)` is if `x` is set to `y`. + +Returning to #### The Top-Level Implication Prover Algorithm @@ -197,7 +217,7 @@ The top-level implication prover algorithm is then implemented as a descending s | `proveVarImplInt` | Wrapper function that pushes the primary permissions for `x` onto the top of the stack, performs debug tracing, calls `proveVarImplH`, and then checks that the proved permission is correct | -#### Proving a Single Permissino +#### Proving a Permission The main logic for proving a permission is in the function `proveVarImplH`. (The implication prover uses the convention of using "`H`" as a suffix for helper functions.) As with many functions in the implication prover, this function takes in: a variable `x` that we are trying to prove a permission on; a permission `p` for `x` which is currently on top of the stack; and a permission `mb_p` inside a context of evars that we are trying to prove for `x`. (The prefix "`mb`" refers to "multi-binding", a binding of 0 or more evars.) The function then works by pattern-matching on `p` (the left-hand side) and `mb_p` (the right-hand side), using the following cases, many of which call out to helper functions described below: @@ -211,36 +231,57 @@ The main logic for proving a permission is in the function `proveVarImplH`. (The | `eq(y &+ off)` | `mb_p` | Prove `y:(offsetPerm mb_p off)` and then case the proof to `x:mb_p` | | `p` | `mb_p1 or mb_p2` | Nondeterminsitically try to prove either `mb_p1` or `mb_p2` | | `p` | `exists z. mb_p` | Add a new evar for `z`, prove `x:mb_p`, and then use the value determined for `x` to introduce an existential permission | -| `P` | `P` | For reachabilitiy permission `P`, nondeterministically prove the RHS by either reflexivity, meaning `x:eq(mb_e)`, or transitivity, meaning `e:P` | -| `P` | `P` | For non-reachabilitiy named permission `P`, prove `args` _weakens to_ `mb_args`, where write modalities weaken to read, bigger lifetimes weaken to smaller ones, and otherwise arguments weaken to themselves | -| `p1 * ... * pi-1 * P * pi+1 * ... pn` | `P` | Similar to above | +| `P` | `mb_p` | Use the more specific rules below for named permissions | +| `p1 * ... * pi-1 * P * pi+1 * ... * pn` | `mb_p` | Use the more specific rules below for named permissions | +| `p` | `P` | Use the more specific rules below for named permissions | +| `eq(llvmword e)` | `p1 * ... * pn` | Fail, because we cannot prove any non-equality permissions for words, and the equality permissions were matched by an earlier case | +| `eq(struct(e1,...,en))` | `mb_p` | Eliminate `eq(struct(e1,...,en))` to a `struct(eq(e1),...,eq(en))` permission with equalities for each field | +| `eq(constant f)` | `(gs) ps_in -o ps_out` | Use an assumption on known function `f` | +| `p1 * ... * pn` | `mb_p1 * ... * mb_pn` | Call `proveVarConjImpl` to prove a conjunction implies a conjunction | +| `p` | `(X). X` | For existential permission variable `X`, set `X:=p` | +| `X` | `X` | For universal permission variable `X`, prove `X -o X` by reflexivity | +| `_` | `_` | In all other cases, fail | +#### Proving Named Permissions -FIXME HERE NOW +| Left-hand side | Right-hand side | Algorithmic steps taken | +|------------|--------------|--------------------| +| `P` | `P` | For reachabilitiy permission `P`, nondeterministically prove the RHS by either reflexivity, meaning `x:eq(mb_e)`, or transitivity, meaning `e:P` | +| `P` | `P` | For non-reachabilitiy named permission `P`, prove `args` _weakens to_ `mb_args`, where write modalities weaken to read, bigger lifetimes weaken to smaller ones, and otherwise arguments weaken to themselves | +| `p1 * ... * pi-1 * P * pi+1 * ... pn` | `P` | Similar to above | +| `p` | `P` | If `P` is a _defined_ (i.e., non-recursive) name, unfold `P` to its definition and recurse | +| `P` | `mb_p` | If `P` is a defined name, unfold `P` to its definition and recurse | +| `p1 * ... * pi-1 * P * pi+1 * ... pn` | `mb_p` | If `P` is defined, unfold `P` to its definition and recurse | +| `P1` | `P2` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose one side to unfold | +| `p1 * ... * pi-1 * P1 * pi+1 * ... pn` | `P2` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose one side to unfold | +| `p` | `P` | If `P` is recursive, unfold `P` and recurse | +| `P` | `mb_p` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose +FIXME HERE: mention the recursion flag: cannot unfold on the left and the right in the same proof -The main logic for proving most of the permission constructs is in the next function down, `proveVarImplInt`, which proves a single permission on a variable `x`. It does this by first pushing the primary permsisions `p` for `x` onto the top of the stack and then proving an implication `x:p |- x:mb_p`, where `mb_p` is the desired permission that we want to prove for `x`. The "mb" prefix indicates that `mb_p` is a permission inside a multi-binding, i.e., a binding of 0 or more existential variables that could be used in the permission we are trying to prove. The `proveVarImplInt` then proceeds by case analysis on `p` and `mb_p`, in most cases using either an introduction rule for proving a construct in `mb_p` on the right or an elimination rule for eliminating a construct in `p` on the left combined with a recursive call to `proveVarImplInt` to prove the remaining implication for smaller `p` and/or smaller `mb_p`. The most complex cases are for proving an equality permission on the right or for proving an implication `x:p1 * ... * pn |- x:p1' * ... * pm'` between conjuncts permissions. For an equality permission on the right, the special-purpose helper function `proveVarEq` is used to prove equality permissions `x:eq(e)` based on the structure of `e`. For proving an implication of conjuncts, the function `proveVarConjImpl` is used, which is structured in a similar manner to `proveVarsImplAppendInt`, in that it repeatedly chooses the "best" permission on the right to prove, proves it, and then recursively proves the remaining permissions on the right until they have all been proved. -The `proveVarAtomicImpl` function is called by `proveVarConjImpl` to prove each conjunct. This function performs +#### Proving Equalities and Equality Permissions +FIXME HERE: +- Explain the representaiton of equality proofs +- `proveEq` builds an equality proof +- `proveVarEq` then casts a reflexive equality proof `x:eq(x)` to one of `x:eq(e)` using a proof of `x=e` -FIXME: put this discussion about needed and determined variables into the description of proveVarsImplAppendInt +#### Proving Conjuncts of Permissions -There are a number of difficulties in doing this proof search which must be addressed by the implication prover. The first is that existential permissions mean we do not have most general types; e.g., there are two distinct ways to prove +FIXME HERE: +- Explain that `proveVarConjImpl` repeatedly chooses the "best" permission to prove next; chooses defined permissions first, then recursive ones, then those whose needed vars are determined +- give the table of cases -``` -x:ptr((R,0) |-> true) * x:ptr((R,8) |-> true) |- exists off:bv 64. x:ptr((R,off) |-> true) -``` -The difficulty is that if we choose the wrong value for `off` we might have to backtrack, potentially leading to an exponential search. The Heapster implication prover addresses this problem by requiring that all existential variables that could lead to this sort of problem in a permission `P` are assigned a uniquely determined value before it attempts to satisfy permission `P`. These variables are called the _needed_ variables of `P`, defined by the `neededVars` function in Permissions.hs, and include any variables in the offsets and lengths of pointer, array, and block permissions, among others. In our example above, `off` is a needed variable on the right-hand side, so the implication prover will not +#### Proving Field Permissions -so, because there is no the implication prover will not prove this implication, but will instead raise an error. (NOTE: ) +#### Proving Array Permissions -The only way to prove a permission with needed variables is if there is some other permission which is proved first that _determines_ the value of that variable. A permission `P` determines an existential variable `x` iff there is only one possible value of `x` for which `P` can possibly be proved. The canonical example of determination is the permission `eq(x)`: the only possible way to prove `y:eq(x)` is if `x` is set to `y`. +#### Proving Block Permissions -Returning to ## Crucible Type-checker From 35b7c48e938fd08bf6b5f9982b678d1ee80035c6 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Thu, 20 Jan 2022 06:39:31 -0800 Subject: [PATCH 04/20] finished describing needed and determined variables --- .../src/Verifier/SAW/Heapster/README.md | 69 +++++++++++++++++-- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md index 6c95c8b857..a3f871bd90 100644 --- a/heapster-saw/src/Verifier/SAW/Heapster/README.md +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -169,21 +169,76 @@ FIXME HERE: explain that ImplM is a state-continuation monad, whose final output #### Needed and Determined Variables -One difficulty in doing proof search which must be addressed by the implication prover is that existential variables mean we do not have most general types. For instance, there are two distinct ways to prove +One difficulty in doing proof search which must be addressed by the implication prover is that existential variables mean we do not have most general types. (There are other ways in which Heapster does not have most general types, but this is a more aggregious one.) For instance, there are two distinct ways to prove ``` x:ptr((R,0) |-> true) * x:ptr((R,8) |-> true) |- exists off:bv 64. x:ptr((R,off) |-> true) ``` -The difficulty is that if we choose the wrong value for `off` we might have to backtrack, potentially leading to an exponential search. The Heapster implication prover addresses this problem by requiring that all existential variables that could lead to this sort of problem in a permission `P` are assigned a uniquely determined value before it attempts to satisfy permission `P`. These variables are called the _needed_ variables of `P`, defined by the `neededVars` function in Permissions.hs, and include any variables in the offsets and lengths of pointer, array, and block permissions, among others. In our example above, `off` is a needed variable on the right-hand side, so the implication prover will not +by instantiating `off` to either 0 or 8. The difficulty is that if we choose the wrong value for `off` we might have to backtrack, potentially leading to an exponential search. The same problem occurs for function permissions with ghost variables, as ghost variables become existential variables that must be instantiated at call sites. Thus, for instance, Heapster cannot handle a function with permissions like -FIXME HERE +``` +(off:bv 64). arg0:ptr((R,off) |-> true) -o empty +``` + +because it will not know how to instantiate `off` at the call site. Currently, this shows up as a type-checking error when such a function is called, but we could consider raising an error where such a function is defined. If you think about it, though, a function type like this does not really make any sense. How could a function take in a pointer to something where it doesn't know the offset of that pointer? + +If, however, there is some other permission that _determines_ the offset, then this problem is resolved. Consider, for instance, the following function type: + +``` +(off:bv 64). arg0:ptr((R,off) |-> true), arg1:eq(llvmword(off)) -o empty +``` + +This describes a function whose second argument says what the offset is for the first. Unlike the previous example, Heapster can handle this function type, because it will prove the equality permission on `arg1` first, and this proof will determine the value of `off` to be used for the permission on `arg0`. This function type also makes a lot more sense operationally, because now the function can know what the offset is. The more common version of this situation is passing the length of an array, using a type like this: + +``` +(len:bv 64). arg0:array(W,0,), arg1:eq(llvmword(len)) -o empty +``` + +A similar pattern can occur inside data structures. A common pattern in C is to have a `struct` with a variable-length array at the end, whose length is determined by one of the fields, like this: + +``` +struct foo { + ...; + int64_t len; + char data[]; +} +``` + +Rust slices are similar. A struct like this can be described by the Heapster shape + +``` +...; exsh len:bv 64.(fieldsh(eq(llvmword len));arraysh())) +``` + +This shape can be proved by the Heapster implication prover because the existential variable `len` in the shape is determined by the equality permission in the `len` field in the struct. If the struct did not have this field, Heapster would not be able to prove permissions with this shape. Again, such a shape does not really make sense, as the program would never know how long the `data` field is. + + +The Heapster implication prover addresses the problem of existential variables leading to non-unique types by requiring that all existential variables that could lead to this sort of problem in a permission `p` are assigned a uniquely determined value before it attempts to satisfy permission `p`. These variables are called the _needed_ variables of `p`, defined by the `neededVars` function in Permissions.hs. The needed variables include any free variables in the offsets and lengths of pointer, array, and block permissions, as well as any free variables of the more complicated permissions like lifetime ownership permissions. For equality permissions `eq(e)`, the free variables of `e` are not needed if `e` is a _determining_ expression, discussed below. In our example above, `off` is a needed variable on the right-hand side, so the implication prover will not prove this implication but will instead raise a type-checking error (with the `Impl1_Fail` rule described above). + +The only way to prove a permission with needed variables is if there is some other permission which is proved first that _determines_ the value of that variable. Intuitively, the idea is that a permission `p` determines an existential variable `x` if there is only one possible value of `x` for which `p` can possibly be proved. The canonical example of determination is the permission `eq(x)`: the only possible way to prove an `eq(x)` permission is to set `x` to the value that has this permission. If we are proving `y:eq(x)`, then `x` has to be set to `y`, while if we are proving a pointer permission `y:ptr((rw,off) |-> eq(x))`, `x` has to be set to the value pointed to by `y` at offset `off`. Note that, in this latter case, the implication prover will first prove some permission of the form `y:ptr((rw,off) |-> p)` and will then use the `Impl1_ElimLLVMFieldContents` rule to bind a local variable `z` for the value pointed to by `y` at offset `off`, so `x` will be set to this local variable `z`. In order to prove a pointer permission, however, the free variables in `off` (if there are any) must already be determined by some other permission, because these are needed variables of the pointer permission. Thus determined variables have a dependency structure, where some variables can only be determined if other variables are determined first. Further, a variable can not be determined by an equality inside an arbitrary permission. For instance, `eq(x) or p` does not determine `x`, because the proof may not take the left-hand branch of the disjunct. + +More generally, determined variables are defined by the `determinedVars` function. This function uses the helper function `isDeterminingExpr` to define whether an expression `e` used in an equality permission determines its variables. The following expression forms are determining: +* `x` +* `llvmword e` if `e` is determining +* `N*x + K` for constants `N` and `K` +* The permission `eq(e)` as an expression if `e` is determining +* `x &+ off` if `off` is determining +* Any expression with no free variables + +The `determinedVars` function is then defined as follows on permission `p`: -so, because there is no the implication prover will not prove this implication, but will instead raise an error. (NOTE: ) +| Permission `p` | Determined Variables | +|-------------|----------------------| +| `eq(e)` | The free variables of `e` if `e` is a determining expression, otherwise `[]` | +| `p1 * ... * pn` | The determined variables of the `pi` | +| `P` | The free variables of each determining expression in `args` | +| `[l]ptr((rw,off) |-> p)` | The determined variables of `l`, `rw`, and `p`, if the variables in `off` are determined | +| `[l]array(rw,off, Date: Thu, 20 Jan 2022 07:14:35 -0800 Subject: [PATCH 05/20] added a bit more explanation about ImplM being a state-continuation monad --- heapster-saw/src/Verifier/SAW/Heapster/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md index a3f871bd90..881ac7fbf8 100644 --- a/heapster-saw/src/Verifier/SAW/Heapster/README.md +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -164,7 +164,17 @@ implPushM :: HasCallStack => NuMatchingAny1 r => ExprVar a -> ValuePerm a -> meaning that `implPushM` takes in a variable `x` and a permission `p` and returns a computation that starts in any permission stack `ps` and pushes permission `x:p` of type `a` onto the top of the stack. -FIXME HERE: explain that ImplM is a state-continuation monad, whose final output is always a `PermImpl` +If the permission stack does not change, meaning that `ps_in` equals `ps_out`, then `ImplM` forms a monad. For instance, the function + +``` +partialSubstForceM :: (NuMatchingAny1 r, PermPretty a, + Substable PartialSubst a Maybe) => + Mb vars a -> String -> ImplM vars s r ps ps a +``` + +takes the current partial substitution for the evars and attempts to apply it to a value in a name-binding for those evars, raising an error if some evar that has not yet been instantiated is used in the value. (The "force" means to force the substitution to be defined or fail.) This function does not change the permission stack, and is in fact written in `do` notation in the code. + +The `ImplM` monad is defined as a generalized state-continuation monad. This construct is defined in `GenMonad.hs`, but we will not discuss it in too much detail here. The state that is maintained is given by the `ImplState` type, which contains information such as the current permission set, the types of all univeral and existential variables in scope, and the current instantiations of all the evars. The input and output types of the continuation portion of `ImplM` are both `PermImpl`, meaning each `ImplM` computation builds up an implication. The fact that `ImplM` is a continuation monad is only used in the `implApplyImpl1` function, which applies an `Impl1` rule by shifting the current continuation and re-applying it to build the sub-`PermImpl`s passed to that rule as an `MbPermImpls`. This means that rules with multiple disjunctive outputs, like or elimination and the catch rule, cause `ImplM` to fork its execution, running any subsequent computation once in each disjunctive branch. Thus, for performance reasons, it is helpful to reduce this forking as much as possible. #### Needed and Determined Variables From eac8d7b2e596aef3f8e131470c3495362d6ad9f7 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Thu, 20 Jan 2022 11:51:04 -0800 Subject: [PATCH 06/20] finished describing the proofs for named perms, started describing proveVarEq --- .../src/Verifier/SAW/Heapster/README.md | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md index 881ac7fbf8..82f4ecc885 100644 --- a/heapster-saw/src/Verifier/SAW/Heapster/README.md +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -129,10 +129,9 @@ Equality permissions are manipulated with the following simple implication rules | `SImpl_CastPerm` | Cast a permission `p` to `p'`, assuming that `x1=e1`, ..., `xn=en` imply that `p=p'` | `x1:eq(e1) * ... * xn:eq(en) * x:p \|- x:p'` | +[comment]: (FIXME: Implementation of the rules: `simplImplIn` and `simplImplOut`, `applyImpl1`: these all check for correct perms) -- Implementation of the rules: `simplImplIn` and `simplImplOut`, `applyImpl1`: these all check for correct perms - -- Explain overall pattern of the simplimpl rules: intro vs elim rules for most constructs +[comment]: (FIXME: Explain overall pattern of the simplimpl rules: intro vs elim rules for most constructs) ### The Implication Prover Algorithm @@ -152,7 +151,8 @@ An element of this type is an implication prover computation with return type `a The type variables `ps_in` and `ps_out` describe the permission stack on the beginning and end of the computation. The existence of these two variables make `ImplM` a _generalized_ monad instead of just a standard monad, which means that these types can vary throughout an implication computation. The bind for `ImplM` is written `>>>=` instead of `>>=`, and has type ``` -(>>>=) :: ImplM vars s r ps' ps_in a -> (a -> ImplM vars s r ps_out ps' b) -> ImplM vars s r ps_out ps_in b +(>>>=) :: ImplM vars s r ps' ps_in a -> (a -> ImplM vars s r ps_out ps' b) -> + ImplM vars s r ps_out ps_in b ``` That is, the bind `m >>>= f` first runs `m`, which changes the permissions stack from `ps_in` to `ps'`, and then it passes the output of `m` to `f`, which changes the permissions stack from `ps'` to `s_out`, so the overall computation changes the permissions stack from `ps_in` to `ps_out`. As a more concrete example, the computation for pushing a permission onto the top of the stack is declared as @@ -310,6 +310,20 @@ The main logic for proving a permission is in the function `proveVarImplH`. (The #### Proving Named Permissions +Named permissions are of the form `P` for some permission name `P`. Each named permission represents some more complicated collection of permissions, that can depend on the argument expressions `args`. Permission names come in three sorts: + +* _Defined names_ are essentially abbreviations, where `P` unfolds to some permission `p` that does not contain `P` itself (unless `P` occurs `args`); + +* _Recursive names_ `P` are similar to defined names but where `P` unfolds to a permission that can contain `P`; and + +* _Opaque names_ are permissions which are too complicated to represent in Heapster, so Heapster just represents them with names. + +The "best" way to prove a named permission `P` is by reflexivity, i.e., to find an instance of `P` that is already in the current permissions set. The implication rules do allow some amount of weakening the arguments, so technically this is a search for `P` for some argument list `args'` that can be coerced to `args`. For opaque names, this is the only way to prove `P`. For defined names, the other option is to just unfold `P` to its definition, prove that permission, and then fold the result to `P`. Similarly, recursive names can also be unfolded to prove them. Dually, if there is a permission `P` with a defined or recursive name on the left, meaning it is already held in the current permission set, the implication prover will unfold this permission if it gets stuck trying to prove its goal any other way. This logic is implemented by the `implUnfoldOrFail` function, which is called in a number of places in the implication prover where it will otherwise fail. + +The one wrinkle is that unfolding recursive names can lead to non-termination. This can happen if we have an assumption `P1` on the left and we are trying to prove `P2` on the right where both `P1` and `P2` are recursive names. If we proceed by unfolding both `P1` and `P2`, then, because these unfoldings can each contain `P1` and `P2` again, we can end up back at the same proof goal, of having an assumption `P1` on the left and trying to prove `P2`. To prevent this infinite regress, the implication prover is restricted so that it will not unfold names on both the left and right sides in the same proof. This is done by maintainining a flag called `implStateRecRecurseFlag` that tracks whether there has been an unfolding of a recursive name on one side or the other. Whenever the implication prover has `P1` on the left and `P2` on the right for recursive names `P1` and `P2`, it then non-deterministically (using the `Impl1_Catch` rule to backtrack) chooses one side to unfold, and it proceeds from there. This handles the possibility that one of `P1` or `P2` contains the other as a sub-permission. + +In more detail, here are the cases of `proveVarImplH` that handle recursive permissions: + | Left-hand side | Right-hand side | Algorithmic steps taken | |------------|--------------|--------------------| | `P` | `P` | For reachabilitiy permission `P`, nondeterministically prove the RHS by either reflexivity, meaning `x:eq(mb_e)`, or transitivity, meaning `e:P` | @@ -323,15 +337,14 @@ The main logic for proving a permission is in the function `proveVarImplH`. (The | `p` | `P` | If `P` is recursive, unfold `P` and recurse | | `P` | `mb_p` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose -FIXME HERE: mention the recursion flag: cannot unfold on the left and the right in the same proof - #### Proving Equalities and Equality Permissions -FIXME HERE: -- Explain the representaiton of equality proofs -- `proveEq` builds an equality proof -- `proveVarEq` then casts a reflexive equality proof `x:eq(x)` to one of `x:eq(e)` using a proof of `x=e` +Equality permissions are proved by `proveVarEq`, which takes a variable `x` and an expresson `mb_e` in a binding of the existential variables, and proves `x:eq(e)` for some instantiation `e` of the variables in `mb_e`. This function pushes a reflexive proof that `x:eq(x)`, calls `proveEq` to build an equality proof that `x=e`, and uses the equality proof with the `SImpl_CastPerm` rule to cast the proof of `x:eq(x)` on top of the stack to `x:eq(e)`. The meat of `proveVarEq` is thus in `proveEq`, which attempts to build equality proofs. The `proveEq` function is also called in other parts of the implication prover, e.g., to coerce the modalities of field, array, and block permissions. + +An equality proof in Heapster is a transitive sequence of equality proof steps `e=e',e'=e'',...`. Each step is a sequence of equality permissions `x1:eq(e1),...,xn:eq(en)`, where each equality `xi:eq(ei)` is oriented either left-to-right as `xi=ei` or right-to-left as `ei=xi`, along with a function `f` on `n` expressions. This represents the equality `f(left1,...,leftn)=f(right1,...,rightn)`, where `lefti` and `righti` are the left- and right-hand sides of the `i`th oriented version `xi=ei` or `ei=xi` of the permission `xi:eq(ei)`. Equality steps are represented by the Haskell type `EqProofStep ps a`, where `ps` is a list of the types of the variables `x1,...,xn` and `a` is the Haskell type of the objects being proved equal. (Equality proofs can be used not just on expressions, but at other types as well.) Entire equality proofs are represented by the type `EqProof ps a`, while the type `SomeEqProof a` is an equality proof where the permissions needed to prove it are existentially quantified. + +FIXME HERE: describe `proveEq` and `proveEqH` #### Proving Conjuncts of Permissions From 93542fa28e4e2e2e2b6fd4efc8b0964f92e8f68c Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Thu, 20 Jan 2022 13:36:50 -0800 Subject: [PATCH 07/20] finished describing proveVarAtomicImpl` ` --- .../src/Verifier/SAW/Heapster/README.md | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md index 82f4ecc885..5206d11706 100644 --- a/heapster-saw/src/Verifier/SAW/Heapster/README.md +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -26,9 +26,9 @@ Heapster permission implication is a form of _affine logic_, which in turn is a dollar * dollar * dollar |- candy_bar ``` -to represent the concept that a particular candy bar costs $3. Intuitively, the `dollar` proposition represents possession of a dollar, `candy_bar` represents possession of a (reasonably fancy) candy bar, and `*` represents the conjunction of two propositions. A "proof" using this rule consumes three `dollar` propositions and generates one `candy_bar` proposition, intuitively representing the purchase of this candy bar. Importantly, unlike standard classical or even intuitionistic logic, where `P and P` is equivalent to `P`, the conjunction `P * P` in linear logic represents two copies of the proposition `P`, which in general is different than `P` by itself; e.g., if we could prove `dollar |- dollar * dollar` then we could generate all the money we wanted. This is not to say that `P |- P * P` is never true, just that it is only true for some `P`, which correspond to resources that can be duplicated. See any introduction to linear logic for more details. +to represent the concept that a particular candy bar costs $3. Intuitively, the `dollar` proposition represents possession of a dollar, `candy_bar` represents possession of a (reasonably fancy) candy bar, and `*` represents the conjunction of two propositions. A "proof" using this rule consumes three `dollar` propositions and generates one `candy_bar` proposition, intuitively representing the purchase of this candy bar. Importantly, unlike standard classical or even intuitionistic logic, where `p and p` is equivalent to `p`, the conjunction `p * p` in linear logic represents two copies of the proposition `p`, which in general is different than `p` by itself; e.g., if we could prove `dollar |- dollar * dollar` then we could generate all the money we wanted. This is not to say that `p |- p * p` is never true, just that it is only true for some `p`, which correspond to resources that can be duplicated. See any introduction to linear logic for more details. -Affine logic is a version of linear logic where propositions can be "thrown away", that is, where the rule `P * Q |- P` holds for all `P` and `Q`. The reason we use affine logic here is that it is useful for describing a notion of _permissions_, where each `P` intuitively corresponds to permission to perform a particular action. It is always justified to forget some permission if you are not going to use it, but you can't in general give yourself more permissions. One of the central permissions used in Heapster is the permission to access memory through a particular pointer. The simplest form of this is the pointer permission `x:ptr((rw,off) |-> p)`, that represents a permission to read — and possibly write, depending on `rw` — memory at offset `off` from `x`, along with permission `y:p` to whatever value `y` is currently stored there. The `array` and `memblock` permissions also represent different forms of permission to read and possibly write memory, with different stipulations on the permissions held for the values currently stored there. Read-only permissions are copyable, meaning that `x:ptr((R,off) |-> p) |- x:ptr((R,off) |-> p) * x:ptr((R,off) |-> p)` can be proved in Heapster, as long as `p` does not contain any write permissions, while write permissions `x:ptr((W,off) |-> p)` are not. This corresponds to the one-writer or multiple readers paradigm of, e.g., Rust. +Affine logic is a version of linear logic where propositions can be "thrown away", that is, where the rule `p * q |- p` holds for all `p` and `q`. The reason we use affine logic here is that it is useful for describing a notion of _permissions_, where each `p` intuitively corresponds to permission to perform a particular action. It is always justified to forget some permission if you are not going to use it, but you can't in general give yourself more permissions. One of the central permissions used in Heapster is the permission to access memory through a particular pointer. The simplest form of this is the pointer permission `x:ptr((rw,off) |-> p)`, that represents a permission to read — and possibly write, depending on `rw` — memory at offset `off` from `x`, along with permission `y:p` to whatever value `y` is currently stored there. The `array` and `memblock` permissions also represent different forms of permission to read and possibly write memory, with different stipulations on the permissions held for the values currently stored there. Read-only permissions are copyable, meaning that `x:ptr((R,off) |-> p) |- x:ptr((R,off) |-> p) * x:ptr((R,off) |-> p)` can be proved in Heapster, as long as `p` does not contain any write permissions, while write permissions `x:ptr((W,off) |-> p)` are not. This corresponds to the one-writer or multiple readers paradigm of, e.g., Rust. The remainder of this section explains Heapster implication in more detail and then describes the permission implication prover algorithm used by Heapster used to prove these implications. @@ -38,10 +38,10 @@ The remainder of this section explains Heapster implication in more detail and t At any given point during type-checking and/or implication proving, Heapster maintains a _permission set_ that describes the current permissions to various objects that are currently held by the program. Permission sets are defined by the `PermSet` type in Permissions.hs, and have two components: the _permissions map_, which maps each variable `x` in scope to what is called the _primary_ permission held on `x`; and the _permissions stack_, which represents the permissions that are actively being used or manipulated. We write a permission set as: ``` -x1 -> Px1 * ... * xm -> Pxm; y1:P1 * ... * yn:Pn +x1 -> px1 * ... * xm -> pxm; y1:p1 * ... * yn:pn ``` -The portion before the semicolon represents the permissions map, which maps each `xi` to its primary permission `Pxi`, while the portion after the semicolon represents the permissions stack, containing permissions `y1:P1` through `yn:Pn` in sequence. The final permissions `yn:Pn` is the top of the stack. We often write `PS` for a permission set. +The portion before the semicolon represents the permissions map, which maps each `xi` to its primary permission `pxi`, while the portion after the semicolon represents the permissions stack, containing permissions `y1:p1` through `yn:pn` in sequence. The final permissions `yn:pn` is the top of the stack. We often write `PS` for a permission set. The general form of permission implication is the judgment @@ -49,7 +49,7 @@ The general form of permission implication is the judgment PS |- (z1_1, ..., z1_k1 . PS1) \/ ... \/ (zn_1, ..., zn_kn . PSn) ``` -which says that, starting with permission set `PS` on the left-hand side of the turnstyle `|-`, we can prove one of the permission sets `PSi` on the right-hand side. Each disjunctive case could introduce 0 or more existential variables `zi_1, ..., zi_ki`, which can be used in the corresponding permission set `PSi`. We often omit the permissions map and/or the existential variables when they are not necessary; e.g., we write `PS1 |- PS2` instead of `PS1 |- ( [] . PS2)`. We also tend to omit the permissions map from implications, as permissions maps almost never change; thus, e.g., we might write `x:p |- y:q` instead of `x1 -> Px1 * ... * xm -> Pxm; x:p |- x1 -> Px1 * ... * xm -> Pxm; y:q`. +which says that, starting with permission set `PS` on the left-hand side of the turnstyle `|-`, we can prove one of the permission sets `PSi` on the right-hand side. Each disjunctive case could introduce 0 or more existential variables `zi_1, ..., zi_ki`, which can be used in the corresponding permission set `PSi`. We often omit the permissions map and/or the existential variables when they are not necessary; e.g., we write `PS1 |- PS2` instead of `PS1 |- ( [] . PS2)`. We also tend to omit the permissions map from implications, as permissions maps almost never change; thus, e.g., we might write `x:p |- y:q` instead of `x1 -> px1 * ... * xm -> pxm; x:p |- x1 -> px1 * ... * xm -> pxm; y:q`. Permission implication in Heapster is actually a sort of "partial implication". The above-displayed implicaiton judgment in fact says that, if we hvae permission set `PS`, we can _try_ to get one of the permission sets `PSi`, though we can't control which one we get, and we might fail. What this failure means exactly is a little hard to define without going into the details of the translation to SAW core / Coq and relationship between the resulting term and the original program. As one way to understand what failure means here, consider that each permission set `PS` actually describes a set of possible states, one for each substitution of values for all the free variables in `PS`. For some of these states, we can exchange the permissions in `PS` for the permissions in one of the `PSi`, though in some of those states, trying to do this leads to undefined behavior, or at least behavior we are not going to reason about. Another way to think about Heapster implication is to always add an extra disjunction `\/ dunno` to each right-hand side, so an implication `PS |- PS1 \/ ... \/ PSn` becomes `PS |- PS1 \/ ... \/ PSn \/ dunno`, meaning that from permissions `PS` we either can get one of the `PSi` or we get a result that says that we have to give up on modeling the current execution of the program. At a slightly more technical level, failure means means that the translation of a failure is just the `errorM` computation, which, again, doesn't mean that the original computation actually has an error, just that we don't know how to reason about it. Either way, we will simply say "prove" or "implies" below instead of something like "partially prove in some states". @@ -129,9 +129,9 @@ Equality permissions are manipulated with the following simple implication rules | `SImpl_CastPerm` | Cast a permission `p` to `p'`, assuming that `x1=e1`, ..., `xn=en` imply that `p=p'` | `x1:eq(e1) * ... * xn:eq(en) * x:p \|- x:p'` | -[comment]: (FIXME: Implementation of the rules: `simplImplIn` and `simplImplOut`, `applyImpl1`: these all check for correct perms) +[comment]: <> (FIXME: Implementation of the rules: `simplImplIn` and `simplImplOut`, `applyImpl1`: these all check for correct perms) -[comment]: (FIXME: Explain overall pattern of the simplimpl rules: intro vs elim rules for most constructs) +[comment]: <> (FIXME: Explain overall pattern of the simplimpl rules: intro vs elim rules for most constructs) ### The Implication Prover Algorithm @@ -344,14 +344,32 @@ Equality permissions are proved by `proveVarEq`, which takes a variable `x` and An equality proof in Heapster is a transitive sequence of equality proof steps `e=e',e'=e'',...`. Each step is a sequence of equality permissions `x1:eq(e1),...,xn:eq(en)`, where each equality `xi:eq(ei)` is oriented either left-to-right as `xi=ei` or right-to-left as `ei=xi`, along with a function `f` on `n` expressions. This represents the equality `f(left1,...,leftn)=f(right1,...,rightn)`, where `lefti` and `righti` are the left- and right-hand sides of the `i`th oriented version `xi=ei` or `ei=xi` of the permission `xi:eq(ei)`. Equality steps are represented by the Haskell type `EqProofStep ps a`, where `ps` is a list of the types of the variables `x1,...,xn` and `a` is the Haskell type of the objects being proved equal. (Equality proofs can be used not just on expressions, but at other types as well.) Entire equality proofs are represented by the type `EqProof ps a`, while the type `SomeEqProof a` is an equality proof where the permissions needed to prove it are existentially quantified. -FIXME HERE: describe `proveEq` and `proveEqH` +[comment]: <> (FIXME HERE: describe `proveEq` and `proveEqH`) #### Proving Conjuncts of Permissions -FIXME HERE: -- Explain that `proveVarConjImpl` repeatedly chooses the "best" permission to prove next; chooses defined permissions first, then recursive ones, then those whose needed vars are determined -- give the table of cases +Conjuncts `p1 * ... * pn` are proved by `proveVarConjImpl`, which repeatedly picks the "best" permission on the right to prove and calls `proveVarAtomicImpl` to prove it. Finding the "best" permission prioritizes defined permissions first, followed by recursive permissions, as this fits with the named permissions algorithm described above, followed by finding a permission whose needed variables have all been determined. + +The cases for `proveVarAtomicImpl` are as follows: + +| Permission to prove | Algorithmic steps taken | +|----------------|--------------------| +| `[l]ptr((rw,off) |-> p)` | Call `proveVarLLVMField` | +| `[l]array(rw,off, Date: Thu, 20 Jan 2022 17:02:38 -0800 Subject: [PATCH 08/20] added a description of how to prove field permissions --- .../src/Verifier/SAW/Heapster/README.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md index 5206d11706..75d8264e7d 100644 --- a/heapster-saw/src/Verifier/SAW/Heapster/README.md +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -249,6 +249,10 @@ The `determinedVars` function is then defined as follows on permission `p`: | `llvmframe[e1:i1, ..., en:in]` | The free variables of each `ei` that is a determining expression | +#### Recombining Permissions + +FIXME HERE: explain `recombinePerm` + #### The Top-Level Implication Prover Algorithm @@ -374,8 +378,36 @@ The cases for `proveVarAtomicImpl` are as follows: #### Proving Field Permissions +To prove a field permission `[l]ptr((rw,off) |-> p)`, the `proveVarLLVMField` function starts by first calling `implGetLLVMPermForOffset` to find a permission on the left that contains `off`. If this permission is a `memblock` permission, it is repeatedly eliminated, using the helper function `implElimLLVMBlock`, until an array or field permission is obtained. This permission is then passed to `proveVarLLVMFieldH`, which calls `proveVarLLVMFieldH2`, which dispatches based on the form of the permission. We call this the left-hand permission in this discussion, since `implGetLLVMPermForOffset` puts it on the top of the stack, i.e., the left of the implication. + +The main case `proveVarLLVMFieldH2` is when the left-hand permission is a field permission +`[l']ptr((rw',off) |-> p')` of the same size as the required one. In this case, `proveVarLLVMFieldH2` performs the following steps: +* Eliminate the contents of the field permission to get a permission of the form `[l']ptr((rw',off) |-> eq(y))` for some variable `y`; +* Prove `y:p` with a recursive call to `proveVarImplInt`; +* Use the `SImpl_IntroLLVMFieldContents` to combine the `y:p` permission into the left-hand permission to get `[l']ptr((rw',off) |-> p)`; +* Coerce the lifetime `l'` to `l` by calling `proveVarLifetimeFunctor`, which splits or ends lifetimes as needed; +* Coerce `rw'` to `rw` by calling `equalizeRWs`, which either proves the two are equal using `proveEq` and casting or weakens a write modality to a read modality; and +* Duplicate and recombine the pointer permission if it is copyable. + +If the left-hand permission is a pointer permission that is bigger than required, split the left-hand permission by calling `implLLVMFieldSplit`, recombine the part that is not required, and recursively call `proveVarLLVMFieldH` with the remaining left-hand permission. + +If the left-hand permission is a pointer permission that is smaller than required: +* Recursively call `proveVarLLVMFieldH` with the same left-hand permission to prove a pointer permission `[l]ptr((rw,off) |-> eq(y))` of the same size, i.e., with existential variable `y:llvmptr (8*sz)` where `sz` is the size of the left-hand permission in bytes; +* Prove `[l]ptr((rw,off+sz) |-> eq(z))` for existential variables `z` of the remaining size; +* Call `implLLVMFieldConcat` to concatenate these two field permissions; and +* Call `proveVarLLVMFieldH` with the resulting permission of the correct size as the left-hand permission. + +If the left-hand permission is an array permission where the required permission lines up with one of the cells of the array, borrow or copy (depending on whether the array is copyable) the corresponding array cell and recursively call `proveVarLLVMFieldH` with the cell permission that was borrowed. + +If the left-hand permission is an array permission where the required permission covers multiple cells of the array, borrow or copy those cells (depending on whether the array is copyable) as a single array permission, coerce the resulting cells to a field using the `SImpl_LLVMArrayToField` rule, and pass the resulting permission as the left-hand permission of a recursive call to `proveVarLLVMFieldH`. + +In all other cases, `proveVarLLVMFieldH2` fails. + + #### Proving Array Permissions + + #### Proving Block Permissions From 2bd62f8cdce4f31583c7aa794059defb0c962966 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Fri, 3 Jun 2022 18:23:03 -0700 Subject: [PATCH 09/20] moved most of the documentation into new files in the doc subdirectory --- heapster-saw/doc/ImplProver.md | 277 ++++++++++++ heapster-saw/doc/Permissions.md | 3 + heapster-saw/doc/Rules.md | 115 +++++ heapster-saw/doc/RustTrans.md | 111 +++++ .../src/Verifier/SAW/Heapster/README.md | 408 +----------------- 5 files changed, 510 insertions(+), 404 deletions(-) create mode 100644 heapster-saw/doc/ImplProver.md create mode 100644 heapster-saw/doc/Rules.md create mode 100644 heapster-saw/doc/RustTrans.md diff --git a/heapster-saw/doc/ImplProver.md b/heapster-saw/doc/ImplProver.md new file mode 100644 index 0000000000..6d4f5c13e4 --- /dev/null +++ b/heapster-saw/doc/ImplProver.md @@ -0,0 +1,277 @@ + +# The Heapster Implication Prover + +This document describes the Heapster implication prover. + +## The Implication Prover Monad + +The implication prover runs in the `ImplM` monad, whose type parameters are as follows: + +``` +ImplM vars s r ps_out ps_in a +``` + +An element of this type is an implication prover computation with return type `a`. The type variable `vars` lists the types of the existential variables, or _evars_, in scope. These represent "holes" in the permissions we are trying to prove. The type variables `s` and `r` describe the calling context of this implication computation at the top level: `s` describes the monadic state maintained by this calling context, while `r` describes the top-level result type required by this context. These types are left abstract in all of the implication prover. + +The type variables `ps_in` and `ps_out` describe the permission stack on the beginning and end of the computation. The existence of these two variables make `ImplM` a _generalized_ monad instead of just a standard monad, which means that these types can vary throughout an implication computation. The bind for `ImplM` is written `>>>=` instead of `>>=`, and has type + +``` +(>>>=) :: ImplM vars s r ps' ps_in a -> (a -> ImplM vars s r ps_out ps' b) -> + ImplM vars s r ps_out ps_in b +``` + +That is, the bind `m >>>= f` first runs `m`, which changes the permissions stack from `ps_in` to `ps'`, and then it passes the output of `m` to `f`, which changes the permissions stack from `ps'` to `s_out`, so the overall computation changes the permissions stack from `ps_in` to `ps_out`. As a more concrete example, the computation for pushing a permission onto the top of the stack is declared as + +``` +implPushM :: HasCallStack => NuMatchingAny1 r => ExprVar a -> ValuePerm a -> + ImplM vars s r (ps :> a) ps () +``` + +meaning that `implPushM` takes in a variable `x` and a permission `p` and returns a computation that starts in any permission stack `ps` and pushes permission `x:p` of type `a` onto the top of the stack. + +If the permission stack does not change, meaning that `ps_in` equals `ps_out`, then `ImplM` forms a monad. For instance, the function + +``` +partialSubstForceM :: (NuMatchingAny1 r, PermPretty a, + Substable PartialSubst a Maybe) => + Mb vars a -> String -> ImplM vars s r ps ps a +``` + +takes the current partial substitution for the evars and attempts to apply it to a value in a name-binding for those evars, raising an error if some evar that has not yet been instantiated is used in the value. (The "force" means to force the substitution to be defined or fail.) This function does not change the permission stack, and is in fact written in `do` notation in the code. + +The `ImplM` monad is defined as a generalized state-continuation monad. This construct is defined in `GenMonad.hs`, but we will not discuss it in too much detail here. The state that is maintained is given by the `ImplState` type, which contains information such as the current permission set, the types of all univeral and existential variables in scope, and the current instantiations of all the evars. The input and output types of the continuation portion of `ImplM` are both `PermImpl`, meaning each `ImplM` computation builds up an implication. The fact that `ImplM` is a continuation monad is only used in the `implApplyImpl1` function, which applies an `Impl1` rule by shifting the current continuation and re-applying it to build the sub-`PermImpl`s passed to that rule as an `MbPermImpls`. This means that rules with multiple disjunctive outputs, like or elimination and the catch rule, cause `ImplM` to fork its execution, running any subsequent computation once in each disjunctive branch. Thus, for performance reasons, it is helpful to reduce this forking as much as possible. + + +## Needed and Determined Variables + +One difficulty in doing proof search which must be addressed by the implication prover is that existential variables mean we do not have most general types. (There are other ways in which Heapster does not have most general types, but this is a more aggregious one.) For instance, there are two distinct ways to prove + +``` +x:ptr((R,0) |-> true) * x:ptr((R,8) |-> true) |- exists off:bv 64. x:ptr((R,off) |-> true) +``` + +by instantiating `off` to either 0 or 8. The difficulty is that if we choose the wrong value for `off` we might have to backtrack, potentially leading to an exponential search. The same problem occurs for function permissions with ghost variables, as ghost variables become existential variables that must be instantiated at call sites. Thus, for instance, Heapster cannot handle a function with permissions like + +``` +(off:bv 64). arg0:ptr((R,off) |-> true) -o empty +``` + +because it will not know how to instantiate `off` at the call site. Currently, this shows up as a type-checking error when such a function is called, but we could consider raising an error where such a function is defined. If you think about it, though, a function type like this does not really make any sense. How could a function take in a pointer to something where it doesn't know the offset of that pointer? + +If, however, there is some other permission that _determines_ the offset, then this problem is resolved. Consider, for instance, the following function type: + +``` +(off:bv 64). arg0:ptr((R,off) |-> true), arg1:eq(llvmword(off)) -o empty +``` + +This describes a function whose second argument says what the offset is for the first. Unlike the previous example, Heapster can handle this function type, because it will prove the equality permission on `arg1` first, and this proof will determine the value of `off` to be used for the permission on `arg0`. This function type also makes a lot more sense operationally, because now the function can know what the offset is. The more common version of this situation is passing the length of an array, using a type like this: + +``` +(len:bv 64). arg0:array(W,0,), arg1:eq(llvmword(len)) -o empty +``` + +A similar pattern can occur inside data structures. A common pattern in C is to have a `struct` with a variable-length array at the end, whose length is determined by one of the fields, like this: + +``` +struct foo { + ...; + int64_t len; + char data[]; +} +``` + +Rust slices are similar. A struct like this can be described by the Heapster shape + +``` +...; exsh len:bv 64.(fieldsh(eq(llvmword len));arraysh())) +``` + +This shape can be proved by the Heapster implication prover because the existential variable `len` in the shape is determined by the equality permission in the `len` field in the struct. If the struct did not have this field, Heapster would not be able to prove permissions with this shape. Again, such a shape does not really make sense, as the program would never know how long the `data` field is. + + +The Heapster implication prover addresses the problem of existential variables leading to non-unique types by requiring that all existential variables that could lead to this sort of problem in a permission `p` are assigned a uniquely determined value before it attempts to satisfy permission `p`. These variables are called the _needed_ variables of `p`, defined by the `neededVars` function in Permissions.hs. The needed variables include any free variables in the offsets and lengths of pointer, array, and block permissions, as well as any free variables of the more complicated permissions like lifetime ownership permissions. For equality permissions `eq(e)`, the free variables of `e` are not needed if `e` is a _determining_ expression, discussed below. In our example above, `off` is a needed variable on the right-hand side, so the implication prover will not prove this implication but will instead raise a type-checking error (with the `Impl1_Fail` rule described above). + +The only way to prove a permission with needed variables is if there is some other permission which is proved first that _determines_ the value of that variable. Intuitively, the idea is that a permission `p` determines an existential variable `x` if there is only one possible value of `x` for which `p` can possibly be proved. The canonical example of determination is the permission `eq(x)`: the only possible way to prove an `eq(x)` permission is to set `x` to the value that has this permission. If we are proving `y:eq(x)`, then `x` has to be set to `y`, while if we are proving a pointer permission `y:ptr((rw,off) |-> eq(x))`, `x` has to be set to the value pointed to by `y` at offset `off`. Note that, in this latter case, the implication prover will first prove some permission of the form `y:ptr((rw,off) |-> p)` and will then use the `Impl1_ElimLLVMFieldContents` rule to bind a local variable `z` for the value pointed to by `y` at offset `off`, so `x` will be set to this local variable `z`. In order to prove a pointer permission, however, the free variables in `off` (if there are any) must already be determined by some other permission, because these are needed variables of the pointer permission. Thus determined variables have a dependency structure, where some variables can only be determined if other variables are determined first. Further, a variable can not be determined by an equality inside an arbitrary permission. For instance, `eq(x) or p` does not determine `x`, because the proof may not take the left-hand branch of the disjunct. + +More generally, determined variables are defined by the `determinedVars` function. This function uses the helper function `isDeterminingExpr` to define whether an expression `e` used in an equality permission determines its variables. The following expression forms are determining: +* `x` +* `llvmword e` if `e` is determining +* `N*x + K` for constants `N` and `K` +* The permission `eq(e)` as an expression if `e` is determining +* `x &+ off` if `off` is determining +* Any expression with no free variables + +The `determinedVars` function is then defined as follows on permission `p`: + +| Permission `p` | Determined Variables | +|-------------|----------------------| +| `eq(e)` | The free variables of `e` if `e` is a determining expression, otherwise `[]` | +| `p1 * ... * pn` | The determined variables of the `pi` | +| `P` | The free variables of each determining expression in `args` | +| `[l]ptr((rw,off) \|-> p)` | The determined variables of `l`, `rw`, and `p`, if the variables in `off` are determined | +| `[l]array(rw,off, ImplM vars s r (ps_in :++: ps) ps_in () +``` + +This function attempts to prove `n` permisisons `x1:p1, ..., xn:pn`, adding those permissions to the top of the permissions stack. These permissions are inside of a binding for the existential variables specified by `vars`, which represent "holes" or unknown expressions that will be solved by building the proof. As an example, the type-checker for the pointer read instruction calls the implication prover with the existentially quantified permission + +``` +(rw,l,z). [l]ptr((rw,0) |-> eq(z)) +``` + +expressing that it requires a pointer permission at offset 0 with any lifetime `l`, any read/write modality `rw`, that points to any value `z`. + +There are a number of wrapper functions that call `proveVarsImplAppend`, including: + +* `proveVarsImpl`, which assumes the input permission stack is empty; +* `proveVarImpl`, which proves one permission; and +* `proveVarsImplVarEVars`, which is like `proveVarsImpl` but where all existential variables are instantiated with fresh variables. + +The top-level implication prover algorithm is then implemented as a descending sequence of "levels", each of is implemented as a function that performs some particular function and then calls the next level: + +| Function Name | Purpose | +--------------|----------| +| `proveVarsImplAppend` | Try to prove the required permissions, and, if that failos, non-deterministically end some lifetimes that could help in the proof | +| `proveVarsImplAppendInt` | Repeatedly call `findProvablePerm` to find the permission on the right that is most likely to be provable and then try to prove that permission | +| `proveExVarImpl` | Handle the case of a right-hand permission `x:p` where `x` itself is an evar by instantiating `x`, if possible | +| `proveVarImplInt` | Wrapper function that pushes the primary permissions for `x` onto the top of the stack, performs debug tracing, calls `proveVarImplH`, and then checks that the proved permission is correct | + + +## Proving a Permission + +The main logic for proving a permission is in the function `proveVarImplH`. (The implication prover uses the convention of using "`H`" as a suffix for helper functions.) As with many functions in the implication prover, this function takes in: a variable `x` that we are trying to prove a permission on; a permission `p` for `x` which is currently on top of the stack; and a permission `mb_p` inside a context of evars that we are trying to prove for `x`. (The prefix "`mb`" refers to "multi-binding", a binding of 0 or more evars.) The function then works by pattern-matching on `p` (the left-hand side) and `mb_p` (the right-hand side), using the following cases, some of which call out to helper functions described below: + +| Left-hand side | Right-hand side | Algorithmic steps taken | +|------------|--------------|--------------------| +| `p` | `true` | Pop `p` and Introduce a vacuous proof of `true` | +| `p` | `eq(e)` | Call `proveVarEq` to prove the equality | +| `p1 or p2` | `mb_p` | Eliminate the disjunction and recurse | +| `exists z. p` | `mb_p` | Eliminate the existential and recurse | +| `eq(y)` | `mb_p` | Prove `y:mb_p` and then cast the proof to `x:mb_p` | +| `eq(y &+ off)` | `mb_p` | Prove `y:(offsetPerm mb_p off)` and then case the proof to `x:mb_p` | +| `p` | `mb_p1 or mb_p2` | Nondeterminsitically try to prove either `mb_p1` or `mb_p2` | +| `p` | `exists z. mb_p` | Add a new evar for `z`, prove `x:mb_p`, and then use the value determined for `x` to introduce an existential permission | +| `P` | `mb_p` | Use the more specific rules below for named permissions | +| `p1 * ... * pi-1 * P * pi+1 * ... * pn` | `mb_p` | Use the more specific rules below for named permissions | +| `p` | `P` | Use the more specific rules below for named permissions | +| `eq(llvmword e)` | `p1 * ... * pn` | Fail, because we cannot prove any non-equality permissions for words, and the equality permissions were matched by an earlier case | +| `eq(struct(e1,...,en))` | `mb_p` | Eliminate `eq(struct(e1,...,en))` to a `struct(eq(e1),...,eq(en))` permission with equalities for each field | +| `eq(constant f)` | `(gs) ps_in -o ps_out` | Use an assumption on known function `f` | +| `p1 * ... * pn` | `mb_p1 * ... * mb_pn` | Call `proveVarConjImpl` to prove a conjunction implies a conjunction | +| `p` | `(X). X` | For existential permission variable `X`, set `X:=p` | +| `X` | `X` | For universal permission variable `X`, prove `X -o X` by reflexivity | +| `_` | `_` | In all other cases, fail | + + +## Proving Named Permissions + +Named permissions are of the form `P` for some permission name `P`. Each named permission represents some more complicated collection of permissions, that can depend on the argument expressions `args`. Permission names come in three sorts: + +* _Defined names_ are essentially abbreviations, where `P` unfolds to some permission `p` that does not contain `P` itself (unless `P` occurs `args`); + +* _Recursive names_ `P` are similar to defined names but where `P` unfolds to a permission that can contain `P`; and + +* _Opaque names_ are permissions which are too complicated to represent in Heapster, so Heapster just represents them with names. + +The "best" way to prove a named permission `P` is by reflexivity, i.e., to find an instance of `P` that is already in the current permissions set. The implication rules do allow some amount of weakening the arguments, so technically this is a search for `P` for some argument list `args'` that can be coerced to `args`. For opaque names, this is the only way to prove `P`. For defined names, the other option is to just unfold `P` to its definition, prove that permission, and then fold the result to `P`. Similarly, recursive names can also be unfolded to prove them. Dually, if there is a permission `P` with a defined or recursive name on the left, meaning it is already held in the current permission set, the implication prover will unfold this permission if it gets stuck trying to prove its goal any other way. This logic is implemented by the `implUnfoldOrFail` function, which is called in a number of places in the implication prover where it will otherwise fail. + +The one wrinkle is that unfolding recursive names can lead to non-termination. This can happen if we have an assumption `P1` on the left and we are trying to prove `P2` on the right where both `P1` and `P2` are recursive names. If we proceed by unfolding both `P1` and `P2`, then, because these unfoldings can each contain `P1` and `P2` again, we can end up back at the same proof goal, of having an assumption `P1` on the left and trying to prove `P2`. To prevent this infinite regress, the implication prover is restricted so that it will not unfold names on both the left and right sides in the same proof. This is done by maintainining a flag called `implStateRecRecurseFlag` that tracks whether there has been an unfolding of a recursive name on one side or the other. Whenever the implication prover has `P1` on the left and `P2` on the right for recursive names `P1` and `P2`, it then non-deterministically (using the `Impl1_Catch` rule to backtrack) chooses one side to unfold, and it proceeds from there. This handles the possibility that one of `P1` or `P2` contains the other as a sub-permission. + +In more detail, here are the cases of `proveVarImplH` that handle recursive permissions: + +| Left-hand side | Right-hand side | Algorithmic steps taken | +|------------|--------------|--------------------| +| `P` | `P` | For reachabilitiy permission `P`, nondeterministically prove the RHS by either reflexivity, meaning `x:eq(mb_e)`, or transitivity, meaning `e:P` | +| `P` | `P` | For non-reachabilitiy named permission `P`, prove `args` _weakens to_ `mb_args`, where write modalities weaken to read, bigger lifetimes weaken to smaller ones, and otherwise arguments weaken to themselves | +| `p1 * ... * pi-1 * P * pi+1 * ... pn` | `P` | Similar to above | +| `p` | `P` | If `P` is a _defined_ (i.e., non-recursive) name, unfold `P` to its definition and recurse | +| `P` | `mb_p` | If `P` is a defined name, unfold `P` to its definition and recurse | +| `p1 * ... * pi-1 * P * pi+1 * ... pn` | `mb_p` | If `P` is defined, unfold `P` to its definition and recurse | +| `P1` | `P2` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose one side to unfold | +| `p1 * ... * pi-1 * P1 * pi+1 * ... pn` | `P2` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose one side to unfold | +| `p` | `P` | If `P` is recursive, unfold `P` and recurse | +| `P` | `mb_p` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose + + +## Proving Equalities and Equality Permissions + +Equality permissions are proved by `proveVarEq`, which takes a variable `x` and an expresson `mb_e` in a binding of the existential variables, and proves `x:eq(e)` for some instantiation `e` of the variables in `mb_e`. This function pushes a reflexive proof that `x:eq(x)`, calls `proveEq` to build an equality proof that `x=e`, and uses the equality proof with the `SImpl_CastPerm` rule to cast the proof of `x:eq(x)` on top of the stack to `x:eq(e)`. The meat of `proveVarEq` is thus in `proveEq`, which attempts to build equality proofs. The `proveEq` function is also called in other parts of the implication prover, e.g., to coerce the modalities of field, array, and block permissions. + +An equality proof in Heapster is a transitive sequence of equality proof steps `e=e',e'=e'',...`. Each step is a sequence of equality permissions `x1:eq(e1),...,xn:eq(en)`, where each equality `xi:eq(ei)` is oriented either left-to-right as `xi=ei` or right-to-left as `ei=xi`, along with a function `f` on `n` expressions. This represents the equality `f(left1,...,leftn)=f(right1,...,rightn)`, where `lefti` and `righti` are the left- and right-hand sides of the `i`th oriented version `xi=ei` or `ei=xi` of the permission `xi:eq(ei)`. Equality steps are represented by the Haskell type `EqProofStep ps a`, where `ps` is a list of the types of the variables `x1,...,xn` and `a` is the Haskell type of the objects being proved equal. (Equality proofs can be used not just on expressions, but at other types as well.) Entire equality proofs are represented by the type `EqProof ps a`, while the type `SomeEqProof a` is an equality proof where the permissions needed to prove it are existentially quantified. + +[comment]: <> (FIXME HERE: describe `proveEq` and `proveEqH`) + + +## Proving Conjuncts of Permissions + +Conjuncts `p1 * ... * pn` are proved by `proveVarConjImpl`, which repeatedly picks the "best" permission on the right to prove and calls `proveVarAtomicImpl` to prove it. Finding the "best" permission prioritizes defined permissions first, followed by recursive permissions, as this fits with the named permissions algorithm described above, followed by finding a permission whose needed variables have all been determined. + +The cases for `proveVarAtomicImpl` are as follows: + +| Permission to prove | Algorithmic steps taken | +|----------------|--------------------| +| `[l]ptr((rw,off) \|-> p)` | Call `proveVarLLVMField` | +| `[l]array(rw,off, p)`, the `proveVarLLVMField` function starts by first calling `implGetLLVMPermForOffset` to find a permission on the left that contains `off`. If this permission is a `memblock` permission, it is repeatedly eliminated, using the helper function `implElimLLVMBlock`, until an array or field permission is obtained. This permission is then passed to `proveVarLLVMFieldH`, which calls `proveVarLLVMFieldH2`, which dispatches based on the form of the permission. We call this the left-hand permission in this discussion, since `implGetLLVMPermForOffset` puts it on the top of the stack, i.e., the left of the implication. + +The main case `proveVarLLVMFieldH2` is when the left-hand permission is a field permission +`[l']ptr((rw',off) |-> p')` of the same size as the required one. In this case, `proveVarLLVMFieldH2` performs the following steps: +* Eliminate the contents of the field permission to get a permission of the form `[l']ptr((rw',off) |-> eq(y))` for some variable `y`; +* Prove `y:p` with a recursive call to `proveVarImplInt`; +* Use the `SImpl_IntroLLVMFieldContents` to combine the `y:p` permission into the left-hand permission to get `[l']ptr((rw',off) |-> p)`; +* Coerce the lifetime `l'` to `l` by calling `proveVarLifetimeFunctor`, which splits or ends lifetimes as needed; +* Coerce `rw'` to `rw` by calling `equalizeRWs`, which either proves the two are equal using `proveEq` and casting or weakens a write modality to a read modality; and +* Duplicate and recombine the pointer permission if it is copyable. + +If the left-hand permission is a pointer permission that is bigger than required, split the left-hand permission by calling `implLLVMFieldSplit`, recombine the part that is not required, and recursively call `proveVarLLVMFieldH` with the remaining left-hand permission. + +If the left-hand permission is a pointer permission that is smaller than required: +* Recursively call `proveVarLLVMFieldH` with the same left-hand permission to prove a pointer permission `[l]ptr((rw,off) |-> eq(y))` of the same size, i.e., with existential variable `y:llvmptr (8*sz)` where `sz` is the size of the left-hand permission in bytes; +* Prove `[l]ptr((rw,off+sz) |-> eq(z))` for existential variables `z` of the remaining size; +* Call `implLLVMFieldConcat` to concatenate these two field permissions; and +* Call `proveVarLLVMFieldH` with the resulting permission of the correct size as the left-hand permission. + +If the left-hand permission is an array permission where the required permission lines up with one of the cells of the array, borrow or copy (depending on whether the array is copyable) the corresponding array cell and recursively call `proveVarLLVMFieldH` with the cell permission that was borrowed. + +If the left-hand permission is an array permission where the required permission covers multiple cells of the array, borrow or copy those cells (depending on whether the array is copyable) as a single array permission, coerce the resulting cells to a field using the `SImpl_LLVMArrayToField` rule, and pass the resulting permission as the left-hand permission of a recursive call to `proveVarLLVMFieldH`. + +In all other cases, `proveVarLLVMFieldH2` fails. + + +## Proving Array Permissions + + + +## Proving Block Permissions + diff --git a/heapster-saw/doc/Permissions.md b/heapster-saw/doc/Permissions.md index 9894d36726..5905203875 100644 --- a/heapster-saw/doc/Permissions.md +++ b/heapster-saw/doc/Permissions.md @@ -75,6 +75,8 @@ In addition to the above expressions, we also have shape expressions, which we s | `fieldsh(sz,p)` | `llvmshape w` | Like `fieldsh(p)`, but where the permission acts on a pointer of size `sz`. | | `arraysh(s,len,sh)` | `llvmshape w` | A shape for an array with the given stride, length (in number of elements = total length / stride), and fields `sh` | | `exsh x:a.sh` | `llvmshape w` | An existential shape | +| `falsesh` | `llvmshape w` | The unsatisfiable or contradictory shape | + Value permissions ================= @@ -108,6 +110,7 @@ For a variable `x:a`, the proposition `x:p` means "`x` has permission `p`": this | `p1 * p2` | `perm(a)` | separating conjunction; `p1` and `p2` (both satisfying `perm(a)`) must be atomic permissions (not a disjunction, existential, or equality permission) | | `eq(e)` | `perm(a)` | a value equal to the expression `e:a` | | `exists x:a. p` | `perm(b)` | there exists some expression `e:a` such that `p[e/x]:perm(b)` holds | +| `false` | `perm(a)` | The unsatisfiable or contradictory permission | | `x@o` | `perm(a)` | A named permission with expression arguments, with optional offset expression `o`. Named permissions can either be (1) defined permissions (aliases) (2) recursive permissions, or (3) opaque permissions (axioms). | | `[l]array(rw, off,
  • `l` is the lifetime during which this permission is active;
  • `off` is a permission expression representing the offset of this array from the pointer in bytes;
  • `len` is a permission expression representing the number of cells in the array;
  • `stride` is a permission expression representing the number of bytes in a cell;
  • `sh` is a shape expression representing the shape of elements in the array
  • | | `[l]memblock(rw,o,len,sh)` | `perm(llvmptr w)` | gives read or write access to a memory block, whose contents also give some permissions, where:
    • `rw` indicates whether this is a read or write block permission
    • `l` is the lifetime during which this block permission is active
    • `o` is the offset of the block from the pointer in bytes
    • `len` is the length of the block in bytes
    • `sh` is the shape of the block
    | diff --git a/heapster-saw/doc/Rules.md b/heapster-saw/doc/Rules.md new file mode 100644 index 0000000000..21d3e58e1b --- /dev/null +++ b/heapster-saw/doc/Rules.md @@ -0,0 +1,115 @@ +# Heapster Permission Implication + +Heapster permission implication is a form of _affine logic_, which in turn is a form of the better-known concept of linear logic. Linear logic is a logic where each proposition assumed by a proof must be used exactly once in that proof. Propositions in linear logic can thus be viewed as a form of "resource" that gets used up in building a proof. For example, consider the rule + +``` +dollar * dollar * dollar |- candy_bar +``` + +to represent the concept that a particular candy bar costs $3. Intuitively, the `dollar` proposition represents possession of a dollar, `candy_bar` represents possession of a (reasonably fancy) candy bar, and `*` represents the conjunction of two propositions. A "proof" using this rule consumes three `dollar` propositions and generates one `candy_bar` proposition, intuitively representing the purchase of this candy bar. Importantly, unlike standard classical or even intuitionistic logic, where `p and p` is equivalent to `p`, the conjunction `p * p` in linear logic represents two copies of the proposition `p`, which in general is different than `p` by itself; e.g., if we could prove `dollar |- dollar * dollar` then we could generate all the money we wanted. This is not to say that `p |- p * p` is never true, just that it is only true for some `p`, which correspond to resources that can be duplicated. See any introduction to linear logic for more details. + +Affine logic is a version of linear logic where propositions can be "thrown away", that is, where the rule `p * q |- p` holds for all `p` and `q`. The reason we use affine logic here is that it is useful for describing a notion of _permissions_, where each `p` intuitively corresponds to permission to perform a particular action. It is always justified to forget some permission if you are not going to use it, but you can't in general give yourself more permissions. One of the central permissions used in Heapster is the permission to access memory through a particular pointer. The simplest form of this is the pointer permission `x:ptr((rw,off) |-> p)`, that represents a permission to read — and possibly write, depending on `rw` — memory at offset `off` from `x`, along with permission `y:p` to whatever value `y` is currently stored there. The `array` and `memblock` permissions also represent different forms of permission to read and possibly write memory, with different stipulations on the permissions held for the values currently stored there. Read-only permissions are copyable, meaning that `x:ptr((R,off) |-> p) |- x:ptr((R,off) |-> p) * x:ptr((R,off) |-> p)` can be proved in Heapster, as long as `p` does not contain any write permissions, while write permissions `x:ptr((W,off) |-> p)` are not. This corresponds to the one-writer or multiple readers paradigm of, e.g., Rust. + +The remainder of this section explains Heapster implication. + + +## Permission Implication Rules + +At any given point during type-checking and/or implication proving, Heapster maintains a _permission set_ that describes the current permissions to various objects that are currently held by the program. Permission sets are defined by the `PermSet` type in Permissions.hs, and have two components: the _permissions map_, which maps each variable `x` in scope to what is called the _primary_ permission held on `x`; and the _permissions stack_, which represents the permissions that are actively being used or manipulated. We write a permission set as: + +``` +x1 -> px1 * ... * xm -> pxm; y1:p1 * ... * yn:pn +``` + +The portion before the semicolon represents the permissions map, which maps each `xi` to its primary permission `pxi`, while the portion after the semicolon represents the permissions stack, containing permissions `y1:p1` through `yn:pn` in sequence. The final permissions `yn:pn` is the top of the stack. We often write `PS` for a permission set. + +The general form of permission implication is the judgment + +``` +PS |- (z1_1, ..., z1_k1 . PS1) \/ ... \/ (zn_1, ..., zn_kn . PSn) +``` + +which says that, starting with permission set `PS` on the left-hand side of the turnstyle `|-`, we can prove one of the permission sets `PSi` on the right-hand side. Each disjunctive case could introduce 0 or more existential variables `zi_1, ..., zi_ki`, which can be used in the corresponding permission set `PSi`. We often omit the permissions map and/or the existential variables when they are not necessary; e.g., we write `PS1 |- PS2` instead of `PS1 |- ( [] . PS2)`. We also tend to omit the permissions map from implications, as permissions maps almost never change; thus, e.g., we might write `x:p |- y:q` instead of `x1 -> px1 * ... * xm -> pxm; x:p |- x1 -> px1 * ... * xm -> pxm; y:q`. + +Permission implication in Heapster is actually a sort of "partial implication". The above-displayed implicaiton judgment in fact says that, if we hvae permission set `PS`, we can _try_ to get one of the permission sets `PSi`, though we can't control which one we get, and we might fail. What this failure means exactly is a little hard to define without going into the details of the translation to SAW core / Coq and relationship between the resulting term and the original program. As one way to understand what failure means here, consider that each permission set `PS` actually describes a set of possible states, one for each substitution of values for all the free variables in `PS`. For some of these states, we can exchange the permissions in `PS` for the permissions in one of the `PSi`, though in some of those states, trying to do this leads to undefined behavior, or at least behavior we are not going to reason about. Another way to think about Heapster implication is to always add an extra disjunction `\/ dunno` to each right-hand side, so an implication `PS |- PS1 \/ ... \/ PSn` becomes `PS |- PS1 \/ ... \/ PSn \/ dunno`, meaning that from permissions `PS` we either can get one of the `PSi` or we get a result that says that we have to give up on modeling the current execution of the program. At a slightly more technical level, failure means means that the translation of a failure is just the `errorM` computation, which, again, doesn't mean that the original computation actually has an error, just that we don't know how to reason about it. Either way, we will simply say "prove" or "implies" below instead of something like "partially prove in some states". + +Permission implications are built up from two rules, the identity rule and the step rule. The identity rule is just a proof that `PS |- PS`. The step rule looks like this: + +``` +PS |-(1) (zs1.PS1) \/ ... \/ (zsn.PSn) +PS1 |- (zs1_1.PS1_1) \/ ... \/ (zs1_k1.PS1_k1) +... +PSm |- (zsm_1.PSm_1) \/ ... \/ (zsm_km.PSm_km) +----------------------------------------------------- +PS |- (zs1_1.PS1_1) \/ ... \/ (zs1_k1.PS1_k1) \/ ... \/ (zsm_1.PSm_1) \/ ... \/ (zsm_km.PSm_km) +``` + +Intuitively, this says that we can start with an implication and then apply further implications to each of the output permission sets of the original implication, yielding a bigger implication of all of the disjuncts returned by all of the further implications. The notation `|-(1)` denotes a single step of implication, which is built using one of the single-step rules that we describe below. Intuitively, this means that a permission implication can be viewed as a tree, whose leaves are identity rules and whose internal nodes are step rules whose shapes are defined by the single step `|-(1)` implication. + +Permission implications are represented in Haskell by the type `PermImpl r ps`. The type variable `ps` is a Haskell datakind that specifies a sequence of Crucible types for the variables and permissions on the stack at the beginning of the proof. For example, the representation of an implication `x1:p1 * ... * xn:pn |- PS1 \/ ... \/ PSn` will have type `PermImpl r (RNil :> t1 :> ... :> tn)` in Haskell, where each `xi` has Crucible type `ti` and each `pi` has the corresponding Crucible type `ValuePermType ti` (which is the type of a permission that applies to an element of type `ti`). The datakind `RNil` is the empty sequence. (The "R" stands for "right-associated list", because the cons operator `:>` adds new list elements on the right instead of the left; this datakind is defined in the Hobbits library, but is identical to the one defined in Data.Parameterized.Ctx.) + +In addition to describing the form of an implication, the `PermImpl` representation in Haskell also contains a "result" for each output permission set. That is, permission implications are a form of tree, as described above, and the `PermImpl` type stores results `r1, ..., rn` at each leaf `PS1, ..., PSn` in an implication of `PS |- PS1 \/ ... \/ PSn`. The result type is given by the `r` argument to `PermImpl`, and this type is parameterized by the datakind corresponding to the types of the permissions on the stack at that point. That is, a permission implication `PS |- PS1 \/ ... \/ PSn` will contain elements of type `r ps1` through `r psn`, assuming that each `psi` is the Haskell datakind that represents the stack for each `PSi`. Intuitively, the result does something with the permissions `PSi`. The most common example of this is in the typed representation of functions used in TypedCrucible.hs, where a function can contain a permission implication, using the `TypedImplStmt` constructor, to coerce the permissions it currently holds to some form that is needed to perform an operation. For instance, a `load` instruction requires the permissions currently held by a program to be coerced to a `ptr` permission. Whenever an implication `PS |- PS1 \/ ... \/ PSn` occurs in a typed Crucible representation, the remaining instructions must be type-checked relative to each of the permission sets `PSi`. This is represented by having the `PermImpl` representation contain one copy of the remaining instructions for each output `PSi`, type-checked relative to that permission set. + +The one-step implication rules defined by the `|-(1)` judgment are defined by the `PermImpl1 ps_in ps_outs` type, which represents a rule with input stack described by datakind `ps_in` and 0 or more disjunctive output stacks given by `ps_outs`, which is a list of 0 or more disjuncts that bind 0 or more existential variables and leave 0 or more types on the stack. (See the documentation of `PermImpl1` for more details.) These include the following rules (along with a few more that we do not discuss here): + +| Rule name | Rule description | Rule implication | +----------|-------------|-----------------| +| `Impl1_Fail` | Failure of implication | `ps \|-(1) empty` (where `empty` is 0 disjuncts) | +| `Impl1_Catch` | Try one implication and then a second if the first fails | `ps \|-(1) ps \/ ps` | +| `Impl1_Push` | Push the primary permission for `x` onto the stack | `..., x -> p; ps \|-(1) ..., x -> true; ps * x:p` | +| `Impl1_Pop` | Pop the top of the stack back to the primary permission for `x` | `..., x -> true; ps * x:p \|-(1) ..., x -> p; ps` | +| `Impl1_ElimOr` | Eliminate a disjunction on the top of the stack | `ps * x:(p1 or p2) \|-(1) (ps * x:p1) \/ (ps * x:p2)` | +| `Impl1_ElimExists` | Eliminate an existential on the top of the stack | `ps * x:(exists z.p) \|-(1) (z. ps * x:p)` | +| `Impl1_Simpl` | Apply a simple implication of the form `ps1 \|- ps2` | `ps * ps1 \|-(1) ps * ps2` | +| `Impl1_ElimLLVMFieldContents` | Extract the contents of an LLVM pointer permission | `ps * x:ptr((rw,off) -> p) \|-(1) y. ps * x:ptr((rw,off) -> eq(y)) * y:p` | + +[comment]: <> (FIXME: explain the above rules!) + +The most common implication rule is `Impl1_Simpl`, which applies a "simple" implication rule that exactly only one disjunctive output permission and binds no variables. The simple implication rules are described by the type `SimplImpl ps_in ps_out`. A rule of this type assumes that permissions `ps_in` are on the top of the stack, though there can be more permissions below these on the stack. It then consumes `ps_in`, replacing them with permissions `ps_out`. (As above, the `ps_in` and `ps_out` type arguments in Haskell are actually datakinds capturing the types of the input and output permissions of the rule.) These include too many rules to list here, so we only describe enough of them to give a flavor of what they do. + +Some of the simple implication rules are structural. These include the following: + +| Rule name | Rule description | Rule implication | +----------|-------------|-----------------| +| `SImpl_Drop` | Drop a permission | `x:p \|- .` | +| `SImpl_Copy` | Copy any permission that is copyable, i.e., satisfies `permIsCopyable` | `x:p \|- x:p * x:p` | +| `SImpl_Swap` | Swap the top two permissions on the stack | `x:p1 * y:p2 \|- y:p2 * x:p1` | +| `SImpl_MoveUp` | Move a permission towards the top of the stack | `x:p * ps1 * ps2 \|- ps1 * x:p * ps2` | +| `SImpl_MoveDown` | Move a permission away from the top of the stack | `ps1 * x:p * ps2 \|- x:p * ps1 * ps2` | +| `SImpl_IntroConj` | Prove an empty conjunction (which is the same as `true`) | `. \|- x:true` | +| `SImpl_ExtractConj` | Extract the `i`th conjunct of a conjunction | `x:(p0 * ... * p(n-1)) \|- x:pi * x:(p0 * ... p(i-1) * p(i+1) ... * p(n-1))` | +| `SImpl_CopyConj` | Copy the `i`th conjunct of a conjunction, assuming it is copyable | `x:(p0 * ... * p (n-1)) \|- x:pi * x:(p0 * ... * p(n-1))` | +| `SImpl_InsertConj` | Insert a permission into a conjunction | `x:p * x:(p0 * ... * p(n-1)) \|- x:(p0 * ... * p(i-1) * p * pi * ... * p(n-1))` | +| `SImpl_AppendConjs` | Combine the top two conjunctions on the stack | `x:(p1 * ... * pi) * x:(pi+1 * ... * pn) \|- x:(p1 * ... * pn)` | +| `SImpl_SplitConjs` | Split the conjunctive permissions on the top of the stack in two | `x:(p1 * ... * pn) \|- x:(p1 * ... * pi) * x:(pi+1 * ... * pn)` | + + +The elimination rules for disjunctions and existentials are `PermImpl1`s, because the former has multiple disjuncts and the latter introduces local variables, but their introduction rules are simple implications, as are both the introduction and elimination rules for named permissions: + +| Rule name | Rule description | Rule implication | +----------|-------------|-----------------| +| `SImpl_IntroOrL` | Prove a disjunctive permission from its left disjunct | `x:p1 \|- x:(p1 or p2)` | +| `SImpl_IntroOrR` | Prove a disjunctive permission from its right disjunct | `x:p2 \|- x:(p1 or p2)` | +| `SImpl_IntroExists` | Prove an existential permission from a substitution instance | `x:[e/z]p \|- x:(exists z.p)` | +| `SImpl_FoldNamed` | Prove a named permission from its unfolding | `x:(unfold P args) \|- x:P` | +| `SImpl_UnfoldNamed` | Eliminate a named permission by unfolding it | `x:P \|- x:(unfold P args)` | + + +Equality permissions are manipulated with the following simple implication rules: + +| Rule name | Rule description | Rule implication | +----------|-------------|-----------------| +| `SImpl_IntroEqRefl` | Prove any `x` equals itself | `. \|- x:eq(x)` | +| `SImpl_InvertEq` | Prove if `x` equals `y` then `y` equals `x` | `x:eq(y) \|- y:eq(x)` | +| `SImpl_InvTransEq` | Prove that if `x` and `y` equal the same `e` then they equal each other | `x:eq(e) * y:eq(e) \|- x:eq(y)` | +| `SImpl_LLVMWordEq` | If `y` equals `e` then `llvmword(y)` equals `llvmword(e)` | `x:eq(llvmword(y)) * y:eq(e) \|- x:eq(llvmword(e))` | +| `SImpl_LLVMOffsetZeroEq` | Offsetting an LLVM value by `0` preserves equality | `. \|- x:eq(x &+ 0)` | +| `SImpl_InvertLLVMOffsetEq` | Subtract an offset from both sides of an LLVM value equality | `x:eq(y+off) \|- y:eq(x-off)` | +| `SImpl_Cast` | Cast the variable of a permission using an equality | `x:eq(y) * y:p \|- x:p` | +| `SImpl_CastPerm` | Cast a permission `p` to `p'`, assuming that `x1=e1`, ..., `xn=en` imply that `p=p'` | `x1:eq(e1) * ... * xn:eq(en) * x:p \|- x:p'` | + + +[comment]: <> (FIXME: Implementation of the rules: `simplImplIn` and `simplImplOut`, `applyImpl1`: these all check for correct perms) + +[comment]: <> (FIXME: Explain overall pattern of the simplimpl rules: intro vs elim rules for most constructs) + diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md new file mode 100644 index 0000000000..12b2ac0cba --- /dev/null +++ b/heapster-saw/doc/RustTrans.md @@ -0,0 +1,111 @@ + +# Rust-to-Heapster Translation + +FIXME: Rust translates to a subset of Heapster, as described in this document + +## Translating Expression Types + +Unlike in many languages where types describe program values, Rust types in fact +describe the shape and structure of blocks of memory. Each Rust variable +designates a block of memory where the value of the variable is stored. The type +of the variable then describes the shape of that memory. Thus, Rust types are +translated to Heapster shape expressions, which Heapster uses to describe +memory. Heapster shapes are documented [here](Permissions.md). The basic +conversion from Rust is described in the following table, though Rust implements +a number of layout optimizations, described below, that alter this translation. +In this table, we write `[| T |]` for the translation of Rust type `T` to a +Heapster shape, and we write `len(sh)` for the Heapster expression giving the +length of Heapster shape `sh`, when this is defined. The notation `[\| Name \|]` +denotes the translation of the type definition associated with type name `Name`, +as defined in the next section. + + +| Rust Type | Translation to a Heapster Shape | +|--------|--------------------| +| `Box` | `ptr((W,0) \|-> [\| T \|])` | +| `&mut 'a T` | `[a]ptr((W,0) \|-> [\| T \|])` | +| `&'a T` | `[a]ptr((R,0) \|-> [\| T \|])` | +| `[T]` | `exists n:bv 64. arraysh(n, len([\| T \|]), [\| T \|])` | +| `(T1,...,Tn)` | `[\| T1 \|] ; ... ; [\| Tn \|]` | +| `Name<'a1,...,'am,T1,...,Tn>` | `[\| Name \|] (a1, ..., am, [\| T1 \|], ..., [\| Tn \|])` | +| `!` | `falsesh` | + +FIXME: describe the above + + +## Translating Type Definitions + +Rust includes type definitions for structure and enumeration types, which allow +the user to define a type name `Name` as either a sequence of Rust types or a +tagged disjunction of sequences of Rust types, respectively. These type +definitions can be polymorphic, meaning that the can quantify over Rust +lifetimes and types. They can also be recursive, meaning the definition of +`Name` can include `Name` itself. + +Both structure and enumeration types are translated to Heapster by using the SAW +command + +``` +heapster_define_rust_type env "...Rust type definition..." +``` + +This command adds a Heapster named shape to the current Heapster environment +`env` with the same name as the Rust type definition. + + +Rust structure types are written + +``` +pub struct Name<'a1,...,'am,X1,...,Xn> { fld1 : T1, ..., fldn : Tn } +``` + +This type is essentially a sequence type, and is translated to a Heapster named +shape defined as follows: +``` +Name = [\| T1 \|] ; ... ; [\| Tn \|] +``` +As with the translation of Rust tuple types, this translates a Rust structure +type to the sequence shape built from sequencing the translations of the +structure fields. Note that Heapster named shapes can be recursive, which is the +case is the original definition of `Name` is recursive. + + +Rust enumeration types are written + +``` +enum Name<'a1,...,'am,X1,...,Xn> { + Ctor1 (T1_1,...,T1_k1), + Ctor2 (T2_1,...,T2_k2), + ... + Ctorl (Tl_1,...,Tl_kl) +} +``` + +This defines `Name` as a disjunctive type, whose elements are sequences of one +of the lists `Ti_1, ..., Ti_ki` of types. To identify which of these disjunctive +cases holds for a particular block of memory, the block always starts with a +tag, also called a _discriminant_, that is an integer in the range `0,...,l-1`. +An enumeration type like the above is translated to Heapster as follows: + +``` +Name = + (fieldsh(eq(0)) ; [| T1_1 |] ; ... ; [| T1_k1 |]) orsh + (fieldsh(eq(1)) ; [| T2_1 |] ; ... ; [| T2_k2 |]) orsh + ... + (fieldsh(eq(l-1)) ; [| Tl_1 |] ; ... ; [| Tl_kl |]) +``` + +(NOTE: Technically speaking, this translation assumes the enum has been +flagged with the `#[repr(C,u64)]` pragma to indicate that the discriminant is a +64-bit integer and that the type is laid out in a C-compatible manner.) + + +## Layout Optimizations + +FIXME: Option-like types + +## Translating Function Types + +FIXME: +- explain layout (types that take more than two fields become pointers) +- explain lifetimes diff --git a/heapster-saw/src/Verifier/SAW/Heapster/README.md b/heapster-saw/src/Verifier/SAW/Heapster/README.md index 75d8264e7d..0dbebd1154 100644 --- a/heapster-saw/src/Verifier/SAW/Heapster/README.md +++ b/heapster-saw/src/Verifier/SAW/Heapster/README.md @@ -7,414 +7,14 @@ This directory contains an implementation of the Heapster portion of SAW. The central components of Heapster are in the following files: -* Permissions.hs: This file defines the language of _permissions_, which are the types in Heapster. Heapster permissions are defined by the `ValuePerm` datatype, which is defined mutually with the type `PermExpr` of Heapster expressions. +* Permissions.hs: This file defines the language of _permissions_, which are the types in Heapster. Heapster permissions are defined by the `ValuePerm` datatype, which is defined mutually with the type `PermExpr` of Heapster expressions. See [here](../../../../doc/Permissions.md) for more detail on the Heapster permission langauge. -* Implication.hs: This file defines the concept of _permission implication_ in Heapster, which is a form of subtyping on the Heapster permission types. Permission implication is defined by the `PermImpl` type, which represents a proof that one permission implies, or is a subtype of, another. This file also contains the implication prover, which is the algorithm that attempts to build permission implication proofs. The implication prover is described in more detail below. +* Implication.hs: This file defines the concept of _permission implication_ in Heapster, which is a form of subtyping on the Heapster permission types. Permission implication is defined by the `PermImpl` type, which represents a proof that one permission implies, or is a subtype of, another. This file also contains the implication prover, which is the algorithm that attempts to build permission implication proofs. The implication rules are described [here](../../../../doc/Rules.md), while the implication prover is described [here](../../../../doc/ImplProver.md). * TypedCrucible.hs: This file defines a version of Crucible control-flow graphs (CFGs) that have been type-checked by Heapster. Each Crucible data type used to define CFGs, including the type `CFG` itself, has a corresponding data type in TypedCrucible.hs with `"Typed"` prefixed to its name. This includes the `TypedCFG` type used to represent an entire typed-checked CFG. This file also contains the function `tcCFG` that performs type-checking on a Crucible CFG, along with helper functions used to type-check the various component pieces of Crucible CFGs. * SAWTranslation.hs: This file defines the translation from type-checked Crucible CFGs to SAW core terms that represent their specifications. -[comment]: <> (FIXME: describe the other files) - - -## Heapster Permission Implication - -Heapster permission implication is a form of _affine logic_, which in turn is a form of the better-known concept of linear logic. Linear logic is a logic where each proposition assumed by a proof must be used exactly once in that proof. Propositions in linear logic can thus be viewed as a form of "resource" that gets used up in building a proof. For example, consider the rule - -``` -dollar * dollar * dollar |- candy_bar -``` - -to represent the concept that a particular candy bar costs $3. Intuitively, the `dollar` proposition represents possession of a dollar, `candy_bar` represents possession of a (reasonably fancy) candy bar, and `*` represents the conjunction of two propositions. A "proof" using this rule consumes three `dollar` propositions and generates one `candy_bar` proposition, intuitively representing the purchase of this candy bar. Importantly, unlike standard classical or even intuitionistic logic, where `p and p` is equivalent to `p`, the conjunction `p * p` in linear logic represents two copies of the proposition `p`, which in general is different than `p` by itself; e.g., if we could prove `dollar |- dollar * dollar` then we could generate all the money we wanted. This is not to say that `p |- p * p` is never true, just that it is only true for some `p`, which correspond to resources that can be duplicated. See any introduction to linear logic for more details. - -Affine logic is a version of linear logic where propositions can be "thrown away", that is, where the rule `p * q |- p` holds for all `p` and `q`. The reason we use affine logic here is that it is useful for describing a notion of _permissions_, where each `p` intuitively corresponds to permission to perform a particular action. It is always justified to forget some permission if you are not going to use it, but you can't in general give yourself more permissions. One of the central permissions used in Heapster is the permission to access memory through a particular pointer. The simplest form of this is the pointer permission `x:ptr((rw,off) |-> p)`, that represents a permission to read — and possibly write, depending on `rw` — memory at offset `off` from `x`, along with permission `y:p` to whatever value `y` is currently stored there. The `array` and `memblock` permissions also represent different forms of permission to read and possibly write memory, with different stipulations on the permissions held for the values currently stored there. Read-only permissions are copyable, meaning that `x:ptr((R,off) |-> p) |- x:ptr((R,off) |-> p) * x:ptr((R,off) |-> p)` can be proved in Heapster, as long as `p` does not contain any write permissions, while write permissions `x:ptr((W,off) |-> p)` are not. This corresponds to the one-writer or multiple readers paradigm of, e.g., Rust. - -The remainder of this section explains Heapster implication in more detail and then describes the permission implication prover algorithm used by Heapster used to prove these implications. - - -### Permission Implication Rules - -At any given point during type-checking and/or implication proving, Heapster maintains a _permission set_ that describes the current permissions to various objects that are currently held by the program. Permission sets are defined by the `PermSet` type in Permissions.hs, and have two components: the _permissions map_, which maps each variable `x` in scope to what is called the _primary_ permission held on `x`; and the _permissions stack_, which represents the permissions that are actively being used or manipulated. We write a permission set as: - -``` -x1 -> px1 * ... * xm -> pxm; y1:p1 * ... * yn:pn -``` - -The portion before the semicolon represents the permissions map, which maps each `xi` to its primary permission `pxi`, while the portion after the semicolon represents the permissions stack, containing permissions `y1:p1` through `yn:pn` in sequence. The final permissions `yn:pn` is the top of the stack. We often write `PS` for a permission set. - -The general form of permission implication is the judgment - -``` -PS |- (z1_1, ..., z1_k1 . PS1) \/ ... \/ (zn_1, ..., zn_kn . PSn) -``` - -which says that, starting with permission set `PS` on the left-hand side of the turnstyle `|-`, we can prove one of the permission sets `PSi` on the right-hand side. Each disjunctive case could introduce 0 or more existential variables `zi_1, ..., zi_ki`, which can be used in the corresponding permission set `PSi`. We often omit the permissions map and/or the existential variables when they are not necessary; e.g., we write `PS1 |- PS2` instead of `PS1 |- ( [] . PS2)`. We also tend to omit the permissions map from implications, as permissions maps almost never change; thus, e.g., we might write `x:p |- y:q` instead of `x1 -> px1 * ... * xm -> pxm; x:p |- x1 -> px1 * ... * xm -> pxm; y:q`. - -Permission implication in Heapster is actually a sort of "partial implication". The above-displayed implicaiton judgment in fact says that, if we hvae permission set `PS`, we can _try_ to get one of the permission sets `PSi`, though we can't control which one we get, and we might fail. What this failure means exactly is a little hard to define without going into the details of the translation to SAW core / Coq and relationship between the resulting term and the original program. As one way to understand what failure means here, consider that each permission set `PS` actually describes a set of possible states, one for each substitution of values for all the free variables in `PS`. For some of these states, we can exchange the permissions in `PS` for the permissions in one of the `PSi`, though in some of those states, trying to do this leads to undefined behavior, or at least behavior we are not going to reason about. Another way to think about Heapster implication is to always add an extra disjunction `\/ dunno` to each right-hand side, so an implication `PS |- PS1 \/ ... \/ PSn` becomes `PS |- PS1 \/ ... \/ PSn \/ dunno`, meaning that from permissions `PS` we either can get one of the `PSi` or we get a result that says that we have to give up on modeling the current execution of the program. At a slightly more technical level, failure means means that the translation of a failure is just the `errorM` computation, which, again, doesn't mean that the original computation actually has an error, just that we don't know how to reason about it. Either way, we will simply say "prove" or "implies" below instead of something like "partially prove in some states". - -Permission implications are built up from two rules, the identity rule and the step rule. The identity rule is just a proof that `PS |- PS`. The step rule looks like this: - -``` -PS |-(1) (zs1.PS1) \/ ... \/ (zsn.PSn) -PS1 |- (zs1_1.PS1_1) \/ ... \/ (zs1_k1.PS1_k1) -... -PSm |- (zsm_1.PSm_1) \/ ... \/ (zsm_km.PSm_km) ------------------------------------------------------ -PS |- (zs1_1.PS1_1) \/ ... \/ (zs1_k1.PS1_k1) \/ ... \/ (zsm_1.PSm_1) \/ ... \/ (zsm_km.PSm_km) -``` - -Intuitively, this says that we can start with an implication and then apply further implications to each of the output permission sets of the original implication, yielding a bigger implication of all of the disjuncts returned by all of the further implications. The notation `|-(1)` denotes a single step of implication, which is built using one of the single-step rules that we describe below. Intuitively, this means that a permission implication can be viewed as a tree, whose leaves are identity rules and whose internal nodes are step rules whose shapes are defined by the single step `|-(1)` implication. - -Permission implications are represented in Haskell by the type `PermImpl r ps`. The type variable `ps` is a Haskell datakind that specifies a sequence of Crucible types for the variables and permissions on the stack at the beginning of the proof. For example, the representation of an implication `x1:p1 * ... * xn:pn |- PS1 \/ ... \/ PSn` will have type `PermImpl r (RNil :> t1 :> ... :> tn)` in Haskell, where each `xi` has Crucible type `ti` and each `pi` has the corresponding Crucible type `ValuePermType ti` (which is the type of a permission that applies to an element of type `ti`). The datakind `RNil` is the empty sequence. (The "R" stands for "right-associated list", because the cons operator `:>` adds new list elements on the right instead of the left; this datakind is defined in the Hobbits library, but is identical to the one defined in Data.Parameterized.Ctx.) - -In addition to describing the form of an implication, the `PermImpl` representation in Haskell also contains a "result" for each output permission set. That is, permission implications are a form of tree, as described above, and the `PermImpl` type stores results `r1, ..., rn` at each leaf `PS1, ..., PSn` in an implication of `PS |- PS1 \/ ... \/ PSn`. The result type is given by the `r` argument to `PermImpl`, and this type is parameterized by the datakind corresponding to the types of the permissions on the stack at that point. That is, a permission implication `PS |- PS1 \/ ... \/ PSn` will contain elements of type `r ps1` through `r psn`, assuming that each `psi` is the Haskell datakind that represents the stack for each `PSi`. Intuitively, the result does something with the permissions `PSi`. The most common example of this is in the typed representation of functions used in TypedCrucible.hs, where a function can contain a permission implication, using the `TypedImplStmt` constructor, to coerce the permissions it currently holds to some form that is needed to perform an operation. For instance, a `load` instruction requires the permissions currently held by a program to be coerced to a `ptr` permission. Whenever an implication `PS |- PS1 \/ ... \/ PSn` occurs in a typed Crucible representation, the remaining instructions must be type-checked relative to each of the permission sets `PSi`. This is represented by having the `PermImpl` representation contain one copy of the remaining instructions for each output `PSi`, type-checked relative to that permission set. - -The one-step implication rules defined by the `|-(1)` judgment are defined by the `PermImpl1 ps_in ps_outs` type, which represents a rule with input stack described by datakind `ps_in` and 0 or more disjunctive output stacks given by `ps_outs`, which is a list of 0 or more disjuncts that bind 0 or more existential variables and leave 0 or more types on the stack. (See the documentation of `PermImpl1` for more details.) These include the following rules (along with a few more that we do not discuss here): - -| Rule name | Rule description | Rule implication | -----------|-------------|-----------------| -| `Impl1_Fail` | Failure of implication | `ps \|-(1) empty` (where `empty` is 0 disjuncts) | -| `Impl1_Catch` | Try one implication and then a second if the first fails | `ps \|-(1) ps \/ ps` | -| `Impl1_Push` | Push the primary permission for `x` onto the stack | `..., x -> p; ps \|-(1) ..., x -> true; ps * x:p` | -| `Impl1_Pop` | Pop the top of the stack back to the primary permission for `x` | `..., x -> true; ps * x:p \|-(1) ..., x -> p; ps` | -| `Impl1_ElimOr` | Eliminate a disjunction on the top of the stack | `ps * x:(p1 or p2) \|-(1) (ps * x:p1) \/ (ps * x:p2)` | -| `Impl1_ElimExists` | Eliminate an existential on the top of the stack | `ps * x:(exists z.p) \|-(1) (z. ps * x:p)` | -| `Impl1_Simpl` | Apply a simple implication of the form `ps1 \|- ps2` | `ps * ps1 \|-(1) ps * ps2` | -| `Impl1_ElimLLVMFieldContents` | Extract the contents of an LLVM pointer permission | `ps * x:ptr((rw,off) -> p) \|-(1) y. ps * x:ptr((rw,off) -> eq(y)) * y:p` | - -[comment]: <> (FIXME: explain the above rules!) - -The most common implication rule is `Impl1_Simpl`, which applies a "simple" implication rule that exactly only one disjunctive output permission and binds no variables. The simple implication rules are described by the type `SimplImpl ps_in ps_out`. A rule of this type assumes that permissions `ps_in` are on the top of the stack, though there can be more permissions below these on the stack. It then consumes `ps_in`, replacing them with permissions `ps_out`. (As above, the `ps_in` and `ps_out` type arguments in Haskell are actually datakinds capturing the types of the input and output permissions of the rule.) These include too many rules to list here, so we only describe enough of them to give a flavor of what they do. - -Some of the simple implication rules are structural. These include the following: - -| Rule name | Rule description | Rule implication | -----------|-------------|-----------------| -| `SImpl_Drop` | Drop a permission | `x:p \|- .` | -| `SImpl_Copy` | Copy any permission that is copyable, i.e., satisfies `permIsCopyable` | `x:p \|- x:p * x:p` | -| `SImpl_Swap` | Swap the top two permissions on the stack | `x:p1 * y:p2 \|- y:p2 * x:p1` | -| `SImpl_MoveUp` | Move a permission towards the top of the stack | `x:p * ps1 * ps2 \|- ps1 * x:p * ps2` | -| `SImpl_MoveDown` | Move a permission away from the top of the stack | `ps1 * x:p * ps2 \|- x:p * ps1 * ps2` | -| `SImpl_IntroConj` | Prove an empty conjunction (which is the same as `true`) | `. \|- x:true` | -| `SImpl_ExtractConj` | Extract the `i`th conjunct of a conjunction | `x:(p0 * ... * p(n-1)) \|- x:pi * x:(p0 * ... p(i-1) * p(i+1) ... * p(n-1))` | -| `SImpl_CopyConj` | Copy the `i`th conjunct of a conjunction, assuming it is copyable | `x:(p0 * ... * p (n-1)) \|- x:pi * x:(p0 * ... * p(n-1))` | -| `SImpl_InsertConj` | Insert a permission into a conjunction | `x:p * x:(p0 * ... * p(n-1)) \|- x:(p0 * ... * p(i-1) * p * pi * ... * p(n-1))` | -| `SImpl_AppendConjs` | Combine the top two conjunctions on the stack | `x:(p1 * ... * pi) * x:(pi+1 * ... * pn) \|- x:(p1 * ... * pn)` | -| `SImpl_SplitConjs` | Split the conjunctive permissions on the top of the stack in two | `x:(p1 * ... * pn) \|- x:(p1 * ... * pi) * x:(pi+1 * ... * pn)` | - - -The elimination rules for disjunctions and existentials are `PermImpl1`s, because the former has multiple disjuncts and the latter introduces local variables, but their introduction rules are simple implications, as are both the introduction and elimination rules for named permissions: - -| Rule name | Rule description | Rule implication | -----------|-------------|-----------------| -| `SImpl_IntroOrL` | Prove a disjunctive permission from its left disjunct | `x:p1 \|- x:(p1 or p2)` | -| `SImpl_IntroOrR` | Prove a disjunctive permission from its right disjunct | `x:p2 \|- x:(p1 or p2)` | -| `SImpl_IntroExists` | Prove an existential permission from a substitution instance | `x:[e/z]p \|- x:(exists z.p)` | -| `SImpl_FoldNamed` | Prove a named permission from its unfolding | `x:(unfold P args) \|- x:P` | -| `SImpl_UnfoldNamed` | Eliminate a named permission by unfolding it | `x:P \|- x:(unfold P args)` | - - -Equality permissions are manipulated with the following simple implication rules: - -| Rule name | Rule description | Rule implication | -----------|-------------|-----------------| -| `SImpl_IntroEqRefl` | Prove any `x` equals itself | `. \|- x:eq(x)` | -| `SImpl_InvertEq` | Prove if `x` equals `y` then `y` equals `x` | `x:eq(y) \|- y:eq(x)` | -| `SImpl_InvTransEq` | Prove that if `x` and `y` equal the same `e` then they equal each other | `x:eq(e) * y:eq(e) \|- x:eq(y)` | -| `SImpl_LLVMWordEq` | If `y` equals `e` then `llvmword(y)` equals `llvmword(e)` | `x:eq(llvmword(y)) * y:eq(e) \|- x:eq(llvmword(e))` | -| `SImpl_LLVMOffsetZeroEq` | Offsetting an LLVM value by `0` preserves equality | `. \|- x:eq(x &+ 0)` | -| `SImpl_InvertLLVMOffsetEq` | Subtract an offset from both sides of an LLVM value equality | `x:eq(y+off) \|- y:eq(x-off)` | -| `SImpl_Cast` | Cast the variable of a permission using an equality | `x:eq(y) * y:p \|- x:p` | -| `SImpl_CastPerm` | Cast a permission `p` to `p'`, assuming that `x1=e1`, ..., `xn=en` imply that `p=p'` | `x1:eq(e1) * ... * xn:eq(en) * x:p \|- x:p'` | - - -[comment]: <> (FIXME: Implementation of the rules: `simplImplIn` and `simplImplOut`, `applyImpl1`: these all check for correct perms) - -[comment]: <> (FIXME: Explain overall pattern of the simplimpl rules: intro vs elim rules for most constructs) - - -### The Implication Prover Algorithm - -The goal of the implication prover is to search for a permission implication proof using the implication rules discussed above. The implication prover is not complete, however, meaning that there are proofs that can be made with the implication rules that the implication prover will not find. To date, work on the implication prover has focused on heuristics to make it work in practice on code that comes up in practice. - -#### The Implication Prover Monad - -The implication prover runs in the `ImplM` monad, whose type parameters are as follows: - -``` -ImplM vars s r ps_out ps_in a -``` - -An element of this type is an implication prover computation with return type `a`. The type variable `vars` lists the types of the existential variables, or _evars_, in scope. These represent "holes" in the permissions we are trying to prove. The type variables `s` and `r` describe the calling context of this implication computation at the top level: `s` describes the monadic state maintained by this calling context, while `r` describes the top-level result type required by this context. These types are left abstract in all of the implication prover. - -The type variables `ps_in` and `ps_out` describe the permission stack on the beginning and end of the computation. The existence of these two variables make `ImplM` a _generalized_ monad instead of just a standard monad, which means that these types can vary throughout an implication computation. The bind for `ImplM` is written `>>>=` instead of `>>=`, and has type - -``` -(>>>=) :: ImplM vars s r ps' ps_in a -> (a -> ImplM vars s r ps_out ps' b) -> - ImplM vars s r ps_out ps_in b -``` - -That is, the bind `m >>>= f` first runs `m`, which changes the permissions stack from `ps_in` to `ps'`, and then it passes the output of `m` to `f`, which changes the permissions stack from `ps'` to `s_out`, so the overall computation changes the permissions stack from `ps_in` to `ps_out`. As a more concrete example, the computation for pushing a permission onto the top of the stack is declared as - -``` -implPushM :: HasCallStack => NuMatchingAny1 r => ExprVar a -> ValuePerm a -> - ImplM vars s r (ps :> a) ps () -``` - -meaning that `implPushM` takes in a variable `x` and a permission `p` and returns a computation that starts in any permission stack `ps` and pushes permission `x:p` of type `a` onto the top of the stack. - -If the permission stack does not change, meaning that `ps_in` equals `ps_out`, then `ImplM` forms a monad. For instance, the function - -``` -partialSubstForceM :: (NuMatchingAny1 r, PermPretty a, - Substable PartialSubst a Maybe) => - Mb vars a -> String -> ImplM vars s r ps ps a -``` - -takes the current partial substitution for the evars and attempts to apply it to a value in a name-binding for those evars, raising an error if some evar that has not yet been instantiated is used in the value. (The "force" means to force the substitution to be defined or fail.) This function does not change the permission stack, and is in fact written in `do` notation in the code. - -The `ImplM` monad is defined as a generalized state-continuation monad. This construct is defined in `GenMonad.hs`, but we will not discuss it in too much detail here. The state that is maintained is given by the `ImplState` type, which contains information such as the current permission set, the types of all univeral and existential variables in scope, and the current instantiations of all the evars. The input and output types of the continuation portion of `ImplM` are both `PermImpl`, meaning each `ImplM` computation builds up an implication. The fact that `ImplM` is a continuation monad is only used in the `implApplyImpl1` function, which applies an `Impl1` rule by shifting the current continuation and re-applying it to build the sub-`PermImpl`s passed to that rule as an `MbPermImpls`. This means that rules with multiple disjunctive outputs, like or elimination and the catch rule, cause `ImplM` to fork its execution, running any subsequent computation once in each disjunctive branch. Thus, for performance reasons, it is helpful to reduce this forking as much as possible. - - -#### Needed and Determined Variables - -One difficulty in doing proof search which must be addressed by the implication prover is that existential variables mean we do not have most general types. (There are other ways in which Heapster does not have most general types, but this is a more aggregious one.) For instance, there are two distinct ways to prove - -``` -x:ptr((R,0) |-> true) * x:ptr((R,8) |-> true) |- exists off:bv 64. x:ptr((R,off) |-> true) -``` - -by instantiating `off` to either 0 or 8. The difficulty is that if we choose the wrong value for `off` we might have to backtrack, potentially leading to an exponential search. The same problem occurs for function permissions with ghost variables, as ghost variables become existential variables that must be instantiated at call sites. Thus, for instance, Heapster cannot handle a function with permissions like - -``` -(off:bv 64). arg0:ptr((R,off) |-> true) -o empty -``` - -because it will not know how to instantiate `off` at the call site. Currently, this shows up as a type-checking error when such a function is called, but we could consider raising an error where such a function is defined. If you think about it, though, a function type like this does not really make any sense. How could a function take in a pointer to something where it doesn't know the offset of that pointer? - -If, however, there is some other permission that _determines_ the offset, then this problem is resolved. Consider, for instance, the following function type: - -``` -(off:bv 64). arg0:ptr((R,off) |-> true), arg1:eq(llvmword(off)) -o empty -``` - -This describes a function whose second argument says what the offset is for the first. Unlike the previous example, Heapster can handle this function type, because it will prove the equality permission on `arg1` first, and this proof will determine the value of `off` to be used for the permission on `arg0`. This function type also makes a lot more sense operationally, because now the function can know what the offset is. The more common version of this situation is passing the length of an array, using a type like this: - -``` -(len:bv 64). arg0:array(W,0,), arg1:eq(llvmword(len)) -o empty -``` - -A similar pattern can occur inside data structures. A common pattern in C is to have a `struct` with a variable-length array at the end, whose length is determined by one of the fields, like this: - -``` -struct foo { - ...; - int64_t len; - char data[]; -} -``` +* RustTypes.hs: This file translates Rust types into Heapster types, using a process described [here](../../../../doc/RustTrans.md). -Rust slices are similar. A struct like this can be described by the Heapster shape - -``` -...; exsh len:bv 64.(fieldsh(eq(llvmword len));arraysh())) -``` - -This shape can be proved by the Heapster implication prover because the existential variable `len` in the shape is determined by the equality permission in the `len` field in the struct. If the struct did not have this field, Heapster would not be able to prove permissions with this shape. Again, such a shape does not really make sense, as the program would never know how long the `data` field is. - - -The Heapster implication prover addresses the problem of existential variables leading to non-unique types by requiring that all existential variables that could lead to this sort of problem in a permission `p` are assigned a uniquely determined value before it attempts to satisfy permission `p`. These variables are called the _needed_ variables of `p`, defined by the `neededVars` function in Permissions.hs. The needed variables include any free variables in the offsets and lengths of pointer, array, and block permissions, as well as any free variables of the more complicated permissions like lifetime ownership permissions. For equality permissions `eq(e)`, the free variables of `e` are not needed if `e` is a _determining_ expression, discussed below. In our example above, `off` is a needed variable on the right-hand side, so the implication prover will not prove this implication but will instead raise a type-checking error (with the `Impl1_Fail` rule described above). - -The only way to prove a permission with needed variables is if there is some other permission which is proved first that _determines_ the value of that variable. Intuitively, the idea is that a permission `p` determines an existential variable `x` if there is only one possible value of `x` for which `p` can possibly be proved. The canonical example of determination is the permission `eq(x)`: the only possible way to prove an `eq(x)` permission is to set `x` to the value that has this permission. If we are proving `y:eq(x)`, then `x` has to be set to `y`, while if we are proving a pointer permission `y:ptr((rw,off) |-> eq(x))`, `x` has to be set to the value pointed to by `y` at offset `off`. Note that, in this latter case, the implication prover will first prove some permission of the form `y:ptr((rw,off) |-> p)` and will then use the `Impl1_ElimLLVMFieldContents` rule to bind a local variable `z` for the value pointed to by `y` at offset `off`, so `x` will be set to this local variable `z`. In order to prove a pointer permission, however, the free variables in `off` (if there are any) must already be determined by some other permission, because these are needed variables of the pointer permission. Thus determined variables have a dependency structure, where some variables can only be determined if other variables are determined first. Further, a variable can not be determined by an equality inside an arbitrary permission. For instance, `eq(x) or p` does not determine `x`, because the proof may not take the left-hand branch of the disjunct. - -More generally, determined variables are defined by the `determinedVars` function. This function uses the helper function `isDeterminingExpr` to define whether an expression `e` used in an equality permission determines its variables. The following expression forms are determining: -* `x` -* `llvmword e` if `e` is determining -* `N*x + K` for constants `N` and `K` -* The permission `eq(e)` as an expression if `e` is determining -* `x &+ off` if `off` is determining -* Any expression with no free variables - -The `determinedVars` function is then defined as follows on permission `p`: - -| Permission `p` | Determined Variables | -|-------------|----------------------| -| `eq(e)` | The free variables of `e` if `e` is a determining expression, otherwise `[]` | -| `p1 * ... * pn` | The determined variables of the `pi` | -| `P` | The free variables of each determining expression in `args` | -| `[l]ptr((rw,off) |-> p)` | The determined variables of `l`, `rw`, and `p`, if the variables in `off` are determined | -| `[l]array(rw,off, ImplM vars s r (ps_in :++: ps) ps_in () -``` - -This function attempts to prove `n` permisisons `x1:p1, ..., xn:pn`, adding those permissions to the top of the permissions stack. These permissions are inside of a binding for the existential variables specified by `vars`, which represent "holes" or unknown expressions that will be solved by building the proof. As an example, the type-checker for the pointer read instruction calls the implication prover with the existentially quantified permission - -``` -(rw,l,z). [l]ptr((rw,0) |-> eq(z)) -``` - -expressing that it requires a pointer permission at offset 0 with any lifetime `l`, any read/write modality `rw`, that points to any value `z`. - -There are a number of wrapper functions that call `proveVarsImplAppend`, including: - -* `proveVarsImpl`, which assumes the input permission stack is empty; -* `proveVarImpl`, which proves one permission; and -* `proveVarsImplVarEVars`, which is like `proveVarsImpl` but where all existential variables are instantiated with fresh variables. - -The top-level implication prover algorithm is then implemented as a descending sequence of "levels", each of is implemented as a function that performs some particular function and then calls the next level: - -| Function Name | Purpose | ---------------|----------| -| `proveVarsImplAppend` | Try to prove the required permissions, and, if that failos, non-deterministically end some lifetimes that could help in the proof | -| `proveVarsImplAppendInt` | Repeatedly call `findProvablePerm` to find the permission on the right that is most likely to be provable and then try to prove that permission | -| `proveExVarImpl` | Handle the case of a right-hand permission `x:p` where `x` itself is an evar by instantiating `x`, if possible | -| `proveVarImplInt` | Wrapper function that pushes the primary permissions for `x` onto the top of the stack, performs debug tracing, calls `proveVarImplH`, and then checks that the proved permission is correct | - - -#### Proving a Permission - -The main logic for proving a permission is in the function `proveVarImplH`. (The implication prover uses the convention of using "`H`" as a suffix for helper functions.) As with many functions in the implication prover, this function takes in: a variable `x` that we are trying to prove a permission on; a permission `p` for `x` which is currently on top of the stack; and a permission `mb_p` inside a context of evars that we are trying to prove for `x`. (The prefix "`mb`" refers to "multi-binding", a binding of 0 or more evars.) The function then works by pattern-matching on `p` (the left-hand side) and `mb_p` (the right-hand side), using the following cases, some of which call out to helper functions described below: - -| Left-hand side | Right-hand side | Algorithmic steps taken | -|------------|--------------|--------------------| -| `p` | `true` | Pop `p` and Introduce a vacuous proof of `true` | -| `p` | `eq(e)` | Call `proveVarEq` to prove the equality | -| `p1 or p2` | `mb_p` | Eliminate the disjunction and recurse | -| `exists z. p` | `mb_p` | Eliminate the existential and recurse | -| `eq(y)` | `mb_p` | Prove `y:mb_p` and then cast the proof to `x:mb_p` | -| `eq(y &+ off)` | `mb_p` | Prove `y:(offsetPerm mb_p off)` and then case the proof to `x:mb_p` | -| `p` | `mb_p1 or mb_p2` | Nondeterminsitically try to prove either `mb_p1` or `mb_p2` | -| `p` | `exists z. mb_p` | Add a new evar for `z`, prove `x:mb_p`, and then use the value determined for `x` to introduce an existential permission | -| `P` | `mb_p` | Use the more specific rules below for named permissions | -| `p1 * ... * pi-1 * P * pi+1 * ... * pn` | `mb_p` | Use the more specific rules below for named permissions | -| `p` | `P` | Use the more specific rules below for named permissions | -| `eq(llvmword e)` | `p1 * ... * pn` | Fail, because we cannot prove any non-equality permissions for words, and the equality permissions were matched by an earlier case | -| `eq(struct(e1,...,en))` | `mb_p` | Eliminate `eq(struct(e1,...,en))` to a `struct(eq(e1),...,eq(en))` permission with equalities for each field | -| `eq(constant f)` | `(gs) ps_in -o ps_out` | Use an assumption on known function `f` | -| `p1 * ... * pn` | `mb_p1 * ... * mb_pn` | Call `proveVarConjImpl` to prove a conjunction implies a conjunction | -| `p` | `(X). X` | For existential permission variable `X`, set `X:=p` | -| `X` | `X` | For universal permission variable `X`, prove `X -o X` by reflexivity | -| `_` | `_` | In all other cases, fail | - - -#### Proving Named Permissions - -Named permissions are of the form `P` for some permission name `P`. Each named permission represents some more complicated collection of permissions, that can depend on the argument expressions `args`. Permission names come in three sorts: - -* _Defined names_ are essentially abbreviations, where `P` unfolds to some permission `p` that does not contain `P` itself (unless `P` occurs `args`); - -* _Recursive names_ `P` are similar to defined names but where `P` unfolds to a permission that can contain `P`; and - -* _Opaque names_ are permissions which are too complicated to represent in Heapster, so Heapster just represents them with names. - -The "best" way to prove a named permission `P` is by reflexivity, i.e., to find an instance of `P` that is already in the current permissions set. The implication rules do allow some amount of weakening the arguments, so technically this is a search for `P` for some argument list `args'` that can be coerced to `args`. For opaque names, this is the only way to prove `P`. For defined names, the other option is to just unfold `P` to its definition, prove that permission, and then fold the result to `P`. Similarly, recursive names can also be unfolded to prove them. Dually, if there is a permission `P` with a defined or recursive name on the left, meaning it is already held in the current permission set, the implication prover will unfold this permission if it gets stuck trying to prove its goal any other way. This logic is implemented by the `implUnfoldOrFail` function, which is called in a number of places in the implication prover where it will otherwise fail. - -The one wrinkle is that unfolding recursive names can lead to non-termination. This can happen if we have an assumption `P1` on the left and we are trying to prove `P2` on the right where both `P1` and `P2` are recursive names. If we proceed by unfolding both `P1` and `P2`, then, because these unfoldings can each contain `P1` and `P2` again, we can end up back at the same proof goal, of having an assumption `P1` on the left and trying to prove `P2`. To prevent this infinite regress, the implication prover is restricted so that it will not unfold names on both the left and right sides in the same proof. This is done by maintainining a flag called `implStateRecRecurseFlag` that tracks whether there has been an unfolding of a recursive name on one side or the other. Whenever the implication prover has `P1` on the left and `P2` on the right for recursive names `P1` and `P2`, it then non-deterministically (using the `Impl1_Catch` rule to backtrack) chooses one side to unfold, and it proceeds from there. This handles the possibility that one of `P1` or `P2` contains the other as a sub-permission. - -In more detail, here are the cases of `proveVarImplH` that handle recursive permissions: - -| Left-hand side | Right-hand side | Algorithmic steps taken | -|------------|--------------|--------------------| -| `P` | `P` | For reachabilitiy permission `P`, nondeterministically prove the RHS by either reflexivity, meaning `x:eq(mb_e)`, or transitivity, meaning `e:P` | -| `P` | `P` | For non-reachabilitiy named permission `P`, prove `args` _weakens to_ `mb_args`, where write modalities weaken to read, bigger lifetimes weaken to smaller ones, and otherwise arguments weaken to themselves | -| `p1 * ... * pi-1 * P * pi+1 * ... pn` | `P` | Similar to above | -| `p` | `P` | If `P` is a _defined_ (i.e., non-recursive) name, unfold `P` to its definition and recurse | -| `P` | `mb_p` | If `P` is a defined name, unfold `P` to its definition and recurse | -| `p1 * ... * pi-1 * P * pi+1 * ... pn` | `mb_p` | If `P` is defined, unfold `P` to its definition and recurse | -| `P1` | `P2` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose one side to unfold | -| `p1 * ... * pi-1 * P1 * pi+1 * ... pn` | `P2` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose one side to unfold | -| `p` | `P` | If `P` is recursive, unfold `P` and recurse | -| `P` | `mb_p` | If `P1` and `P2` are both recursively-defined, nondeterminstically choose - - -#### Proving Equalities and Equality Permissions - -Equality permissions are proved by `proveVarEq`, which takes a variable `x` and an expresson `mb_e` in a binding of the existential variables, and proves `x:eq(e)` for some instantiation `e` of the variables in `mb_e`. This function pushes a reflexive proof that `x:eq(x)`, calls `proveEq` to build an equality proof that `x=e`, and uses the equality proof with the `SImpl_CastPerm` rule to cast the proof of `x:eq(x)` on top of the stack to `x:eq(e)`. The meat of `proveVarEq` is thus in `proveEq`, which attempts to build equality proofs. The `proveEq` function is also called in other parts of the implication prover, e.g., to coerce the modalities of field, array, and block permissions. - -An equality proof in Heapster is a transitive sequence of equality proof steps `e=e',e'=e'',...`. Each step is a sequence of equality permissions `x1:eq(e1),...,xn:eq(en)`, where each equality `xi:eq(ei)` is oriented either left-to-right as `xi=ei` or right-to-left as `ei=xi`, along with a function `f` on `n` expressions. This represents the equality `f(left1,...,leftn)=f(right1,...,rightn)`, where `lefti` and `righti` are the left- and right-hand sides of the `i`th oriented version `xi=ei` or `ei=xi` of the permission `xi:eq(ei)`. Equality steps are represented by the Haskell type `EqProofStep ps a`, where `ps` is a list of the types of the variables `x1,...,xn` and `a` is the Haskell type of the objects being proved equal. (Equality proofs can be used not just on expressions, but at other types as well.) Entire equality proofs are represented by the type `EqProof ps a`, while the type `SomeEqProof a` is an equality proof where the permissions needed to prove it are existentially quantified. - -[comment]: <> (FIXME HERE: describe `proveEq` and `proveEqH`) - - -#### Proving Conjuncts of Permissions - -Conjuncts `p1 * ... * pn` are proved by `proveVarConjImpl`, which repeatedly picks the "best" permission on the right to prove and calls `proveVarAtomicImpl` to prove it. Finding the "best" permission prioritizes defined permissions first, followed by recursive permissions, as this fits with the named permissions algorithm described above, followed by finding a permission whose needed variables have all been determined. - -The cases for `proveVarAtomicImpl` are as follows: - -| Permission to prove | Algorithmic steps taken | -|----------------|--------------------| -| `[l]ptr((rw,off) |-> p)` | Call `proveVarLLVMField` | -| `[l]array(rw,off, p)`, the `proveVarLLVMField` function starts by first calling `implGetLLVMPermForOffset` to find a permission on the left that contains `off`. If this permission is a `memblock` permission, it is repeatedly eliminated, using the helper function `implElimLLVMBlock`, until an array or field permission is obtained. This permission is then passed to `proveVarLLVMFieldH`, which calls `proveVarLLVMFieldH2`, which dispatches based on the form of the permission. We call this the left-hand permission in this discussion, since `implGetLLVMPermForOffset` puts it on the top of the stack, i.e., the left of the implication. - -The main case `proveVarLLVMFieldH2` is when the left-hand permission is a field permission -`[l']ptr((rw',off) |-> p')` of the same size as the required one. In this case, `proveVarLLVMFieldH2` performs the following steps: -* Eliminate the contents of the field permission to get a permission of the form `[l']ptr((rw',off) |-> eq(y))` for some variable `y`; -* Prove `y:p` with a recursive call to `proveVarImplInt`; -* Use the `SImpl_IntroLLVMFieldContents` to combine the `y:p` permission into the left-hand permission to get `[l']ptr((rw',off) |-> p)`; -* Coerce the lifetime `l'` to `l` by calling `proveVarLifetimeFunctor`, which splits or ends lifetimes as needed; -* Coerce `rw'` to `rw` by calling `equalizeRWs`, which either proves the two are equal using `proveEq` and casting or weakens a write modality to a read modality; and -* Duplicate and recombine the pointer permission if it is copyable. - -If the left-hand permission is a pointer permission that is bigger than required, split the left-hand permission by calling `implLLVMFieldSplit`, recombine the part that is not required, and recursively call `proveVarLLVMFieldH` with the remaining left-hand permission. - -If the left-hand permission is a pointer permission that is smaller than required: -* Recursively call `proveVarLLVMFieldH` with the same left-hand permission to prove a pointer permission `[l]ptr((rw,off) |-> eq(y))` of the same size, i.e., with existential variable `y:llvmptr (8*sz)` where `sz` is the size of the left-hand permission in bytes; -* Prove `[l]ptr((rw,off+sz) |-> eq(z))` for existential variables `z` of the remaining size; -* Call `implLLVMFieldConcat` to concatenate these two field permissions; and -* Call `proveVarLLVMFieldH` with the resulting permission of the correct size as the left-hand permission. - -If the left-hand permission is an array permission where the required permission lines up with one of the cells of the array, borrow or copy (depending on whether the array is copyable) the corresponding array cell and recursively call `proveVarLLVMFieldH` with the cell permission that was borrowed. - -If the left-hand permission is an array permission where the required permission covers multiple cells of the array, borrow or copy those cells (depending on whether the array is copyable) as a single array permission, coerce the resulting cells to a field using the `SImpl_LLVMArrayToField` rule, and pass the resulting permission as the left-hand permission of a recursive call to `proveVarLLVMFieldH`. - -In all other cases, `proveVarLLVMFieldH2` fails. - - -#### Proving Array Permissions - - - -#### Proving Block Permissions - - -## Crucible Type-checker - -FIXME - -## SAW Translator - -FIXME +[comment]: <> (FIXME: describe the other files) From f7f2a842f497ee58d33fab05807a9b40165e2807 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Mon, 6 Jun 2022 13:50:00 -0700 Subject: [PATCH 10/20] outlined a section on overall Rust verification --- heapster-saw/doc/Rust.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 heapster-saw/doc/Rust.md diff --git a/heapster-saw/doc/Rust.md b/heapster-saw/doc/Rust.md new file mode 100644 index 0000000000..d1cfc1a667 --- /dev/null +++ b/heapster-saw/doc/Rust.md @@ -0,0 +1,11 @@ + +# Type-checking Rust with Heapster + +FIXME: write a simple tutorial +- Defining types +- Symbols and name-mangling +- Assuming standard library functions +- Assuming low-level primitives like `memcpy`; refer to the + [Rust Translation](RustTrans.md) for more detail about the relationship between + Rust and Heapster types +- Type-checking From a0dc69188ea1dcfe549224a01cf21ad8c05777f7 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Tue, 7 Jun 2022 15:45:52 -0700 Subject: [PATCH 11/20] explained argument layout in RustTrans.md --- heapster-saw/doc/Permissions.md | 4 +- heapster-saw/doc/RustTrans.md | 105 +++++++++++++++++++++++++++++--- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/heapster-saw/doc/Permissions.md b/heapster-saw/doc/Permissions.md index 5905203875..3409b24b7c 100644 --- a/heapster-saw/doc/Permissions.md +++ b/heapster-saw/doc/Permissions.md @@ -16,10 +16,10 @@ In this document, we use the following metavariables to refer to different sorts -Permission types +Value Types ================ -A permission type includes regular crucible types as well as heapster-specific types +The Heapster value types include the regular crucible types as well as heapster-specific types: | **Permission Types `a`** | **Description** | | :---: | :--- | diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md index 12b2ac0cba..7fd55b662a 100644 --- a/heapster-saw/doc/RustTrans.md +++ b/heapster-saw/doc/RustTrans.md @@ -10,14 +10,15 @@ describe the shape and structure of blocks of memory. Each Rust variable designates a block of memory where the value of the variable is stored. The type of the variable then describes the shape of that memory. Thus, Rust types are translated to Heapster shape expressions, which Heapster uses to describe -memory. Heapster shapes are documented [here](Permissions.md). The basic -conversion from Rust is described in the following table, though Rust implements -a number of layout optimizations, described below, that alter this translation. -In this table, we write `[| T |]` for the translation of Rust type `T` to a -Heapster shape, and we write `len(sh)` for the Heapster expression giving the -length of Heapster shape `sh`, when this is defined. The notation `[\| Name \|]` -denotes the translation of the type definition associated with type name `Name`, -as defined in the next section. +memory. Heapster shapes are documented [here](Permissions.md). + +The basic conversion from Rust is described in the following table, though Rust +implements a number of layout optimizations, described below, that alter this +translation. In this table, we write `[| T |]` for the translation of Rust type +`T` to a Heapster shape, and we write `len(sh)` for the Heapster expression +giving the length of Heapster shape `sh`, when this is defined. The notation +`[\| Name \|]` denotes the translation of the type definition associated with +type name `Name`, as defined in the next section. | Rust Type | Translation to a Heapster Shape | @@ -106,6 +107,94 @@ FIXME: Option-like types ## Translating Function Types +Rust function definitions are written like this: + +``` +fn foo <'a1,...,'am,X1,...,Xn> (x1 : T1, ..., xk : Tk) -> T { ... } +``` + +This defines `foo` as a function that is polymorphic over `m` lifetimes and `n` +types that takes `k` input arguments of types `T1` through `Tk` to an output +value of type `T`. In Heapster, we write the type of this function as: + +``` +<'a1,...,'am,X1,...,Xn> fn (x1 : T1, ..., xk : Tk) -> T +``` + +where the variable names are optional. For technical reasons, Rust does not +actually allow polymorphic function types, but only supports non-polymorphic +functions types, starting with the `fn` keyword, so this is a syntactic +extension supported by Heapster. + +- High-level translation to a Heapster function; make forward reference to + layout and to lifetime types + + +### Argument Layout + +Argument layout converts a shape, which describes the layout and associated +permissions of a memory block, to a permission on a sequence of register values, +if this is possible. In Heapster (as in the underlying Crucible type system), +sequences of values are called structs and a permission on a sequence of values +is called a struct permission. More specifically, argument layout is defined as +a partial function `Lyt(sh)` that maps a Heapster shape `sh` for a particular +function argument to a permission of type `perm(struct(tp1,...,tpn))` for some +value types (i.e., Crucible types) `tp1` through `tpn`. When the layout of the +type `T` of an argument is not defined --- e.g., if `T` is too big to fit in +registers or it is a slice or other dynamically-sized type that has no +well-defined size --- then the corresponding argument is represented as a +pointer to a block of memory with the shape defined by `T`. + +In order to define `Lyt(sh)`, we first define two helper operations on structure +permissions. Both of these are partial functions that take in two structure +permissions, possibly of different types, and return a structure permission with +some potentially different type. The first of these is the struct permission +append operator `p1 ++ p2`, which combines a struct permission `p1` of type +`perm(struct(tp1,...,tpm))` and `p2` of type `perm(struct(tp1',...,tpn'))` into +a permission of type `perm(struct(tp1,...,tpm,tp1',...,tpn'))` on the append of +structs with permissions `p1` and `p2`. This operation is defined as follows: + +| Permissions `p1` and `p2` to Append | Resulting Permission `p1++p2` | +| ------------------------ | --------------------- | +| `struct(p1,...,pn) ++ struct(q1,...,qm)` = | `struct(p1,...,pn,q1,...,qm)` | +| `(p1 or p2) ++ q` = | `(p1 ++ q) or (p2 ++ q)` | +| `p ++ (q1 or q2)` = | `(p ++ q1) or (p ++ q2)` | +| `(exists z. p) ++ q` = | `exists z. (p ++ q)` | +| `_ ++ _` = | Undefined otherwise | + +The second operation on structure permissions needed here is the disjucntion +operation `p1 \/ p2`. Intuitively, this operation takes the disjunction of the +two struct permissions `p1` and `p2` after first equalizing the number of +registers they refer to. More formally, this `p1 \/ p2` is defined as follows: + +* If there is a permission `p1' = p1 ++ struct(true,true,...,true)` of the same + type as `p2`, then `p1 \/ p2` is defined as the disjunction `p1' or p2`; + +* If there is a permission `p2' = p2 ++ struct(true,true,...,true)` of the same + type as `p1`, then `p1 \/ p2` is defined as the disjunction `p1 or p2'`; + +* Otherwise, `p1 \/ p2` is undefined. + +Using these operations, the layout function `Lyt(sh)` is defined as follows: + +| Heapster shape | Its layout as a struct permission | +|--------------|--------------------------| +| `Lyt(emptysh)` = | `struct()` | +| `Lyt(Name)` = | `Lyt(unfold(Name,args))` | +| `Lyt(fieldsh(p))` = | `struct(p)` | +| `Lyt(arraysh(_,_,_))` = | undefined | +| `Lyt(sh1 ; sh2)` = | `Lyt(sh1) ++ Lyt(sh2)` | +| `Lyt(sh1 orsh sh2)` = | `Lyt(sh1) \/ Lyt(sh2)` | +| `Lyt(exsh z. sh)` = | `exists z. Lyt(sh)` | +| `Lyt(falsesh)` = | `false` | + +FIXME: explain the definition + +### Lifetime Permissions + + + + FIXME: - explain layout (types that take more than two fields become pointers) - explain lifetimes From 4e94a3d18bbb5808ecfdd2d36324e272fc1a0d50 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Tue, 7 Jun 2022 17:19:19 -0700 Subject: [PATCH 12/20] defined the argument layout function Arg(sh) --- heapster-saw/doc/RustTrans.md | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md index 7fd55b662a..5166d638e8 100644 --- a/heapster-saw/doc/RustTrans.md +++ b/heapster-saw/doc/RustTrans.md @@ -167,6 +167,9 @@ operation `p1 \/ p2`. Intuitively, this operation takes the disjunction of the two struct permissions `p1` and `p2` after first equalizing the number of registers they refer to. More formally, this `p1 \/ p2` is defined as follows: +* If `p1=struct(p1')` and `p2=struct(p2')` where `p1'` and `p2'` have the same + type, then `p1 \/ p2=struct(p1' or p2')`; + * If there is a permission `p1' = p1 ++ struct(true,true,...,true)` of the same type as `p2`, then `p1 \/ p2` is defined as the disjunction `p1' or p2`; @@ -188,7 +191,26 @@ Using these operations, the layout function `Lyt(sh)` is defined as follows: | `Lyt(exsh z. sh)` = | `exists z. Lyt(sh)` | | `Lyt(falsesh)` = | `false` | -FIXME: explain the definition + +FIXME: explain the above definition + +FIXME: define the argument layout function `Arg(sh)` as a function from a shape +`sh` to a sequence of zero or more ghost variables and regular argument +variables plus permissions: + +* If `Lyt(sh)=struct(p1,...,pn)` for a sequence `p1,...,pn` of 0, 1, or 2 + permissions, then `Arg(sh)=arg1:p1,...,argn:pn`; + +* If `Lyt(sh)=p` for `p` of type `perm(struct(tp1,...,tpn))` for a sequence + `tp1,...,tpn` of 0, 1, or 2 types, then + `Arg(sh)=ghost:p,arg1:eq_proj(ghost,1),...,argn:eq_proj(ghost,n)`; + +* If `Lyt(sh)` is undefined but `len(sh)=ln`, then `Arg(sh)=arg:memblock(W,0,ln,sh)`; + +* Otherwise, `Arg(sh)` is undefined. + + + ### Lifetime Permissions From 9cf9562138c87698db8256094149753cf3f80998 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Wed, 8 Jun 2022 15:57:16 -0700 Subject: [PATCH 13/20] finished the mathematical definition of translating a Rust function type, with FIXMEs for the explanations --- heapster-saw/doc/RustTrans.md | 113 ++++++++++++++++++++++++++++++---- 1 file changed, 102 insertions(+), 11 deletions(-) diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md index 5166d638e8..cfdf7fbd89 100644 --- a/heapster-saw/doc/RustTrans.md +++ b/heapster-saw/doc/RustTrans.md @@ -110,15 +110,18 @@ FIXME: Option-like types Rust function definitions are written like this: ``` -fn foo <'a1,...,'am,X1,...,Xn> (x1 : T1, ..., xk : Tk) -> T { ... } +fn foo <'a1,...,'am> (x1 : T1, ..., xn : Tn) -> T { ... } ``` -This defines `foo` as a function that is polymorphic over `m` lifetimes and `n` -types that takes `k` input arguments of types `T1` through `Tk` to an output -value of type `T`. In Heapster, we write the type of this function as: +This defines `foo` as a function that is polymorphic over `m` lifetimes that +takes `n` input arguments of types `T1` through `Tn` to an output value of type +`T`. Note that Rust function types can in general be polymorphic over type +variables as well, but Rust compilation to LLVM always monomorphizes these +polymorphic function types, so Heapster, which runs on LLVM code, never sees +these polymorphic types. In Heapster, we write the type of this function as: ``` -<'a1,...,'am,X1,...,Xn> fn (x1 : T1, ..., xk : Tk) -> T +<'a1,...,'am> fn (x1 : T1, ..., xk : Tk) -> T ``` where the variable names are optional. For technical reasons, Rust does not @@ -126,8 +129,11 @@ actually allow polymorphic function types, but only supports non-polymorphic functions types, starting with the `fn` keyword, so this is a syntactic extension supported by Heapster. -- High-level translation to a Heapster function; make forward reference to - layout and to lifetime types + +FIXME: give some examples of how some simple Rust function types are translated +to Heapster + +FIXME: summarize the overall steps of the translation ### Argument Layout @@ -209,14 +215,99 @@ variables plus permissions: * Otherwise, `Arg(sh)` is undefined. +For any sequence `sh1,...,shn` of shapes for `n` input arguments, we define the +argument sequence layout function `Args(sh1,...,shn)` as the sequence of +permissions on regular and ghost arguments given by `Arg(sh1),...,Arg(shn)`, if +all of these are defined. + +We define the return value layout function `Ret(sh)` as a partial function from +a shape `sh` to a permission on the return value `ret` of a funciton as follows: + +* If `Lyt(sh)=struct(p)` for a single permission `p`, then `Ret(sh)=ret:p`; + +* If `Lyt(sh)=p` for `p` of type `perm(struct(tp1,...,tpn))` for a sequence + `tp1,...,tpn` of 0, 1, or 2 types, then `Ret(sh)=ret:p`; + +* Otherwise, `Ret(sh)` is undefined. + +We can then define the function type layout `FnLyt(sh1,...,shn,sh)` of a +sequence of `n` shapes for input arguments and a shape `sh` for the return value +as follows: +* If `Ret(sh)=ret:p` and `Args(sh1,...,shn)` is defined, then + `FnLyt(sh1,...,shn,sh)` is defined as the Heapster function permission + `Args(sh1,...,shn) -o ret:p` that takes in the regular and ghost arguments + specificed by `Args(sh1,...,shn)` and returns a value `ret` with permission + `p`; +* If `Ret(sh)` is undefined but `Args(sh1,...,shn)` is defined and `len(sh)=ln`, then + `FnLyt(sh1,...,shn,sh)` is defined as the Heapster function permission + ``` + arg0:memblock(W,0,ln,emptysh),Args(sh1,...,shn) -o arg0:memblock(W,0,ln,emptysh) + ``` -### Lifetime Permissions +* Otherwise, `FnLyt(sh1,...,shn,sh)` is undefined. +### Adding Lifetime Permissions + +FIXME: explain the two steps, lifetime lifting and building the lifetime +ownership permissions + +The lifetime lifting function `LtLift(p)` maps a permission `p` to a lifted +permission along with 0 or more fresh ghost variables with permissions on them. +Intuitively, this operation finds permissions contained inside `p` that use any +of the lifetime variables of a function type, and lift those permissions to +permissions on fresh ghost variables. This allows the permission type for a +function to refer to just those values inside a more complicated type that +depend on a particular lifetime. + +FIXME: example + +To define the lifetime lifting function, we first define the lifetime lifting +contexts as follows: + +``` +L ::= _ | [l]ptr((rw,off) |-> L) * p1 * ... * pn | [l]memblock(rw,off,len,Lsh) * p1 * ... * pn +Lsh ::= _ | sh1;Lsh | Lsh;sh2 | fieldsh(L) | ptrsh(rw,l,Lsh) +``` + +FIXME: describe the process of lifting +FIXME: explain the operation `LtPerms(a)(x:p)`, defined as follows: -FIXME: -- explain layout (types that take more than two fields become pointers) -- explain lifetimes +* For conjunctions, the operation returns only those conjuncts that contain + lifetime `a`, meaning that `LtPerms(a)(x:p1*...*pn)=x:p(i1)*...*p(ik)` where + `i1,...,ik` is the sequence of indices `i` such that `pi` contains lifetime + variable `a` free; + +* If `p` is not a conjunction but contains `a` free, then `LtPerms(a)(x:p)=x:p`; + +* Othersise, `LtPerms(a)(x:p)` is the empty sequence `()`. + +To add a lifetime permission to a function type `ps_in -o ps_out`, we define the +function + +``` +AddLt(a)(ps_in -o ps_out) = + let a_ps_in = absMods(a)(LtPerms(a)(ps_in)) in + a:lowned(a_ps_in), ps_in -o a:lowned(LtPerms(a)(ps_out) -o a_ps_in) +``` + +FIXME: explain the above; also define `absMods(a)(ps)` as the funciton that +abstracts all the read/write and lifetime modalities in `ps` + +To add multiple lifetime permissions to a function type, we define + +``` +AddLts(a1,...,an)(ps_in -o ps_out) = + AddLt(a1)(AddLt(a2)(... AddLt(an)(ps_in -o ps_out))) +``` + +Putting all the pieces together, we define the translation of a Rust function +type as follows: + +``` +[| <'a1,...,'am> fn (x1 : T1, ..., xk : Tk) -> T |] = + AddLts(a1,...,an)(LtLift(FnLyt([| T1 |], ..., [| Tn |], [| T |]))) +``` From e8f54af597a7cfd9d12696421d471c8fa0c41d7c Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Wed, 8 Jun 2022 17:22:39 -0700 Subject: [PATCH 14/20] added more text to describe argument layout --- heapster-saw/doc/RustTrans.md | 55 ++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md index cfdf7fbd89..6d25061b8c 100644 --- a/heapster-saw/doc/RustTrans.md +++ b/heapster-saw/doc/RustTrans.md @@ -129,12 +129,19 @@ actually allow polymorphic function types, but only supports non-polymorphic functions types, starting with the `fn` keyword, so this is a syntactic extension supported by Heapster. +Rust function types are translated to Heapster function types in two steps. The +first step is argument layout. Argument layout takes the translations of the +Rust argument types to Heapster shapes, which describe the shape of memory +blocks, and lays out those memory block shapes onto register values. At a high +level, this step can be seen as bridging the gap between Rust types, which +describe blocks of memory, and LLVM types, which describe values. The second +step is to add lifetime permissions. This step generates lifetime ownership +permissions for each of the lifetime variables `'ai` in the Rust function type. + FIXME: give some examples of how some simple Rust function types are translated to Heapster -FIXME: summarize the overall steps of the translation - ### Argument Layout @@ -191,18 +198,33 @@ Using these operations, the layout function `Lyt(sh)` is defined as follows: | `Lyt(emptysh)` = | `struct()` | | `Lyt(Name)` = | `Lyt(unfold(Name,args))` | | `Lyt(fieldsh(p))` = | `struct(p)` | -| `Lyt(arraysh(_,_,_))` = | undefined | +| `Lyt(arraysh(k,stride,sh))` = | `Lyt(sh;...;sh)` for `k` copies of `sh`, if `8*len(sh)=stride` | +| `Lyt(arraysh(_,_,_))` = | undefined otherwise | | `Lyt(sh1 ; sh2)` = | `Lyt(sh1) ++ Lyt(sh2)` | | `Lyt(sh1 orsh sh2)` = | `Lyt(sh1) \/ Lyt(sh2)` | | `Lyt(exsh z. sh)` = | `exists z. Lyt(sh)` | | `Lyt(falsesh)` = | `false` | - -FIXME: explain the above definition - -FIXME: define the argument layout function `Arg(sh)` as a function from a shape -`sh` to a sequence of zero or more ghost variables and regular argument -variables plus permissions: +The empty shape is laid out as a struct permission on an empty list of fields. +Named shapes are laid out by laying out their unfolding. Field shapes are laid +out as a struct permission with a single field whose permission is given by the +permission in the field shape. Array shapes with a known, fixed size `k` are +laid out as `k` copies of their shape. Otherwise, array shapes with a +dynamically-determined length are not laid out as arguments. Sequence and +disjunctive shapes are laid out using the `++` and `\/` operations defined +above, respectively, while existential shapes are laid out as existential +permissions and the false shape is laid out as the false permission. + +Using the `Lyt(sh)` function, we define the argument layout function `Arg(sh)` +that maps `sh` to a sequence of arguments and their corresponding permissions. +The Rust compiler uses the convention that any type that fits in no more than +two argument values is laid out into argument values, and otherwise is passed by +pointer. To handle this convention, `Arg(sh)` returns permissions for up to two +argument values if `Lyt(sh)` returns a struct permission with at most two +fields, and otherwise returns a `memblock` permission describing a pointer to a +memory block of shape `sh`. More formally, `Arg(sh)` is a function from shape +`sh` to a sequence of normal and ghost arguments with permissions, defined as +follows: * If `Lyt(sh)=struct(p1,...,pn)` for a sequence `p1,...,pn` of 0, 1, or 2 permissions, then `Arg(sh)=arg1:p1,...,argn:pn`; @@ -215,10 +237,17 @@ variables plus permissions: * Otherwise, `Arg(sh)` is undefined. -For any sequence `sh1,...,shn` of shapes for `n` input arguments, we define the -argument sequence layout function `Args(sh1,...,shn)` as the sequence of -permissions on regular and ghost arguments given by `Arg(sh1),...,Arg(shn)`, if -all of these are defined. +The complexity of the second case comes from the case where `Lyt(sh)` returns a +struct permission where the permissions on the individual fields are cannot be +separated from each other. In this case, `Arg(sh)` returns a ghost variable +`ghost` to specify the tuple of the arguments, each of which are required to +equal their corresponding projection of `ghost` using `eq_proj` permissions. + +The argument layout function `Arg(sh)` is extended to multiple arguments with +the argument sequence layout function `Args(sh1,...,shn)`. For any sequence +`sh1,...,shn` of shapes for `n` input arguments, we define the +`Args(sh1,...,shn)` as the sequence of permissions on regular and ghost +arguments given by `Arg(sh1),...,Arg(shn)`, if all of these are defined. We define the return value layout function `Ret(sh)` as a partial function from a shape `sh` to a permission on the return value `ret` of a funciton as follows: From 3917f8d7d883dbd616c0ebab1ed22678a608774f Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Wed, 8 Jun 2022 17:25:42 -0700 Subject: [PATCH 15/20] added a note to clarify the relationship between argument layout and type layout --- heapster-saw/doc/RustTrans.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md index 6d25061b8c..37c8bf0e99 100644 --- a/heapster-saw/doc/RustTrans.md +++ b/heapster-saw/doc/RustTrans.md @@ -147,16 +147,21 @@ to Heapster Argument layout converts a shape, which describes the layout and associated permissions of a memory block, to a permission on a sequence of register values, -if this is possible. In Heapster (as in the underlying Crucible type system), -sequences of values are called structs and a permission on a sequence of values -is called a struct permission. More specifically, argument layout is defined as -a partial function `Lyt(sh)` that maps a Heapster shape `sh` for a particular -function argument to a permission of type `perm(struct(tp1,...,tpn))` for some -value types (i.e., Crucible types) `tp1` through `tpn`. When the layout of the -type `T` of an argument is not defined --- e.g., if `T` is too big to fit in -registers or it is a slice or other dynamically-sized type that has no -well-defined size --- then the corresponding argument is represented as a -pointer to a block of memory with the shape defined by `T`. +if this is possible. Note that this concept is different from the Rust concept +of "type layout", though the two are related. In fact, the notion of argument +layout described here is very undocumented in Rust, and has in fact been +determined by consulting a number of blog posts and by much experimentation. + +In Heapster (as in the underlying Crucible type system), sequences of values are +called structs and a permission on a sequence of values is called a struct +permission. Argument layout is thus defined as a partial function `Lyt(sh)` that +maps a Heapster shape `sh` for a particular function argument to a permission of +type `perm(struct(tp1,...,tpn))` for some value types (i.e., Crucible types) +`tp1` through `tpn`. When the layout of the type `T` of an argument is not +defined --- e.g., if `T` is too big to fit in registers or it is a slice or +other dynamically-sized type that has no well-defined size --- then the +corresponding argument is represented as a pointer to a block of memory with the +shape defined by `T`. In order to define `Lyt(sh)`, we first define two helper operations on structure permissions. Both of these are partial functions that take in two structure From 3641bf2528ddedaed7d79b03d4a6488a87a824e2 Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Thu, 9 Jun 2022 18:00:32 -0700 Subject: [PATCH 16/20] added function type translation examples; also updated the translation to correctly handle slices --- heapster-saw/doc/RustTrans.md | 203 +++++++++++++++++++++++++++++++++- 1 file changed, 197 insertions(+), 6 deletions(-) diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md index 37c8bf0e99..fa0f390ed7 100644 --- a/heapster-saw/doc/RustTrans.md +++ b/heapster-saw/doc/RustTrans.md @@ -24,14 +24,35 @@ type name `Name`, as defined in the next section. | Rust Type | Translation to a Heapster Shape | |--------|--------------------| | `Box` | `ptr((W,0) \|-> [\| T \|])` | -| `&mut 'a T` | `[a]ptr((W,0) \|-> [\| T \|])` | -| `&'a T` | `[a]ptr((R,0) \|-> [\| T \|])` | -| `[T]` | `exists n:bv 64. arraysh(n, len([\| T \|]), [\| T \|])` | +| `&mut 'a [T]` | see below | +| `&mut 'a T` | `[a]ptrsh(W,[\| T \|])` if `T` is not a DST | +| `&'a [T]` | see below | +| `&'a T` | `[a]ptrsh(R,[\| T \|])` if `T` is not a DST | +| `[T;N]` | `arraysh(N, [\| T \|])` | | `(T1,...,Tn)` | `[\| T1 \|] ; ... ; [\| Tn \|]` | | `Name<'a1,...,'am,T1,...,Tn>` | `[\| Name \|] (a1, ..., am, [\| T1 \|], ..., [\| Tn \|])` | | `!` | `falsesh` | -FIXME: describe the above + +Types of the form `&mut 'a [T]` and `&'a [T]` are treated specially in Rust, +because these are references to a slice type `[T]` of unknown size. In Rust, +types with unknown size are called _dynamically sized types_ or DSTs. These +require special treatment in order to ensure that dereferences are always +bounds-checked to be in the bounds of the slice. To make this possible, +references to DSTs are always "fat pointers" that are a pointer value along with +an integer value that says how many elements are in the slice pointed to by the +pointer. Thus, the type `&mut 'a [T]` is translated as follows: + +``` +exsh n:bv 64.[a]ptrsh(W,arraysh(n,[| T |]));eq(llvmword(n)) +``` + +This shape says there exists an `n` such that the first field in a memory block +of this shape points to an array of `n` elements, each of which have shape +`[| T |]`, while the second field is an LLVM word value equal to `n`. Read +references `&'a [T]` to slices are translated similarly, but with read instead +of write pointer shapes. + ## Translating Type Definitions @@ -137,10 +158,180 @@ level, this step can be seen as bridging the gap between Rust types, which describe blocks of memory, and LLVM types, which describe values. The second step is to add lifetime permissions. This step generates lifetime ownership permissions for each of the lifetime variables `'ai` in the Rust function type. +The remainder of this section illustrates this translation process through some +examples, and then defines each of the two function type translation steps in +detail. + + +### How Function Types are Translated + +For function types with no lifetimes whose arguments and return values all fit +into a single register (which we assume is 64-bits), the translation is +straightforward. For example, consider the following `box_read` function, that +reads a 64-bit unsigned value from a `Box` pointer: + +``` +fn box_read (p:Box) -> u64 { *p } +``` + +The type of `box_read` is `(Box) -> u64`, which translates to the Heapster +function type + +``` +arg0:ptr((W,0) |-> exists z. eq(llvmword(z))) -o arg0:true, ret:exists z. eq(llvmword(z)) +``` + +This type says that the first and only argument, `arg0`, is a pointer to an LLVM +word value when the function is called. More specifically, the permission +`exists z.eq(llvmword(z))` describes an LLVM value that is a word, or numeric, +value, as opposed to a pointer value. Because it is so common, Heapster scripts +often define the abbreviation `int64<>` for this permission, and we shall use +this abbreviation in the remaining examples here. The return value `ret` for our +example is also an LLVM word value. On return, no permissions are held on the +`arg0` value, reflecting the fact that the `Box` pointer passed into `box_read` +is deallocated by that function. + +If an argument type does not fit into a single register but does fit into two +registers, Rust will lay it out across two argument values at the LLVM level. +For example, let us define a struct type `Pair64` of pairs of 64-bit integers +and a function `pair_proj1` to project out the first element of such a struct as +follows: + +``` +struct Pair64 { proj1 : u64, proj2 : u64 } + +fn pair_proj1 (p:Pair64) -> u64 { p.1 } +``` + +The `Pair64` structure fits into two 64-bit registers, so the type of +`pair_proj1` is translated to the Heapster type + +``` +arg0:int64<>, arg1:int64<> -o ret:int64<> +``` + +Note that, if the input or output permission on an argument is the vacuous +permission `true`, it can be omitted, so the above permission states that no +permissions are returned with the argument values `arg0` and `arg1`. + +If the return value fits into two registers, Rust returns it as a two-element +structure, so, for instance, the Rust function type `fn (Pair64) -> Pair64` +translates to the Heapster type + +``` +arg0:int64<>, arg1:int64<> -o struct(int64<>,int64<>) +``` + +Fieldless enums, which is the Rust name for enum types where none of the +constructors has any fields, can be laid out in a single register for the +discriminant. For enum types with fields, if all the fields of each constructor +of an enum fit into a single register, then the entire enum is laid out as two +registers, one for the discriminant and one for the field(s) of the +corresponding constructor. This type is a little more complicated to represent +in Heapster, because the disjunction for the enum must apply to multiple values +at the same time. This is accomplished using a ghost variable of struct type, +and stating the the individual arguments equal its projections. For example, if +we define the enum + +``` +#[repr(C,u64)] pub enum Sum { Left (X), Right (Y) } +``` + +then the type `fn (Sum<(),u64>) -> u64` is translated as follows: + +``` +ghost:(struct(eq(llvmword(0)),true) or struct(eq(llvmword(1)),int64<>)), +arg0:eq_proj(ghost,0), arg1:eq_proj(ghost,1) +-o +ret:int64<> +``` + +This type says that, on input, the first and second arguments are the first and +second projections, respectively, of some struct given by a ghost variable +`ghost`. The permissions on `ghost` say that either its first field equals `0` +and its second field is unconstrained, corresponding to the `Left` constructor +of the `Sum` type, or its first field equals `1` and its second field is a +64-bit integer, corresponding to the `Right` constructor. As before, the output +permissions are `int64<>` for the return value and no permissions for the input +arguments. + +If the type of an argument does not fit into two registers, Rust passes it by +pointer. That is, if an argument has a type `T` that does not fit into two +registers, then it is treated as if it had type `Box`. For example, if we +define the struct type + +``` +struct Triple64 { triple1:u64, triple2:u64, triple3:u64 } +``` + +then the type `fn (Triple64) -> u64` is translated to + +``` +arg0:memblock(W,0,24,Triple64<>) -o ret:int64<> +``` + +where the named shape `Triple64<>` is defined as the sequence + +``` +fieldsh(int64<>);fieldsh(int64<>);fieldsh(int64<>) +``` + +of three field shapes containing 64-bit integers. The `memblock` input +permission has size `24` because `Triple64<>` has three 8-byte fields, for a +total size of 24 bytes. + +If the return value does not fit into two registers, its value is written to a +pointer that is passed as the first argument. So, for instance, the function +type `fn (Triple64) -> Triple64` is translated to + +``` +arg0:memblock(W,0,24,true), arg1:(W,0,24,Triple64<>) -o arg0:(W,0,24,Triple64<>) +``` + +This indicates that, on input, `arg0` points to a 24-byte memory block. The +`true` shape indicates that this block can be uninitialized, i.e., that no +constraints are made on its shape. The actual input argument of type `Triple64` +is passed as `arg1`. On output, permissions to `arg1` are dropped, but +permissions to `arg0` are changed to have the shape `Triple64<>` of the return +type. + +The remaining complexity in translating function types to Heapster is in +handling lifetimes. This works by adding lifetime ownership permissions to both +the input and output permissions for each lifetime `'a` in the Rust function +type, indicating that lifetime `'a` is active at the start of the function and +when it returns. Recall that a lifetime ownership permission has the form +`a:lowned (ps_in -o ps_out)`. This permission indicates that lifetime `'a` +"holds" or "contains" permissions `ps_out`, and is current "leasing out" or +"lending" permissions `ps_in`. The input lifetime ownership permission for `'a` +is constructed to indicate that `'a` is currently lending out all permissions in +the input type that refer to `'a`, and that it holds versions of these +permissions that are in some other, bigger lifetimes outside of `'a`. The output +lifetime ownership permission for `'a` states that `'a` still holds the same +permissions, but that it is only lending those permissions in the output +permissions of the Heapster function type containing `'a`. + +For example, consider the accessor function + +``` +fn <'a> pair_proj1_ref (p:&mut 'a Pair64) -> &mut 'a u64 { &mut p.1 } +``` + +that takes a mutable reference to a `Pair64` and returns a mutable reference to +its first element. The translation of the type of `pair_proj1_ref`, which is the +function type `<'a> fn (&mut 'a Pair64) -> &mut 'a u64`, is the Heapster type + +``` +a:lowned(arg0:[a]ptr((W,0) |-> Pair64<>) -o arg0:[l]ptr((W,0) |-> Pair64<>)), +arg0:[a]ptr((W,0) |-> Pair64<>) +-o +a:lowned(ret:[a]ptr((W,0) |-> int64<>) -o arg0:[l]ptr((W,0) |-> Pair64<>)), +ret:[a]ptr((W,0) |-> int64<>) +``` + +FIXME: keep going... -FIXME: give some examples of how some simple Rust function types are translated -to Heapster +- More complex types containing references, such as Option out ### Argument Layout From 085182f4d7117891bb73b8808622492b4cc65d3c Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Fri, 10 Jun 2022 14:39:07 -0700 Subject: [PATCH 17/20] finished describing all the pieces of the Rust function type translation --- heapster-saw/doc/RustTrans.md | 137 ++++++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 32 deletions(-) diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md index fa0f390ed7..ef277feb2e 100644 --- a/heapster-saw/doc/RustTrans.md +++ b/heapster-saw/doc/RustTrans.md @@ -299,16 +299,30 @@ The remaining complexity in translating function types to Heapster is in handling lifetimes. This works by adding lifetime ownership permissions to both the input and output permissions for each lifetime `'a` in the Rust function type, indicating that lifetime `'a` is active at the start of the function and -when it returns. Recall that a lifetime ownership permission has the form -`a:lowned (ps_in -o ps_out)`. This permission indicates that lifetime `'a` +when it returns. The input lifetime ownership permission for `'a` says that each +of the permissions mentioning lifetime `'a` has been borrowed from some other, +larger lifetime that is outside of `'a`. The output lifetime ownership +permission for `'a` says that these same permissions are still borrowed by `'a` +from the same outer lifetimes, but that all of those permissions have been +"given back" to `'a` except for those permissions in the output permissions that +still mention `'a`. + +In more detail, recall that a lifetime ownership permission has the form +`a:lowned (ps_in -o ps_out)`. This permission indicates that lifetime `a` "holds" or "contains" permissions `ps_out`, and is current "leasing out" or -"lending" permissions `ps_in`. The input lifetime ownership permission for `'a` -is constructed to indicate that `'a` is currently lending out all permissions in -the input type that refer to `'a`, and that it holds versions of these -permissions that are in some other, bigger lifetimes outside of `'a`. The output -lifetime ownership permission for `'a` states that `'a` still holds the same -permissions, but that it is only lending those permissions in the output -permissions of the Heapster function type containing `'a`. +"lending" permissions `ps_in`. Once all of the lent permissions `ps_in` are +returned to lifetime `a`, that lifetime can be ended, and the permissions +`ps_out` that it holds can be recovered. The input lifetime ownership permission +used for `a` has the form `a:lowned (ps_a_in -o ps_a_abs)`, where `ps_a_in` is +the list of all permissions containing `a` in the input of the translated Rust +function type, and `ps_a_abs` is the result of replacing each occurrence of `a` +and its accompanying read/write modality with fresh variables. (NOTE: the actual +input lifetime ownership permission computed by Heapster is the simplified +lifetime ownership permission `a:lowned(ps_a_abs)`, which is logically +equivalent to the above but has a simpler translation.) The output +lifetime ownership permission is `a:lowned (ps_a_out -o ps_a_abs)`, where +`ps_a_out` is the list of all permissions containing `a` in the output of the +translated Rust function type. For example, consider the accessor function @@ -321,17 +335,45 @@ its first element. The translation of the type of `pair_proj1_ref`, which is the function type `<'a> fn (&mut 'a Pair64) -> &mut 'a u64`, is the Heapster type ``` -a:lowned(arg0:[a]ptr((W,0) |-> Pair64<>) -o arg0:[l]ptr((W,0) |-> Pair64<>)), +a:lowned(arg0:[a]ptr((W,0) |-> Pair64<>) -o arg0:[l]ptr((rw,0) |-> Pair64<>)), arg0:[a]ptr((W,0) |-> Pair64<>) -o -a:lowned(ret:[a]ptr((W,0) |-> int64<>) -o arg0:[l]ptr((W,0) |-> Pair64<>)), +a:lowned(ret:[a]ptr((W,0) |-> int64<>) -o arg0:[l]ptr((rw,0) |-> Pair64<>)), ret:[a]ptr((W,0) |-> int64<>) ``` +The input permissions say that `arg0` is a writeable pointer to a `Pair64` +structure, that is only valid while lifetime `a` is active. Further, the input +lifetime ownership permission for `a` says that, when the function is called, +`a` holds pointer permissions to `arg0` relative to some other lifetime `l`, and +is currently lending pointer permissions to `arg0` relative to `a`. The output +permissions say that the return value `ret` is a writeable pointer to a 64-bit +integer that is relative to lifetime `a`. The output lifetime permission for `a` +says that `a` holds the same pointer permission relative to lifetime `l` as on +input, but is only lending out the pointer held by `ret`. -FIXME: keep going... +If a permission containing lifetime `a` is inside another permission, it is +lifted to the top level by creating a ghost variable that holds that permission. +For instance, the type `<'a> fn (Box<&'a u64>) -> u64` is translated to the +Heapster type -- More complex types containing references, such as Option out +``` +a:lowned(z:[a]ptr((R,0) |-> int64<>) -o z:[l]ptr((rw,0) |-> int64<>)), +arg0:ptr((W,0) |-> eq(z)), z:[a]ptr((R,0) |-> int64<>) +-o +a:lowned(empty -o z:[l]ptr((rw,0) |-> int64<>)), +ret:int64<> +``` + +In this case, `z` is a ghost variable used to represent the pointer value +pointed to by `arg0` for which a pointer permission in lifetime `a` is held. As +before, the input lifetime ownership permission for `a` specifies that pointer +permissions for `z` relative to some outer lifetime `l` are held by lifetime +`a`, which is currently lending out a copy of those permissions relative to +lifetime `a`. Since there are no occurrences of `a` in the output Rust type, the +output lifetime ownership permission for `a` indicates that `a` is not lending +any permissions on return from the function, indicated with the `empty` +permissions list. ### Argument Layout @@ -476,30 +518,61 @@ as follows: ### Adding Lifetime Permissions -FIXME: explain the two steps, lifetime lifting and building the lifetime -ownership permissions - -The lifetime lifting function `LtLift(p)` maps a permission `p` to a lifted -permission along with 0 or more fresh ghost variables with permissions on them. -Intuitively, this operation finds permissions contained inside `p` that use any -of the lifetime variables of a function type, and lift those permissions to -permissions on fresh ghost variables. This allows the permission type for a -function to refer to just those values inside a more complicated type that -depend on a particular lifetime. +Adding lifetime permissions to the translation of a Rust function type is done +in two steps. The first step, lifetime lifting, lifts permissions containing a +lifetime to the top level. The second step constructs the required lifetime +ownership permissions. -FIXME: example +For the first step, the lifetime lifting function `LtLift(p)` maps a permission +`p` to a lifted permission along with 0 or more fresh ghost variables with +permissions on them. Intuitively, this operation finds permissions contained +inside `p` that use any of the lifetime variables of a function type, and lift +those permissions to permissions on fresh ghost variables. This allows the +permission type for a function to refer to just those values inside a more +complicated type that depend on a particular lifetime. To define the lifetime lifting function, we first define the lifetime lifting contexts as follows: ``` L ::= _ | [l]ptr((rw,off) |-> L) * p1 * ... * pn | [l]memblock(rw,off,len,Lsh) * p1 * ... * pn -Lsh ::= _ | sh1;Lsh | Lsh;sh2 | fieldsh(L) | ptrsh(rw,l,Lsh) +Lsh ::= sh1;Lsh | Lsh;sh2 | fieldsh(L) | ptrsh(rw,l,Lsh) ``` -FIXME: describe the process of lifting +Each lifetime lifting context `L` is a permission with a single occurrence of a +"hole" of the form `_`. Similarly, a lifetime lifting shape context `Lsh` is a +shape containing a single occurrence of a hole inside one of its field +permissions. We write `L[p]` and `Lsh[p]` for the result of replacing the hole +`_` with `p` in `L` or `Lsh`, respectively. Intuitively, a hole describes an +occurrence of a permission inside a larger permission or shape that can be +lifted to a top-level ghost variable. Holes are only allowed inside a pointer +permission or the shape of a block permission; specifically, they are not +allowed inside disjunctive or array permissions, because there could be zero or +multiple values corresponding to that permission, and lifetime lifting is only +supposed to lift a single value. -FIXME: explain the operation `LtPerms(a)(x:p)`, defined as follows: +If any permission `p` can be written as `L[p']` for some `L` that is not the +trivial context `_` and some `p'` containing a free lifetime variable, then we +say that the permission `L[eq(z)]` along with the permission assignment `z:p'` +for fresh ghost variable `z` is a _lifetime lifting_ of `p`. We then define the +lifetime lifting function `LtLift(p)` from permission `p` to a permission plus +a sequence of zero or more ghost variables with permissions as follows: + +* If `p` has a lifetime lifting `L[eq(z)]` and `z:p'` such that `p'` itself has + no lifetime lifting, then `LtLift(p)` returns `LtLift(L[eq(z)])` along with + `z:p'`; + +* Otherwise, `LtLift(p)` just returns `p` itself. + +The `LtLift()` function is then extended to lists of permissions +`x1:p1,...,xn:pn` by applying it pointwise to the individual permissions `p1` +through `pn`. + + +For the second step of adding lifetime permissions to the translation of a Rust +function type, we first define the operation `LtPerms(a)(x:p)` that finds all +permissions containing lifetime `a` in the permission assignment `x:p`. This is +defined as follows: * For conjunctions, the operation returns only those conjuncts that contain lifetime `a`, meaning that `LtPerms(a)(x:p1*...*pn)=x:p(i1)*...*p(ik)` where @@ -515,13 +588,13 @@ function ``` AddLt(a)(ps_in -o ps_out) = - let a_ps_in = absMods(a)(LtPerms(a)(ps_in)) in - a:lowned(a_ps_in), ps_in -o a:lowned(LtPerms(a)(ps_out) -o a_ps_in) + let ps_a_in = LtPerms(a)(ps_in) in + let ps_a_abs = absMods(a)(ps_a_in) in + a:lowned(ps_a_in -o ps_a_abs), ps_in -o a:lowned(LtPerms(a)(ps_out) -o a_ps_in) ``` -FIXME: explain the above; also define `absMods(a)(ps)` as the funciton that -abstracts all the read/write and lifetime modalities in `ps` - +The function `absMods(a)(ps)` abstracts each occurrence of lifetime `a` and its +associated read/write modality by instantiating them with fresh ghost variables. To add multiple lifetime permissions to a function type, we define ``` From a339b97013c01453ba6780dda2022b879b7f1e5a Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Fri, 10 Jun 2022 15:12:25 -0700 Subject: [PATCH 18/20] finished removing all the FIXMEs from the document! --- heapster-saw/doc/RustTrans.md | 50 ++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/heapster-saw/doc/RustTrans.md b/heapster-saw/doc/RustTrans.md index ef277feb2e..838e8a5e53 100644 --- a/heapster-saw/doc/RustTrans.md +++ b/heapster-saw/doc/RustTrans.md @@ -1,7 +1,13 @@ # Rust-to-Heapster Translation -FIXME: Rust translates to a subset of Heapster, as described in this document +In this document, we describe the automated translation from Rust types to +Heapster permissions. Because some of the details of how Rust types are laid out +in memory are not explicitly defined by the Rust specification, some of this +translation has been informed by experimentation with how Rust compiles various +functions and types, so may not be entirely complete or accurate, but it so far +seems to work in most cases. + ## Translating Expression Types @@ -111,10 +117,10 @@ An enumeration type like the above is translated to Heapster as follows: ``` Name = - (fieldsh(eq(0)) ; [| T1_1 |] ; ... ; [| T1_k1 |]) orsh - (fieldsh(eq(1)) ; [| T2_1 |] ; ... ; [| T2_k2 |]) orsh + (fieldsh(eq(llvmword(0))) ; [| T1_1 |] ; ... ; [| T1_k1 |]) orsh + (fieldsh(eq(llvmword(1))) ; [| T2_1 |] ; ... ; [| T2_k2 |]) orsh ... - (fieldsh(eq(l-1)) ; [| Tl_1 |] ; ... ; [| Tl_kl |]) + (fieldsh(eq(llvmword(l-1))) ; [| Tl_1 |] ; ... ; [| Tl_kl |]) ``` (NOTE: Technically speaking, this translation assumes the enum has been @@ -122,9 +128,41 @@ flagged with the `#[repr(C,u64)]` pragma to indicate that the discriminant is a 64-bit integer and that the type is laid out in a C-compatible manner.) -## Layout Optimizations +## Niche Optimization + +As an optimization, Rust has one exception to the rules given above for enums +that is called _niche optimization_. To define niche optimization, we first +define the notion of an _option-like_ enum, which is an enum type that has one +constructor with a field of some type `T` and one constructor with no fields. +The type `T` is called the _payload_ of the option-like enum type. The primary +example is the type `Option`, defined (in the Rust standard library) as +follows: + +``` +enum Option { None, Some (X) } +``` + +A _niche_ in a type `T` is any bit pattern with the same size as `T` but that is +disallowed by the shape requirements of `T`. For instance, the `Box` and +reference pointer types in Rust are required to be non-null, so the null value +is a niche for these types. Similarly, an enum type with `N` fields, numbered +`0` through `N-1`, has a niche where the discriminant is set to the value `N`. + +The high-level idea of niche optimization is that the fieldless constructor of +an option-like type can be represented by a niche value in its payload type. +This reduces the size of the elements of this type by eliminating the need for +its discriminant. Thus, for example, the `Option` Rust type is translated to the +following cases: + +``` +Option<[a]ptr((rw,off) |-> p)> = eq(llvmword(0)) or [a]ptr((rw,off) |-> p) +Option<(fieldsh(eq(llvmword(0)));sh0) orsh ... orsh (fieldsh(eq(llvmword(n)));shn)> = + (fieldsh(eq(llvmword(0)));sh0) orsh ... orsh (fieldsh(eq(llvmword(n)));shn) + orsh fieldsh(eq(llvmword(n+1))) +Option = + fieldsh(eq(llvmword(0))) orsh (fieldsh(eq(llvmword(1)));X) +``` -FIXME: Option-like types ## Translating Function Types From cc36a299d15905e419b862814b466c89241d6ffc Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Fri, 10 Jun 2022 15:13:59 -0700 Subject: [PATCH 19/20] added a few more examples to rust_data.rs --- heapster-saw/examples/rust_data.bc | Bin 277360 -> 281088 bytes heapster-saw/examples/rust_data.rs | 46 ++++++++++++++++++++++++++++ heapster-saw/examples/rust_data.saw | 5 +++ 3 files changed, 51 insertions(+) diff --git a/heapster-saw/examples/rust_data.bc b/heapster-saw/examples/rust_data.bc index 6c66093e95ceda75aea28e2758ca1be5a4ce2744..da8841005e2a6b0ccf5b108c78e507488e9baaca 100644 GIT binary patch delta 124996 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 bVxrZs$3(lk9If^hhcMM{bSyttYySK%6GASr delta 121893 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/aYDw=6JH+`&7ywHy*lwxl<+% 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 z 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 DA(_|y diff --git a/heapster-saw/examples/rust_data.rs b/heapster-saw/examples/rust_data.rs index cb021bba12..982abfffdb 100644 --- a/heapster-saw/examples/rust_data.rs +++ b/heapster-saw/examples/rust_data.rs @@ -61,6 +61,10 @@ pub fn two_values_proj1 (x:TwoValues) -> u32 { } } +pub fn two_values_proj1_ref <'a> (x:&'a mut TwoValues) -> &'a mut u32 { + &mut x.1 +} + pub extern fn two_values_proj1_extern (x:TwoValues) -> u32 { match x { TwoValues(x1,_) => x1 @@ -211,6 +215,10 @@ impl Sum { } } +pub fn mk_sum_left_asym (x:u32) -> Sum { + Sum::Left (x) +} + pub fn mk_string_sum_left (x:&str) -> Sum { Sum::Left (x.to_string()) } @@ -232,6 +240,14 @@ pub extern fn mk_sum_sum_left_asym_extern (x:Sum) -> Sum,u } +pub fn elim_sum_u64_u64 (x:Sum) -> u64 { + match x { + Sum::Left (x) => x, + Sum::Right (x) => x, + } +} + + /* A struct containing a string */ #[repr(C)] pub struct StrStruct(String); @@ -344,6 +360,36 @@ impl fmt::Display for TrueEnum { } +/*** + *** Pointers and References + ***/ + +pub fn box_read (p:Box) -> u64 { + *p +} + + +/*** + *** Slices and Arrays + ***/ + +pub fn index_one_array (x:[u64; 1]) -> u64 { + x[0] +} + +pub fn index_two_array (x:[u64; 2]) -> u64 { + x[0] +} + +pub fn index_three_array (x:[u64; 3]) -> u64 { + x[0] +} + + +/*** + *** Linked Lists + ***/ + /* A linked list */ #[derive(Clone, Debug, PartialEq)] #[repr(C,u64)] diff --git a/heapster-saw/examples/rust_data.saw b/heapster-saw/examples/rust_data.saw index 8eba8ebcf1..90f86392de 100644 --- a/heapster-saw/examples/rust_data.saw +++ b/heapster-saw/examples/rust_data.saw @@ -519,6 +519,11 @@ test_sum_impl_sym <- heapster_find_symbol env "13test_sum_impl"; heapster_typecheck_fun_rename env test_sum_impl_sym "test_sum_impl" "().arg0:memblock(R,0,16,Sum),fieldsh(int64<>)>) -o ret:int1<>"; +// elim_sum_u64_u64 +elim_sum_u64_u64_sym <- heapster_find_symbol env "16elim_sum_u64_u64"; +heapster_typecheck_fun_rename env elim_sum_u64_u64_sym "elim_sum_u64_u64" + "<> fn (x:Sum) -> u64"; + // NOTE: Fails because of `clone` in the implementation // MixedStruct::get_s // mixed_struct_get_s <- heapster_find_symbol env "11MixedStruct5get_s"; From adac5e4a051a694170d0ba014bbee3fe97f571bf Mon Sep 17 00:00:00 2001 From: Eddy Westbrook Date: Tue, 14 Jun 2022 06:31:22 -0700 Subject: [PATCH 20/20] whoops, removed an example that does not work yet... --- heapster-saw/examples/rust_data.saw | 3 +++ 1 file changed, 3 insertions(+) diff --git a/heapster-saw/examples/rust_data.saw b/heapster-saw/examples/rust_data.saw index 90f86392de..bfb85f8af8 100644 --- a/heapster-saw/examples/rust_data.saw +++ b/heapster-saw/examples/rust_data.saw @@ -520,9 +520,12 @@ heapster_typecheck_fun_rename env test_sum_impl_sym "test_sum_impl" "().arg0:memblock(R,0,16,Sum),fieldsh(int64<>)>) -o ret:int1<>"; // elim_sum_u64_u64 +// FIXME: needs to handle enums that are small enough to fit in arguments +/* elim_sum_u64_u64_sym <- heapster_find_symbol env "16elim_sum_u64_u64"; heapster_typecheck_fun_rename env elim_sum_u64_u64_sym "elim_sum_u64_u64" "<> fn (x:Sum) -> u64"; +*/ // NOTE: Fails because of `clone` in the implementation // MixedStruct::get_s