Skip to content

Commit 6ff9711

Browse files
authored
Merge pull request #1958 from GaloisInc/T1929
Support ghost values in all language backends
2 parents b002a93 + 3fa027c commit 6ff9711

File tree

49 files changed

+603
-383
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+603
-383
lines changed

CHANGES.md

+7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
generating LLVM setup scripts for Cryptol FFI functions with the
1818
`llvm_ffi_setup` command. For more information, see the [manual](https://github.com/GaloisInc/saw-script/blob/master/doc/manual/manual.md#verifying-cryptol-ffi-functions).
1919

20+
* Ghost state is now supported with the JVM and MIR language backends:
21+
* The `llvm_declare_ghost_state` command is now deprecated in favor of the
22+
new `declare_ghost_state` command, as nothing about this command is
23+
LLVM-specific.
24+
* Add `jvm_ghost_value` and `mir_ghost_value` commands in addition to the
25+
existing `llvm_ghost_value` command.
26+
2027
# Version 1.0 -- 2023-06-26
2128

2229
## New Features

crucible-mir-comp/src/Mir/Compositional/Builder.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -641,8 +641,8 @@ substMethodSpec sc sm ms = do
641641
MS.SetupCond_Equal loc <$> goSetupValue sv1 <*> goSetupValue sv2
642642
goSetupCondition (MS.SetupCond_Pred loc tt) =
643643
MS.SetupCond_Pred loc <$> goTypedTerm tt
644-
goSetupCondition (MS.SetupCond_Ghost b loc gg tt) =
645-
MS.SetupCond_Ghost b loc gg <$> goTypedTerm tt
644+
goSetupCondition (MS.SetupCond_Ghost loc gg tt) =
645+
MS.SetupCond_Ghost loc gg <$> goTypedTerm tt
646646

647647
goSetupSlice (MirSetupSliceRaw ref len) =
648648
MirSetupSliceRaw <$> goSetupValue ref <*> goSetupValue len

crucible-mir-comp/src/Mir/Compositional/Override.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,7 @@ condTerm sc (MS.SetupCond_Pred md tt) = do
581581
sub <- use MS.termSub
582582
t' <- liftIO $ SAW.scInstantiateExt sc sub $ SAW.ttTerm tt
583583
return (md, t')
584-
condTerm _ (MS.SetupCond_Ghost _ _ _ _) = do
584+
condTerm _ (MS.SetupCond_Ghost _ _ _) = do
585585
error $ "learnCond: SetupCond_Ghost is not supported"
586586

587587

doc/manual/manual.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -3541,18 +3541,19 @@ thought of as additional global state that is visible only to the
35413541
verifier. Ghost state with a given name can be declared at the top level
35423542
with the following function:
35433543
3544-
* `llvm_declare_ghost_state : String -> TopLevel Ghost`
3544+
* `declare_ghost_state : String -> TopLevel Ghost`
35453545
35463546
Ghost state variables do not initially have any particluar type, and can
35473547
store data of any type. Given an existing ghost variable the following
3548-
function can be used to specify its value:
3548+
functions can be used to specify its value:
35493549
35503550
* `llvm_ghost_value : Ghost -> Term -> LLVMSetup ()`
3551+
* `jvm_ghost_value : Ghost -> Term -> JVMSetup ()`
3552+
* `mir_ghost_value : Ghost -> Term -> MIRSetup ()`
35513553
3552-
Currently, this function can only be used for LLVM verification, though
3553-
that will likely be generalized in the future. It can be used in either
3554-
the pre state or the post state, to specify the value of ghost state
3555-
either before or after the execution of the function, respectively.
3554+
These can be used in either the pre state or the post state, to specify the
3555+
value of ghost state either before or after the execution of the function,
3556+
respectively.
35563557
35573558
## An Extended Example
35583559

doc/manual/manual.pdf

-30 Bytes
Binary file not shown.

examples/ghost/ghost.saw

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ let example_spec counter : CrucibleSetup () = do {
1616
};
1717

1818
let main : TopLevel () = do {
19-
counter <- llvm_declare_ghost_state "ctr";
19+
counter <- declare_ghost_state "ctr";
2020

2121
m <- llvm_load_module "simple.bc";
2222
next <- llvm_unsafe_assume_spec m "next" (next_spec counter);

intTests/test_ghost/test.saw

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ let f_spec (counter : Ghost) : CrucibleSetup () = do {
2626
};
2727

2828
let main : TopLevel () = do {
29-
counter <- llvm_declare_ghost_state "counter";
29+
counter <- declare_ghost_state "counter";
3030
m <- llvm_load_module "test.bc";
3131
get_and_increment_ov <-
3232
llvm_unsafe_assume_spec m "get_and_increment" (get_and_increment_spec counter);

intTests/test_ghost_branch_00/test.saw

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let g_spec : CrucibleSetup () = do {
2525
};
2626

2727
let main : TopLevel () = do {
28-
x <- llvm_declare_ghost_state "x";
28+
x <- declare_ghost_state "x";
2929
m <- llvm_load_module "test.bc";
3030
f_lt_ov <- llvm_unsafe_assume_spec m "f" (f_lt_spec x);
3131
f_ge_ov <- llvm_unsafe_assume_spec m "f" (f_ge_spec x);

intTests/test_ghost_branch_01/test.saw

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ let h_spec : CrucibleSetup () = do {
2828
};
2929

3030
let main : TopLevel () = do {
31-
x <- llvm_declare_ghost_state "x";
31+
x <- declare_ghost_state "x";
3232
m <- llvm_load_module "test.bc";
3333
get_ov <- llvm_unsafe_assume_spec m "get" (get_spec x);
3434
f_ov <- llvm_unsafe_assume_spec m "f" (f_spec x);

intTests/test_ghost_branch_02/test.saw

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let g_spec : CrucibleSetup () = do {
2121
};
2222

2323
let main : TopLevel () = do {
24-
x <- llvm_declare_ghost_state "x";
24+
x <- declare_ghost_state "x";
2525
m <- llvm_load_module "test.bc";
2626
f_lt_ov <- llvm_unsafe_assume_spec m "f" (f_lt_spec x);
2727
f_ge_ov <- llvm_unsafe_assume_spec m "f" (f_ge_spec x);

intTests/test_ghost_branch_03/test.saw

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ let h_spec : CrucibleSetup () = do {
2828
};
2929

3030
let main : TopLevel () = do {
31-
x <- llvm_declare_ghost_state "x";
31+
x <- declare_ghost_state "x";
3232
m <- llvm_load_module "test.bc";
3333
get_ov <- llvm_unsafe_assume_spec m "get" (get_spec x);
3434
f_ov <- llvm_unsafe_assume_spec m "f" (f_spec x);

intTests/test_ghost_types_00/test.saw

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ let h_spec : CrucibleSetup () = do {
2323
};
2424

2525
let main : TopLevel () = do {
26-
x <- llvm_declare_ghost_state "x";
26+
x <- declare_ghost_state "x";
2727
m <- llvm_load_module "test.bc";
2828
f_ov <- llvm_unsafe_assume_spec m "f" (f_spec x);
2929
// This spec should probably use a different variable, but doesn't:

intTests/test_mir_ghost/Makefile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
all: test.linked-mir.json
2+
3+
test.linked-mir.json: test.rs
4+
saw-rustc $<
5+
$(MAKE) remove-unused-build-artifacts
6+
7+
.PHONY: remove-unused-build-artifacts
8+
remove-unused-build-artifacts:
9+
rm -f test libtest.mir libtest.rlib
10+
11+
.PHONY: clean
12+
clean: remove-unused-build-artifacts
13+
rm -f test.linked-mir.json

intTests/test_mir_ghost/test.linked-mir.json

+1
Large diffs are not rendered by default.

intTests/test_mir_ghost/test.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pub fn next() -> u32 {
2+
unimplemented!("This should be overridden")
3+
}
4+
5+
pub fn example() -> u32 {
6+
next();
7+
next();
8+
next()
9+
}

intTests/test_mir_ghost/test.saw

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
enable_experimental;
2+
3+
let next_spec counter = do {
4+
n <- mir_fresh_var "n" mir_u32;
5+
mir_ghost_value counter n;
6+
7+
mir_execute_func [];
8+
9+
mir_ghost_value counter {{n+1}};
10+
mir_return (mir_term {{n}});
11+
};
12+
13+
let example_spec counter = do {
14+
n <- mir_fresh_var "nm" mir_u32;
15+
mir_precond {{n < 2}};
16+
mir_ghost_value counter n;
17+
18+
mir_execute_func [];
19+
20+
mir_ghost_value counter {{n+3}};
21+
mir_return (mir_term {{n+2}});
22+
};
23+
24+
counter <- declare_ghost_state "ctr";
25+
m <- mir_load_module "test.linked-mir.json";
26+
27+
next <- mir_unsafe_assume_spec m "test::next" (next_spec counter);
28+
mir_verify m "test::example" [next] false (example_spec counter) z3;

intTests/test_mir_ghost/test.sh

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set -e
2+
3+
$SAW test.saw
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
all: test.linked-mir.json
2+
3+
test.linked-mir.json: test.rs
4+
saw-rustc $<
5+
$(MAKE) remove-unused-build-artifacts
6+
7+
.PHONY: remove-unused-build-artifacts
8+
remove-unused-build-artifacts:
9+
rm -f test libtest.mir libtest.rlib
10+
11+
.PHONY: clean
12+
clean: remove-unused-build-artifacts
13+
rm -f test.linked-mir.json
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"fns":[{"abi":{"kind":"Rust"},"args":[{"is_zst":false,"mut":{"kind":"Not"},"name":"_1","ty":"ty::bool"}],"body":{"blocks":[{"block":{"data":[{"kind":"Assign","lhs":{"data":[],"var":{"is_zst":false,"mut":{"kind":"Mut"},"name":"_2","ty":"ty::bool"}},"pos":"test.rs:5:8: 5:9","rhs":{"kind":"Use","usevar":{"data":{"data":[],"var":{"is_zst":false,"mut":{"kind":"Not"},"name":"_1","ty":"ty::bool"}},"kind":"Copy"}}}],"terminator":{"discr":{"data":{"data":[],"var":{"is_zst":false,"mut":{"kind":"Mut"},"name":"_2","ty":"ty::bool"}},"kind":"Move"},"discr_span":"test.rs:5:8: 5:9 !test.rs:5:8: 5:9","kind":"SwitchInt","pos":"test.rs:5:8: 5:9 !test.rs:5:8: 5:9","switch_ty":"ty::bool","targets":["bb2","bb1"],"values":["0"]}},"blockid":"bb0"},{"block":{"data":[],"terminator":{"args":[{"data":{"rendered":{"kind":"uint","size":4,"val":"27"},"ty":"ty::u32"},"kind":"Constant"}],"cleanup":null,"destination":[{"data":[],"var":{"is_zst":true,"mut":{"kind":"Mut"},"name":"_0","ty":"ty::Tuple::e93222e871854c41"}},"bb3"],"from_hir_call":true,"func":{"data":{"rendered":{"kind":"zst"},"ty":"ty::FnDef::140cf1f14525dfe1"},"kind":"Constant"},"kind":"Call","pos":"test.rs:6:9: 6:14"}},"blockid":"bb1"},{"block":{"data":[],"terminator":{"args":[{"data":{"rendered":{"kind":"uint","size":4,"val":"42"},"ty":"ty::u32"},"kind":"Constant"}],"cleanup":null,"destination":[{"data":[],"var":{"is_zst":true,"mut":{"kind":"Mut"},"name":"_0","ty":"ty::Tuple::e93222e871854c41"}},"bb3"],"from_hir_call":true,"func":{"data":{"rendered":{"kind":"zst"},"ty":"ty::FnDef::140cf1f14525dfe1"},"kind":"Constant"},"kind":"Call","pos":"test.rs:8:9: 8:14"}},"blockid":"bb2"},{"block":{"data":[],"terminator":{"kind":"Return","pos":"test.rs:10:2: 10:2"}},"blockid":"bb3"}],"vars":[{"is_zst":true,"mut":{"kind":"Mut"},"name":"_0","ty":"ty::Tuple::e93222e871854c41"},{"is_zst":false,"mut":{"kind":"Mut"},"name":"_2","ty":"ty::bool"}]},"name":"test/2c9d3c4c::g","return_ty":"ty::Tuple::e93222e871854c41","spread_arg":null},{"abi":{"kind":"Rust"},"args":[{"is_zst":false,"mut":{"kind":"Not"},"name":"_1","ty":"ty::u32"}],"body":{"blocks":[{"block":{"data":[],"terminator":{"kind":"Return","pos":"test.rs:2:21: 2:21"}},"blockid":"bb0"}],"vars":[{"is_zst":true,"mut":{"kind":"Mut"},"name":"_0","ty":"ty::Tuple::e93222e871854c41"}]},"name":"test/2c9d3c4c::f","return_ty":"ty::Tuple::e93222e871854c41","spread_arg":null}],"adts":[],"statics":[],"vtables":[],"traits":[],"intrinsics":[{"inst":{"def_id":"test/2c9d3c4c::g","kind":"Item","substs":[]},"name":"test/2c9d3c4c::g"},{"inst":{"def_id":"test/2c9d3c4c::f","kind":"Item","substs":[]},"name":"test/2c9d3c4c::f"}],"tys":[{"name":"ty::bool","ty":{"kind":"Bool"}},{"name":"ty::Tuple::e93222e871854c41","ty":{"kind":"Tuple","tys":[]}},{"name":"ty::FnDef::140cf1f14525dfe1","ty":{"defid":"test/2c9d3c4c::f","kind":"FnDef"}},{"name":"ty::u32","ty":{"kind":"Uint","uintkind":{"kind":"U32"}}}],"roots":["test/2c9d3c4c::f","test/2c9d3c4c::g"]}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Meant to be overridden
2+
pub fn f(_x: u32) {}
3+
4+
pub fn g(b: bool) {
5+
if b {
6+
f(27)
7+
} else {
8+
f(42)
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
enable_experimental;
2+
3+
m <- mir_load_module "test.linked-mir.json";
4+
5+
g <- declare_ghost_state "g";
6+
7+
let f_spec = do {
8+
x <- mir_fresh_var "x" mir_u32;
9+
10+
mir_execute_func [mir_term x];
11+
12+
mir_ghost_value g x;
13+
};
14+
15+
let g_spec = do {
16+
b <- mir_fresh_var "b" mir_bool;
17+
18+
mir_execute_func [mir_term b];
19+
20+
mir_ghost_value g ({{ if b then 27 else 42 : [32] }});
21+
};
22+
23+
f_ov <- mir_unsafe_assume_spec m "test::f" f_spec;
24+
mir_verify m "test::g" [f_ov] false g_spec z3;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
set -e
2+
3+
$SAW test.saw

saw-remote-api/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
* Add `"slice"` and `"slice range"` `setup value`s representing slices in MIR
3636
verification. Attempting to use these in LLVM or JVM verification will raise
3737
an error.
38+
* The `SAW/create ghost variable` command and the associated
39+
`ghost variable value` value are now supported with JVM and MIR verification.
3840

3941
## 1.0.0 -- 2023-06-26
4042

saw-remote-api/python/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
variable. This function is currently only supported with LLVM and MIR
3939
verification, and using this function with JVM verification will raise an
4040
error.
41+
* The `create_ghost_variable()` and `ghost_value()` functions are now supported
42+
with JVM and MIR verification.
4143

4244
## 1.0.1 -- YYYY-MM-DD
4345

saw-remote-api/python/tests/saw/test-files/mir_ghost.linked-mir.json

+1
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pub fn next() -> u32 {
2+
unimplemented!("This should be overridden")
3+
}
4+
5+
pub fn example() -> u32 {
6+
next();
7+
next();
8+
next()
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import unittest
2+
from pathlib import Path
3+
4+
from saw_client import *
5+
from saw_client.crucible import cry_f
6+
from saw_client.mir import Contract, GhostVariable, u32
7+
8+
9+
class NextContract(Contract):
10+
def __init__(self, counter: GhostVariable) -> None:
11+
super().__init__()
12+
self.counter = counter
13+
14+
def specification(self) -> None:
15+
n = self.fresh_var(u32, 'n')
16+
self.ghost_value(self.counter, n)
17+
18+
self.execute_func()
19+
20+
self.ghost_value(self.counter, cry_f('{n} + 1'))
21+
self.returns(n)
22+
23+
24+
class ExampleContract(Contract):
25+
def __init__(self, counter: GhostVariable) -> None:
26+
super().__init__()
27+
self.counter = counter
28+
29+
def specification(self) -> None:
30+
n = self.fresh_var(u32, 'n')
31+
self.precondition_f('{n} < 2')
32+
self.ghost_value(self.counter, n)
33+
34+
self.execute_func()
35+
self.ghost_value(self.counter, cry_f('{n} + 3'))
36+
self.returns_f('{n} + 2')
37+
38+
39+
class MIRGhostTest(unittest.TestCase):
40+
def test_mir_ghost(self):
41+
connect(reset_server=True)
42+
if __name__ == "__main__": view(LogResults())
43+
json_name = str(Path('tests', 'saw', 'test-files', 'mir_ghost.linked-mir.json'))
44+
mod = mir_load_module(json_name)
45+
46+
counter = create_ghost_variable('ctr');
47+
next_ov = mir_assume(mod, 'mir_ghost::next', NextContract(counter))
48+
example_result = mir_verify(mod, 'mir_ghost::example', ExampleContract(counter), lemmas=[next_ov])
49+
self.assertIs(example_result.is_success(), True)
50+
51+
52+
if __name__ == "__main__":
53+
unittest.main()

saw-remote-api/src/SAWServer/JVMCrucibleSetup.hs

+14-8
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import qualified Data.Map as Map
2626
import qualified Cryptol.Parser.AST as P
2727
import Cryptol.Utils.Ident (mkIdent)
2828
import qualified Lang.Crucible.JVM as CJ
29-
import SAWScript.Crucible.Common.MethodSpec as MS (SetupValue(..))
29+
import qualified SAWScript.Crucible.Common.MethodSpec as MS
3030
import SAWScript.Crucible.JVM.Builtins
3131
( jvm_alloc_array,
3232
jvm_alloc_object,
@@ -35,6 +35,7 @@ import SAWScript.Crucible.JVM.Builtins
3535
jvm_static_field_is,
3636
jvm_execute_func,
3737
jvm_fresh_var,
38+
jvm_ghost_value,
3839
jvm_postcond,
3940
jvm_precond,
4041
jvm_return )
@@ -56,10 +57,11 @@ import SAWServer
5657
import SAWServer.Data.Contract
5758
( PointsTo(PointsTo),
5859
PointsToBitfield,
60+
GhostValue(GhostValue),
5961
Allocated(Allocated),
6062
ContractVar(ContractVar),
61-
Contract(preVars, preConds, preAllocated, prePointsTos, prePointsToBitfields,
62-
argumentVals, postVars, postConds, postAllocated, postPointsTos, postPointsToBitfields,
63+
Contract(preVars, preConds, preAllocated, preGhostValues, prePointsTos, prePointsToBitfields,
64+
argumentVals, postVars, postConds, postAllocated, postGhostValues, postPointsTos, postPointsToBitfields,
6365
returnVal) )
6466
import SAWServer.Data.SetupValue ()
6567
import SAWServer.CryptolExpression (CryptolModuleException(..), getTypedTermOfCExp)
@@ -82,28 +84,29 @@ instance Doc.DescribedMethod StartJVMSetupParams OK where
8284
]
8385
resultFieldDescription = []
8486

85-
newtype ServerSetupVal = Val (SetupValue CJ.JVM)
87+
newtype ServerSetupVal = Val (MS.SetupValue CJ.JVM)
8688

8789
compileJVMContract ::
8890
(FilePath -> IO ByteString) ->
8991
BuiltinContext ->
92+
Map ServerName MS.GhostGlobal ->
9093
CryptolEnv ->
9194
Contract JavaType (P.Expr P.PName) ->
9295
JVMSetupM ()
93-
compileJVMContract fileReader bic cenv0 c =
96+
compileJVMContract fileReader bic ghostEnv cenv0 c =
9497
do allocsPre <- mapM setupAlloc (preAllocated c)
9598
(envPre, cenvPre) <- setupState allocsPre (Map.empty, cenv0) (preVars c)
9699
mapM_ (\p -> getTypedTerm cenvPre p >>= jvm_precond) (preConds c)
97100
mapM_ (setupPointsTo (envPre, cenvPre)) (prePointsTos c)
98101
mapM_ setupPointsToBitfields (prePointsToBitfields c)
99-
--mapM_ (setupGhostValue ghostEnv cenvPre) (preGhostValues c)
102+
mapM_ (setupGhostValue ghostEnv cenvPre) (preGhostValues c)
100103
traverse (getSetupVal (envPre, cenvPre)) (argumentVals c) >>= jvm_execute_func
101104
allocsPost <- mapM setupAlloc (postAllocated c)
102105
(envPost, cenvPost) <- setupState (allocsPre ++ allocsPost) (envPre, cenvPre) (postVars c)
103106
mapM_ (\p -> getTypedTerm cenvPost p >>= jvm_postcond) (postConds c)
104107
mapM_ (setupPointsTo (envPost, cenvPost)) (postPointsTos c)
105108
mapM_ setupPointsToBitfields (postPointsToBitfields c)
106-
--mapM_ (setupGhostValue ghostEnv cenvPost) (postGhostValues c)
109+
mapM_ (setupGhostValue ghostEnv cenvPost) (postGhostValues c)
107110
case returnVal c of
108111
Just v -> getSetupVal (envPost, cenvPost) v >>= jvm_return
109112
Nothing -> return ()
@@ -149,7 +152,10 @@ compileJVMContract fileReader bic cenv0 c =
149152
setupPointsToBitfields _ =
150153
JVMSetupM $ fail "Points-to-bitfield not supported in JVM API."
151154

152-
--setupGhostValue _ _ _ = fail "Ghost values not supported yet in JVM API."
155+
setupGhostValue genv cenv (GhostValue serverName e) =
156+
do g <- resolve genv serverName
157+
t <- getTypedTerm cenv e
158+
jvm_ghost_value g t
153159

154160
resolve :: Map ServerName a -> ServerName -> JVMSetupM a
155161
resolve env name =

0 commit comments

Comments
 (0)