From 8d13b41545c50ae82a5db04786acffbfe82ccfa2 Mon Sep 17 00:00:00 2001 From: Taylor Ball trball Date: Tue, 24 May 2022 16:49:07 +0000 Subject: [PATCH 001/120] initial commit --- saw-python/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 saw-python/README.md diff --git a/saw-python/README.md b/saw-python/README.md new file mode 100644 index 00000000..e69de29b From 063c29995ec5ab7a47f15fb6eeafadc9464f3524 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Tue, 24 May 2022 17:31:21 -0400 Subject: [PATCH 002/120] Update README.md --- saw-python/README.md | 191 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/saw-python/README.md b/saw-python/README.md index e69de29b..30ff8c9f 100644 --- a/saw-python/README.md +++ b/saw-python/README.md @@ -0,0 +1,191 @@ +# Setting Everything Up + +- Get saw-python to run +- directory layout + +# Python Contracts + +Here's the general layout for SAW in Python: + +```python +# Import junk + +class contractName(Contract): + def specification(self): + # Initialization and Preconditions + # Execute Function + # Postconditions and Return + +class testName(unittest.TestCase): + def specificTestName(self): + # Verify contracts +``` + +## Imports + +To start making Python contracts first import the necessary files: + +```python +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * +from saw_client.proofscript import * +from saw_client.llvm_type import * +``` + +We put `*` for simplicity, but if one only wanted certain functions, they could refer to this table for some common imports: + +|Package | Values | +|---------------------|--------| +|`saw_client.crucible`| `cry`, `cry_f`| +|`saw_client.llvm` | `Contract`, `CryptolTerm`, `SetupVal`, `FreshVar`, `i8`, `i32`, `i64`, `void`, `null`, `array_ty`, `field`, `struct`, `alias_ty` | +|`saw_client.llvm_type` | `LLVMType`, `LLVMArrayType` | + +## Right Circular Shift Example + +To see contracts in action we need an example. Here's some C code for right circular shift we want to verify: + +```C +uint32_t RCS(uint32_t bits, uint32_t shift) { + return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); +} +``` + +The first step is to make the corresponding Cryptol specification for right circular shift: + +```cryptol +RCS : [32] -> [32] -> [32] +RCS xs shift = xs >>> shift +``` + +Now let's make our Python contract: + +```python +class RCS_Contract(Contract): + def specification(self): + bits = self.fresh_var(i32, "bits") + shift = self.fresh_var(i32, "shift") + + self.execute_func(bits, shift) + + self.returns_f("{bits} >>> {shift}") +``` + +Every `Contract` object keeps track of the state of symbolic variables internally, so throughout there will be many calls to `self`. + +### Fresh Symbolic Variables + +The command `self.fresh_var(type, nameString)` creates a new symbolic variable of type `type` and name `nameString`. Names for fresh symbolic variables +are not optional inputs, and they mostly serve for error messages. The string can be anything, but it makes sense to give it the name of the variable being defined. + +### Execute Functions +The command `self.execute_func(inputs)` will symbolically execute the function. There should be exactly as many comma separated inputs as there are +in the C function. As usual with SAW, one can only place preconditions before this command and postconditions after this command +(there are no preconditions or postconditions in this contract; we'll discuss those later). + +### Return Statements +The command `self.returns_f(string)` asserts the current function returns the Cryptol term parsed from a Python String. To use Python variables in scope within the string use +`{variable_name}`. For example, + +|`self.`|`returns_f`|`("{bits} >>> {shift}")`| +|-------|-----------|---------------------| +|In this contract| assert the current function returns the Cryptol term | circular shift the bit array `xs` by `shift`| + +Sometimes we don't want to return a Cryptol Term. In this case we can just use `returns(someSetupValue)`. The specification function of a Contract must +**always** have a `self.returns(someSetupValue)` or `self.returns_f(string)` statement. If the function returns `void` one can use `self.returns(void)`. + +### Terms from Cryptol +The `CryptolTerm` class is a subclass of `SetupVal`, so you can use them anywhere you'd use a `SetupVal` in SAW (in non-Python SAW they are disjoint types). + +The command `cry(string)` converts a Python string into a Cryptol Term that can be used in SAW. The `_f` indicates one can pass in Python local variables into the strings by surrounding +the variable with braces as we did in `{bits} >>> {shift}`. In fact, `returns_f` is just syntactic sugar for `returns(cry_f(string))`. + +Braces are sometimes used in Cryptol to assign type parameters or record types. To have the parser parse a literal brace in a Cryptol string repeat the `{` or `}` symbols. +For example, consider the following made up SAW line: + +```python + self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} blah }} == foo `blah") +``` + +We parse the Cryptol string as a boolean assertion between records + +```cryptol +{a = take`{5} 23, b = take`{2} 23} == foo `blah +``` + +where `blah` is a local Python variable equal to `23`, `N` is a local Python variable equal to `2`, `foo` some function returning a record, and `blah` some +Cryptol type in the specification loaded. + +### Preconditions and Postconditions + +The [C specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) has the following to say about bit shifts: + +> If the value of the right operand is negative or is +> greater than or equal to the width of the promoted left operand, the behavior is undefined. + +Due to this, we should probably add a precondition to the previous code: + +```python +class RCS_Contract(Contract): + def specification(self): + xs = self.fresh_var(i32, "xs") + shift = self.fresh_var(i32, "shift") + + self.precondition_f("{shift} <= 32") + + self.execute_func(xs, shift) + + self.returns_f("{xs} >>> {shift}") +``` + +The command `precondition_f(string)` asserts the constraint parsed from the Cryptol string is satisfied before execution. The program will error if you place a +precondition after `execute_func`. Similarly, the `postcondition_f(string)` command asserts the constrained is satisfied after execution. There is also a +positionally agnostic command `proclaim_f(string)`. This latter command can be useful in helper functions (more on this later). + +### Unit Testing + + + +## Pointers + +Let's change our right circular shift function to accept a pointer to an integer instead: + +```C +void RCS(uint32_t *bits, uint32_t shift) { + bits = (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); + return; +} +``` + +Then our contract may change to + +```python +class RCS_Contract(Contract): + def specification(self): + bits = self.fresh_var(i32, "bits") + bits_p = self.alloc(i32, name="bits_p", points_to=bits, read_only=False) + shift = self.fresh_var(i32, "shift") + + self.precondition_f("{shift} <= 32") + + self.execute_func(bits_p, shift) + + self.postcondition_f("bits_p == ") + + self.returns(void) +``` + +## Arrays + +## Structs + +## Verifying Options + +### Assumptions + +Sometimes we wish to assume the validity of functions. For example, we might want to black box a call to a library function. + +### Lemmas + +### Uninterpreting Functions From 2c804932e7d4d60a71345db29a99e4b0e314b8d1 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Wed, 25 May 2022 18:00:16 -0400 Subject: [PATCH 003/120] Update README.md --- saw-python/README.md | 391 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 355 insertions(+), 36 deletions(-) diff --git a/saw-python/README.md b/saw-python/README.md index 30ff8c9f..f8809410 100644 --- a/saw-python/README.md +++ b/saw-python/README.md @@ -3,7 +3,7 @@ - Get saw-python to run - directory layout -# Python Contracts +# Python Contracts Introduction Here's the general layout for SAW in Python: @@ -59,7 +59,7 @@ RCS : [32] -> [32] -> [32] RCS xs shift = xs >>> shift ``` -Now let's make our Python contract: +Now let's make our Python `Contract` object with the required `specificaiton` function: ```python class RCS_Contract(Contract): @@ -72,7 +72,7 @@ class RCS_Contract(Contract): self.returns_f("{bits} >>> {shift}") ``` -Every `Contract` object keeps track of the state of symbolic variables internally, so throughout there will be many calls to `self`. +Let's break down `specification` piece by piece. ### Fresh Symbolic Variables @@ -81,11 +81,10 @@ are not optional inputs, and they mostly serve for error messages. The string ca ### Execute Functions The command `self.execute_func(inputs)` will symbolically execute the function. There should be exactly as many comma separated inputs as there are -in the C function. As usual with SAW, one can only place preconditions before this command and postconditions after this command -(there are no preconditions or postconditions in this contract; we'll discuss those later). +in the C function. One can only place preconditions before this command and postconditions after this command. ### Return Statements -The command `self.returns_f(string)` asserts the current function returns the Cryptol term parsed from a Python String. To use Python variables in scope within the string use +The command `self.returns_f(string)` asserts the current function returns the Cryptol term parsed from a Python string. To use Python variables in scope within the string use `{variable_name}`. For example, |`self.`|`returns_f`|`("{bits} >>> {shift}")`| @@ -108,21 +107,15 @@ For example, consider the following made up SAW line: self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} blah }} == foo `blah") ``` -We parse the Cryptol string as a boolean assertion between records +Suppose `blah` is a local Python variable equal to `23` and `N` is a local Python variable equal to `2`. Then we parse the Cryptol string as the following boolean assertion between records: ```cryptol {a = take`{5} 23, b = take`{2} 23} == foo `blah ``` -where `blah` is a local Python variable equal to `23`, `N` is a local Python variable equal to `2`, `foo` some function returning a record, and `blah` some +where `foo` is some Cryptol function returning a record, and `blah` is some Cryptol type in the specification loaded. -### Preconditions and Postconditions - -The [C specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) has the following to say about bit shifts: - -> If the value of the right operand is negative or is -> greater than or equal to the width of the promoted left operand, the behavior is undefined. Due to this, we should probably add a precondition to the previous code: @@ -143,49 +136,375 @@ The command `precondition_f(string)` asserts the constraint parsed from the Cryp precondition after `execute_func`. Similarly, the `postcondition_f(string)` command asserts the constrained is satisfied after execution. There is also a positionally agnostic command `proclaim_f(string)`. This latter command can be useful in helper functions (more on this later). -### Unit Testing +## Unit Testing - +The [C specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) has the following to say about bit shifts: -## Pointers +> If the value of the right operand is negative or is +> greater than or equal to the width of the promoted left operand, the behavior is undefined. -Let's change our right circular shift function to accept a pointer to an integer instead: +Hopefully SAW alerts us of this behavior. To check, we need to make a test: -```C -void RCS(uint32_t *bits, uint32_t shift) { - bits = (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); - return; -} +```python +class RCSTest(unittest.TestCase): + def test_RCS(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + bcname = "/some/path/to/your/file.bc" + mod = llvm_load_module(bcname) + + cryname = "/some/path/to/your/file.cry" + cryptol_load_file(cryname) + + RCS_result = llvm_verify(mod, 'RCS', RCS_Contract()) + self.assertIs(RCS_result.is_success(), True) ``` -Then our contract may change to +For a contract the specification function should be called `specification`. For tests, it doesn't matter what you name your tests. Here we named it `test_RCS`. These tests will be ran when you try running the Python. + +Let's break down the first few lines of this function: + +- The command `connect(reset_server=True)` connects to the server so we can use the SAW Python API +- The line `if __name__ == "__main__": view(LogResults(verbose_failure=True))` allows us to view the output with verbose error messages. If you don't want verbose error messages, then just use `if __name__ == "__main__": view(LogResults())` +- The line `bcname = "/some/path/to/your/file.bc"` declares which bitcode file we're analyzing. If you have multiple bitcodes files, then make a variable for each file. +- The line `mod = llvm_load_module(bcname)` creates the object we will pass to verification that represents the bitcode. +- The line `cryname = "/some/path/to/your/file.cry"` specifies the path to the Cryptol specification. +- The line `cryptol_load_file(cryname)` loads the Cryptol specification. + +Now that we've set up our environment, let's actually verify our contract! This is done at the line + +|`RCS_result =` | `llvm_verify(` | `mod,` | `'RCS',` | `RCS_Contract()`| `)`| +|----------|-----------|----|------|---------------|----| +|Assign this variable| to the result of trying to verify| the bitcode| function with this name| using this contract|.| + +Now that we have the result, we want to assert this result succeeded using `self.assertIs(RCS_result.is_success(), True)`. + + +## Debugging C with SAW + +The full code is: + +```python3 +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * +from saw_client.proofscript import * +from saw_client.llvm_type import * -```python class RCS_Contract(Contract): def specification(self): - bits = self.fresh_var(i32, "bits") - bits_p = self.alloc(i32, name="bits_p", points_to=bits, read_only=False) + xs = self.fresh_var(i32, "xs") shift = self.fresh_var(i32, "shift") self.precondition_f("{shift} <= 32") - self.execute_func(bits_p, shift) + self.execute_func(xs, shift) + + self.returns_f("{xs} >>> {shift}") - self.postcondition_f("bits_p == ") +class RCSTest(unittest.TestCase): + def test_RCS(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + bcname = "/some/path/to/your/file.bc" + cryname = "/some/path/to/your/file.cry" + + cryptol_load_file(cryname) + mod = llvm_load_module(bcname) + + RCS_result = llvm_verify(mod, 'RCS', RCS_Contract()) + self.assertIs(RCS_result.is_success(), True) +``` + +Running the code with + +`command` + +we get the output + +``` +put output here +``` + +As expected, this alerts us of a bug: + +``` + +``` + +One remedy to this is the following: + +``` +uint32_t RCS(uint32_t bits, uint32_t shift) { + shift %= 64 + if(shift == 0) return bits; + return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); +} +``` + +Running SAW gives: + +``` + +``` + +Aha! We forgot about the case when `shift` is zero! Let's try again with + +``` +uint32_t RCS(uint32_t bits, uint32_t shift) { + shift %= 64 + if(shift == 0) return bits; + return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); +} +``` + +Finally, SAW is happy! + +``` + +``` + +# Pointers, Arrays, and Structs + +## Pointers and Arrays + +We'll begin by writing a function that given two arrays adds the second to the first. One way to write this is to +write a function that just mutates the first array and returns nothing: + +```C + +``` + +An alternative way to do this would be to create a new array storing the result and return it: + +```C + +``` + +Finally, we could mutate the first input and return it: + +```C + +``` + +The corresponding Cryptol specification is: + +```cryptol + +``` + +### Initializing Arrays and Pointers + +To initialize the arrays and pointers we'll use the `alloc` command and `array_ty` type: + +```python +def Array5AddMutate_Contract(Contract): + def specification(self): + a = self.fresh_var(array_ty(5, i32), "a") + a_p = self.alloc(array_ty(5, i32), points_to=a) + b = self.fresh_var(array_ty(5, i32), "b") + b_p = self.alloc(array_ty(5, i32), points_to=b, read_only=True) + + self.execute_func(a_p, b_p) + + self.points_to(a_p, cry_f("rowAdd {a} {b}")) + + self.returns(void) +``` + +The `array_ty(type, length)` command creates a type representing an array with entries of type `type` and length `length`. The `alloc` command will initialize a symbolic pointer. Let's break down the initialization of `b_p`: + +| `b_p` | `self.alloc(` | `array_ty(5, i32)`|`,`|`points_to=b`|`,`|`read_only=True`|`)`| +|-------|---------------|-------------------|---|-------------|---|----------------|---| +| Assign this variable to | a symbolic pointer in the current contract | that has the following type | and | points to this object | with | read only permissions| . | + +In C arrays are passed as pointers to the first element of the array, so when we call `execute_func` we supply `a_p` and `b_p` rather than `a` and `b`. + +To verify correctness, we assert that after function exectution `a_p` points to what the Cryptol specification claims it should using `self.points_to(a_p, cry_f("rowAdd {a} {b}"))`. + +Finally, as mentioned, a specification must always have a `self.returns` or `self.returns_f` line. Since our function returns nothing, we use `self.returns(void)`. + +### Helper Functions + +To limit code reuse we can define helper functions in Python. For example, the following construct is often used: + +```python +def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None, read_only : Optional[bool] = False) -> Tuple[FreshVar, SetupVal]: + var = c.fresh_var(ty, name) + ptr = c.alloc(ty, points_to = var, read_only=read_only) + return (var, ptr) +``` + +Given a contract and a type this function outputs a tuple `(var, ptr)` where `var` is a fresh symbolic variable of the given type and `ptr` is a pointer pointing to this variable. We give optional arguments to name the fresh symbolic variable and to set the pointer to read_only. + +To see this in action let's rewrite our previous contract using this: + +```python +def Array5AddNewVar_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + self.points_to(a_p, cry_f("rowAdd {a} {b}")) self.returns(void) ``` -## Arrays +### Auxilary Variables and Post Conditions + +A SAW contract is strictly divided into three parts: +1. Preconditions +2. Execution +3. Postconditions + +SAW will complain if you place a precondition after `execute_func` and similarly for postcondition. If a function returns a value that was not passed through `execute_func`, then you will have to initialize new fresh symbolic variables. For example, consider contracts for the next two C functions: + +```python +def Array5AddNewVar_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + (c, c_p) = ptr_to_fresh(self, array_ty(5, i32), name="c") + self.points_to(c_p, cry_f("rowAdd {a} {b}")) + + self.returns(c_p) + +def Array5AddAlias_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + (aPost, aPost_p) = ptr_to_fresh(self, array_ty(5, i32), name="aPost") + self.postcondition_f("{aPost} == rowAdd {a} {b}") + + self.returns(aPost_p) +``` + +One could replace `self.postcondition_f("{aPost} == rowAdd {a} {b}")` with `self.points_to(aPost_p, cry_f("rowAdd {a} {b}"))`. A SAW symbolic array translates into a Cryptol array and a Cryptol array translates into a SAW symbolic array. + +### Parameterized Contracts + +Suppose our C code was written as + +```C + +``` + +where the length of the array is not specified. The corresponding Cryptol code might be + +```cryptol + +``` + +SAW does not have inductive reasoning capabilities. Suppose later in our program we realize we want to verify the above function not just for arrays of size 5, but also those of a different fixed size, say 10. One option would be to just make a new contract. But then later we realize we need to verify the function with arrays of a different fixed size, say 14. Rewriting the same contract over and over is a waste of code space. Instead, we could supply parameters to the Contract and write it once: + +```python +def arrayAddNewVar_Contract(Contract): + def __init__(self, length : int): + super().__init__() + self.length = length + + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(length, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(length, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + self.points_to(a_p, cry_f("rowAdd {a} {b}")) + + self.returns(void) +``` + +Then we have a unit test for each size: + +```python +class ArrayTests(unittest.TestCase): + def test_rowAdds(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + bcname = "/some/path/to/your/file.bc" + cryname = "/some/path/to/your/file.cry" + + cryptol_load_file(cryname) + mod = llvm_load_module(bcname) + + arrayAddNewVar05_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(5)) + arrayAddNewVar10_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(10)) + arrayAddNewVar15_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(15)) + self.assertIs(arrayAddNewVar05_result.is_success(), True) + self.assertIs(arrayAddNewVar10_result.is_success(), True) + self.assertIs(arrayAddNewVar15_result.is_success(), True) +``` + +### Explicit Arrays + +Another way to initialize arrays is through the `array` command. For example, suppose I have the following C function (taken from [here](https://github.com/GaloisInc/saw-script/blob/337ca6c9edff3bcbcd6924289471abd5ee714c82/saw-remote-api/python/tests/saw/test-files/llvm_array_swap.c)): + +```C +#include + +void array_swap(uint32_t a[2]) { + uint32_t tmp = a[0]; + a[0] = a[1]; + a[1] = tmp; +} +``` + +Here's a contract that doesn't even require Cryptol (taken from [here](https://github.com/GaloisInc/saw-script/blob/337ca6c9edff3bcbcd6924289471abd5ee714c82/saw-remote-api/python/tests/saw/test_llvm_array_swap.py)): + +```python +class ArraySwapContract(Contract): + def specification(self): + a0 = self.fresh_var(i32, "a0") + a1 = self.fresh_var(i32, "a1") + a = self.alloc(array_ty(2, i32), points_to=array(a0, a1)) + + self.execute_func(a) + + self.points_to(a[0], a1) + self.points_to(a[1], a0) + self.returns(void) +``` + +Explicit arrays can be useful when you want to assert a condition on a particular element of the array. Of course, a drawback is you have to initialize every member of the array. However, this can be problematic for large arrays. Ideally, one would just have to declare the array implicitly as before and then pass this array to a Cryptol specification for postconditions as was done in the previous examples. + + +## Structs + +Consider the following struct: + +```C +typedef struct { + uint32_t *vector; + uint32_t size; +} +``` + + +### Struct Initialization + + + +### Structs in Cryptol + + -## Structs +### Struct Helper Functions -## Verifying Options -### Assumptions -Sometimes we wish to assume the validity of functions. For example, we might want to black box a call to a library function. +# Assumptions and Lemmas -### Lemmas -### Uninterpreting Functions +# Uninterpreting Functions From 78ab4fce72bddc61a549553b30f89bf4b5252b61 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Thu, 26 May 2022 12:34:29 -0400 Subject: [PATCH 004/120] Update README.md --- saw-python/README.md | 250 +++++++++++++++++++++++++++++++++---------- 1 file changed, 194 insertions(+), 56 deletions(-) diff --git a/saw-python/README.md b/saw-python/README.md index f8809410..5510ad37 100644 --- a/saw-python/README.md +++ b/saw-python/README.md @@ -52,14 +52,14 @@ uint32_t RCS(uint32_t bits, uint32_t shift) { } ``` -The first step is to make the corresponding Cryptol specification for right circular shift: +and the corresponding Cryptol specification: ```cryptol RCS : [32] -> [32] -> [32] RCS xs shift = xs >>> shift ``` -Now let's make our Python `Contract` object with the required `specificaiton` function: +For the SAW Python API we make a `Contract` object with the required `specificaiton` function: ```python class RCS_Contract(Contract): @@ -69,37 +69,38 @@ class RCS_Contract(Contract): self.execute_func(bits, shift) - self.returns_f("{bits} >>> {shift}") + self.returns_f("RCS {bits} {shift}") ``` Let's break down `specification` piece by piece. ### Fresh Symbolic Variables -The command `self.fresh_var(type, nameString)` creates a new symbolic variable of type `type` and name `nameString`. Names for fresh symbolic variables +The command `self.fresh_var(type, name)` creates a new symbolic variable of type `type` and name `name`, where `name` is a string. Names for fresh symbolic variables are not optional inputs, and they mostly serve for error messages. The string can be anything, but it makes sense to give it the name of the variable being defined. ### Execute Functions -The command `self.execute_func(inputs)` will symbolically execute the function. There should be exactly as many comma separated inputs as there are +The command `self.execute_func(input1, input2, ...)` will symbolically execute the function. There should be exactly as many comma separated inputs as there are in the C function. One can only place preconditions before this command and postconditions after this command. ### Return Statements The command `self.returns_f(string)` asserts the current function returns the Cryptol term parsed from a Python string. To use Python variables in scope within the string use `{variable_name}`. For example, -|`self.`|`returns_f`|`("{bits} >>> {shift}")`| -|-------|-----------|---------------------| -|In this contract| assert the current function returns the Cryptol term | circular shift the bit array `xs` by `shift`| +|`self.`|`returns_f(`|`"RCS {bits} {shift}"`|)| +|-------|-----------|---------------------|----| +|In this contract| assert the current function returns the Cryptol term | right circular shift `bits` by `shift` |.| -Sometimes we don't want to return a Cryptol Term. In this case we can just use `returns(someSetupValue)`. The specification function of a Contract must +Sometimes we don't want to return a Cryptol term. In this case we can just use `returns(someSetupValue)`. The specification function of a Contract must **always** have a `self.returns(someSetupValue)` or `self.returns_f(string)` statement. If the function returns `void` one can use `self.returns(void)`. ### Terms from Cryptol -The `CryptolTerm` class is a subclass of `SetupVal`, so you can use them anywhere you'd use a `SetupVal` in SAW (in non-Python SAW they are disjoint types). -The command `cry(string)` converts a Python string into a Cryptol Term that can be used in SAW. The `_f` indicates one can pass in Python local variables into the strings by surrounding +The command `cry(string)` converts a Python string into a `CryptolTerm` that can be used in SAW. The `_f` indicates one can pass in Python local variables into the strings by surrounding the variable with braces as we did in `{bits} >>> {shift}`. In fact, `returns_f` is just syntactic sugar for `returns(cry_f(string))`. +The `CryptolTerm` class is a subclass of `SetupVal`. This allows using `CryptolTerm` as a `SetupVal`. + Braces are sometimes used in Cryptol to assign type parameters or record types. To have the parser parse a literal brace in a Cryptol string repeat the `{` or `}` symbols. For example, consider the following made up SAW line: @@ -107,7 +108,7 @@ For example, consider the following made up SAW line: self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} blah }} == foo `blah") ``` -Suppose `blah` is a local Python variable equal to `23` and `N` is a local Python variable equal to `2`. Then we parse the Cryptol string as the following boolean assertion between records: +Suppose `blah` is a local Python variable equal to `23` and `N` is a local Python variable equal to `2`. The above string pasrses in Cryptol as ```cryptol {a = take`{5} 23, b = take`{2} 23} == foo `blah @@ -116,33 +117,8 @@ Suppose `blah` is a local Python variable equal to `23` and `N` is a local Pytho where `foo` is some Cryptol function returning a record, and `blah` is some Cryptol type in the specification loaded. - -Due to this, we should probably add a precondition to the previous code: - -```python -class RCS_Contract(Contract): - def specification(self): - xs = self.fresh_var(i32, "xs") - shift = self.fresh_var(i32, "shift") - - self.precondition_f("{shift} <= 32") - - self.execute_func(xs, shift) - - self.returns_f("{xs} >>> {shift}") -``` - -The command `precondition_f(string)` asserts the constraint parsed from the Cryptol string is satisfied before execution. The program will error if you place a -precondition after `execute_func`. Similarly, the `postcondition_f(string)` command asserts the constrained is satisfied after execution. There is also a -positionally agnostic command `proclaim_f(string)`. This latter command can be useful in helper functions (more on this later). - ## Unit Testing -The [C specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) has the following to say about bit shifts: - -> If the value of the right operand is negative or is -> greater than or equal to the width of the promoted left operand, the behavior is undefined. - Hopefully SAW alerts us of this behavior. To check, we need to make a test: ```python @@ -198,11 +174,9 @@ class RCS_Contract(Contract): xs = self.fresh_var(i32, "xs") shift = self.fresh_var(i32, "shift") - self.precondition_f("{shift} <= 32") - self.execute_func(xs, shift) - self.returns_f("{xs} >>> {shift}") + self.returns_f("RCS {xs} {shift}") class RCSTest(unittest.TestCase): def test_RCS(self): @@ -229,6 +203,11 @@ we get the output put output here ``` +SAW alerted us about potentially undefined behavior mentioned in the [C specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) has the following to say about bit shifts: + +> If the value of the right operand is negative or is +> greater than or equal to the width of the promoted left operand, the behavior is undefined. + As expected, this alerts us of a bug: ``` @@ -240,7 +219,6 @@ One remedy to this is the following: ``` uint32_t RCS(uint32_t bits, uint32_t shift) { shift %= 64 - if(shift == 0) return bits; return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); } ``` @@ -315,17 +293,18 @@ def Array5AddMutate_Contract(Contract): self.returns(void) ``` -The `array_ty(type, length)` command creates a type representing an array with entries of type `type` and length `length`. The `alloc` command will initialize a symbolic pointer. Let's break down the initialization of `b_p`: +- The `array_ty(type, length)` command creates a type representing an array with entries of type `type` and length `length`. +- The `alloc` command will initialize a symbolic pointer. Let's break down the initialization of `b_p`: | `b_p` | `self.alloc(` | `array_ty(5, i32)`|`,`|`points_to=b`|`,`|`read_only=True`|`)`| |-------|---------------|-------------------|---|-------------|---|----------------|---| | Assign this variable to | a symbolic pointer in the current contract | that has the following type | and | points to this object | with | read only permissions| . | -In C arrays are passed as pointers to the first element of the array, so when we call `execute_func` we supply `a_p` and `b_p` rather than `a` and `b`. +Since arrays are passed as pointers in C, when we call `execute_func` we supply `a_p` and `b_p` rather than `a` and `b`. To verify correctness, we assert that after function exectution `a_p` points to what the Cryptol specification claims it should using `self.points_to(a_p, cry_f("rowAdd {a} {b}"))`. -Finally, as mentioned, a specification must always have a `self.returns` or `self.returns_f` line. Since our function returns nothing, we use `self.returns(void)`. +A specification must contain a `self.returns` or `self.returns_f` line. Since our function returns nothing, we use `self.returns(void)`. ### Helper Functions @@ -338,12 +317,12 @@ def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None, read_ return (var, ptr) ``` -Given a contract and a type this function outputs a tuple `(var, ptr)` where `var` is a fresh symbolic variable of the given type and `ptr` is a pointer pointing to this variable. We give optional arguments to name the fresh symbolic variable and to set the pointer to read_only. +Given a contract and a type this function outputs a tuple `(var, ptr)` where `var` is a fresh symbolic variable of the given type and `ptr` is a pointer pointing to this variable. We give optional arguments to name the fresh symbolic variable and to force read only pointer constraints. -To see this in action let's rewrite our previous contract using this: +To see this in action, let's rewrite our previous contract: ```python -def Array5AddNewVar_Contract(Contract): +def Array5AddMutate_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) @@ -355,28 +334,55 @@ def Array5AddNewVar_Contract(Contract): self.returns(void) ``` -### Auxilary Variables and Post Conditions +### Auxilary Variables A SAW contract is strictly divided into three parts: 1. Preconditions 2. Execution 3. Postconditions -SAW will complain if you place a precondition after `execute_func` and similarly for postcondition. If a function returns a value that was not passed through `execute_func`, then you will have to initialize new fresh symbolic variables. For example, consider contracts for the next two C functions: +SAW will complain if you place a precondition after `execute_func` and similarly for postcondition. If a function returns a value that was not passed through `execute_func`, then you will have to initialize new fresh symbolic variables. For example, consider the proposed contract for the next C function: ```python def Array5AddNewVar_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + (c, c_p) = ptr_to_fresh(self, array_ty(5, i32), name="c") self.execute_func(a_p, b_p) - (c, c_p) = ptr_to_fresh(self, array_ty(5, i32), name="c") self.points_to(c_p, cry_f("rowAdd {a} {b}")) self.returns(c_p) +``` + +Running a unit test yields the following error message: + +``` +SAW error message +``` + +Think about the precondition block, the part before `execute_func`, as setting up the symbolic variables **before** they enter the function. With this in mind, it doesn't make sense to declare `c_p` in this block because `c_p` is defined **within** the C function. The fix to the previous contract is moving the declaration of `c_p` to the postcondition block: + +```python +def Array5AddNewVar_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + (c, c_p) = ptr_to_fresh(self, array_ty(5, i32), name="c") + self.points_to(c_p, cry_f("rowAdd {a} {b}")) + + self.returns(c_p) +``` +### Postconditions and `points_to` + +Consider a contract for the third C function: + +```python def Array5AddAlias_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") @@ -406,7 +412,7 @@ where the length of the array is not specified. The corresponding Cryptol code m ``` -SAW does not have inductive reasoning capabilities. Suppose later in our program we realize we want to verify the above function not just for arrays of size 5, but also those of a different fixed size, say 10. One option would be to just make a new contract. But then later we realize we need to verify the function with arrays of a different fixed size, say 14. Rewriting the same contract over and over is a waste of code space. Instead, we could supply parameters to the Contract and write it once: +SAW does not have inductive reasoning capabilities. Suppose later in our program we realize we want to verify the above function not just for arrays of size 5, but also those of a different fixed size, say 10. One option would be to just make a new contract. However, rewriting the same contract over and over is a waste of code. Instead, we could supply parameters to the Contract and write it once: ```python def arrayAddNewVar_Contract(Contract): @@ -441,11 +447,97 @@ class ArrayTests(unittest.TestCase): arrayAddNewVar05_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(5)) arrayAddNewVar10_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(10)) - arrayAddNewVar15_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(15)) self.assertIs(arrayAddNewVar05_result.is_success(), True) self.assertIs(arrayAddNewVar10_result.is_success(), True) - self.assertIs(arrayAddNewVar15_result.is_success(), True) ``` + +### Array Example Full Code + +```python +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * +from saw_client.proofscript import * +from saw_client.llvm_type import * + +def array5AddMutate_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + self.points_to(a_p, cry_f("rowAdd {a} {b}")) + + self.returns(void) + +def array5AddNewVar_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + (c, c_p) = ptr_to_fresh(self, array_ty(5, i32), name="c") + self.points_to(c_p, cry_f("rowAdd {a} {b}")) + + self.returns(c_p) + +def arrayAddNewVar_Contract(Contract): + def __init__(self, length : int): + super().__init__() + self.length = length + + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(length, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(length, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + self.points_to(a_p, cry_f("rowAdd {a} {b}")) + + self.returns(void) + +def array5AddAlias_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + (aPost, aPost_p) = ptr_to_fresh(self, array_ty(5, i32), name="aPost") + self.postcondition_f("{aPost} == rowAdd {a} {b}") + + self.returns(aPost_p) + +class ArrayTests(unittest.TestCase): + def test_rowAdds(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + bcname = "/some/path/to/your/file.bc" + cryname = "/some/path/to/your/file.cry" + + cryptol_load_file(cryname) + mod = llvm_load_module(bcname) + + arrayAddNewMutate_result = llvm_verify(mod, 'arrayAddMutate', array5AddMutate_Contract()) + arrayAddNewVar5_result = llvm_verify(mod, 'arrayAddNewVar', array5AddNewVar_Contract()) + arrayAddNewVar05_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(5)) + arrayAddNewVar10_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(10)) + array5AddAlias_result = llvm_verify(mod, 'arrayAddAlias', array5AddAlias_Contract()) + self.assertIs(arrayAddNewMutate_result.is_success(), True) + self.assertIs( arrayAddNewVar5_result.is_success(), True) + self.assertIs( arrayAddNewVar05_result.is_success(), True) + self.assertIs( arrayAddNewVar10_result.is_success(), True) + self.assertIs( array5AddAlias_result.is_success(), True) +``` + +### Preconditions and Indices + +A common situation where one might use `precondition_f` is when an integer is passed into a function and is used to index into an array: + ### Explicit Arrays @@ -492,7 +584,7 @@ typedef struct { ``` -### Struct Initialization +### Struct Initialization and Explicit Structs @@ -500,11 +592,57 @@ typedef struct { -### Struct Helper Functions +# Assumptions and Lemmas +Sometimes one might want to blackbox certain parts of the verification such as functions coming from libraries, especially if they have already been formally verified. For example, the height of [height-balanced binary trees](https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) on n nodes is given by the ceiling of `log(n)`. In C we might use the `math` library to write the helper function +```C +#include -# Assumptions and Lemmas +uint64_t log2i(uint64_t i) { + return ceil(log2(i)); +} + +``` +we may not care to verify this function because it's a library function, especially if that library has already been formally verified. Also, even if we have verified this function we would like to speed up the verification procedure by black boxing its behavior. + +To accomplish these goals we make a contract outlining what the behavior of this function: + +```python +class log2iContract(Contract): + def specification(self): + i = self.fresh_var(i64, "i") + + self.execute_func(i) + + self.returns_f("lg2 {i}") +``` + + +In the unit test we would assume the `log2Contract`: + +``` +log2i_assume = llvm_assume(mod, 'log2i', log2iContract()) + +``` + +If a C function ever used `log2i`, then we could pass in the assumption as an optional argument to verification: + +``` +getHeight_result = llvm_verify(mod, 'getHeight', getHeightContract(), lemmas=[log2i_assume]) +``` + +The optional argument is a list because we can supply multiple assumptions or previous verifications. For example, we might have + +``` +addNode_result = llvm_verify(mod, 'addNode', addNodeContract(), lemmas=[getHeight_result]) +removeNode_result = llvm_verify(mod, 'removeNode', removeNodeContract(), lemmas=[getHeight_result]) +addOneRemoveTwo_result = llvm_verify(mod, 'addOneRemoveTwo', addOneRemoveTwo(), lemmas=[addNode_result, removeNode_result]) +``` + +One can think of lemmas as rewrite rules under the hood. Whenever SAW encounters log2 function in the C it will interpret its behavior as what is specified in the `log2Contract()`. # Uninterpreting Functions + +An alternative to passing in lemmas is uninterpreting a funciton. This is useful when you don't care about the specifics of the values produced by a total function, but rather, the types of the value produced. For example, hashing algorithms are often total functions. We often don't care about the particular bits of a hash function, but rather, that the hash function returned some 32-bit integer. From c1804b0d38379617dd31a8f9d8f33a5b997afd35 Mon Sep 17 00:00:00 2001 From: Taylor Ball trball Date: Thu, 26 May 2022 16:35:47 +0000 Subject: [PATCH 005/120] test files --- saw-python/proof/rcs.py | 29 +++++++++++++++++++++++++++++ saw-python/proof/spec/rcs.cry | 8 ++++++++ saw-python/src/rcs.bc | Bin 0 -> 2316 bytes saw-python/src/rcs.c | 13 +++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 saw-python/proof/rcs.py create mode 100644 saw-python/proof/spec/rcs.cry create mode 100644 saw-python/src/rcs.bc create mode 100644 saw-python/src/rcs.c diff --git a/saw-python/proof/rcs.py b/saw-python/proof/rcs.py new file mode 100644 index 00000000..f93c5efb --- /dev/null +++ b/saw-python/proof/rcs.py @@ -0,0 +1,29 @@ +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * +from saw_client.proofscript import * +from saw_client.llvm_type import * + +class RCS_Contract(Contract): + def specification(self): + xs = self.fresh_var(i32, "xs") + shift = self.fresh_var(i32, "shift") + + self.execute_func(xs, shift) + + self.returns_f("RCS {xs} {shift}") + +class RCSTest(unittest.TestCase): + def test_RCS(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + bcname = "../src/rcs.bc" + cryname = "./spec/rcs.cry" + + cryptol_load_file(cryname) + mod = llvm_load_module(bcname) + + RCS_result = llvm_verify(mod, 'RCS', RCS_Contract()) + self.assertIs(RCS_result.is_success(), True) diff --git a/saw-python/proof/spec/rcs.cry b/saw-python/proof/spec/rcs.cry new file mode 100644 index 00000000..2c9b7656 --- /dev/null +++ b/saw-python/proof/spec/rcs.cry @@ -0,0 +1,8 @@ +module rcs where + +increment : [32] -> [32] +increment x = x + 1 + + +RCS : [32] -> Integer -> [32] +RCS xs shift = xs >>> shift \ No newline at end of file diff --git a/saw-python/src/rcs.bc b/saw-python/src/rcs.bc new file mode 100644 index 0000000000000000000000000000000000000000..af6a84e3f32d012832985af9485348107bcdd1a8 GIT binary patch literal 2316 zcmaJ@Urbxq89$e6d<>Xt^GBUou5YeG8d+UJE{0HpVFPZKx*O9>(}!%zlxs++sSW2L9sP znzkb!#h}}XEhgJr$Kr{mSZvIA+^>;bT9md@1--_YK=~HUS>)HG;oP_hp+DA+EB>u7RpWwzSNa7tNUn`}}x(&WVE_dd^Bzg&z8Wt&}(a1hw?B8RRm(+&^qNYe@^NfN)cmKrPMg@hrM zDJ(UwQHQHa^ZjME>gFnu=KE=Li8@@AnjzHdFqJrPxdH;{aJ9^!4><5Tv%*3K^AuM~ zL-4r*B@kI6^H!|ff?l;JDAX>JNKl8v&jEnYtAH)F9@-Xwj;Yo?sduEKeG2hH(jaxW z@HJ1=FOzUlYJ5=8K1yTgt2_=O92T%Ijr}~nf($+xduwBL@jw8QE8##I&kf@%Nd|g# zTF~w)7(nRdaj2l(S8&*f=ME0mc7JcgJ_Uc%GRAbg_qS8;{qn+>f8RXCy+(-$4TG7r zMhQs#T?7o*Jze0Dc6cH>JWf+cIc}`gyp2ogJ6gt)iH2jZtZBrBR;vbm3`c$iM|yM$ zySmLzwNpcJ(H_etMisyV_NTo1U1WGdMF=C zKp|Y!;dv*Kp(12@E<|LWbmUu@y(F_O7-j^+PL-*;m>%P*c?-(TV$Qg^Dd8Q*-Ke7= z_TQ!l??wk>v?E__pOf%a4}MuKG5oTkqC%GNoE6VKsRy+|gx3r?g`! ztN+t9kIkJx&_b2ykz5>u&a;#k{p*ev4AyioC^K zb!%ULJ8I8o9ZTX61mx??U`2GqVVX_+u@O8aL9hp~3I&^roZm4rd&|rN7xR_Sxw0|B zRkpuT+%|840nQG`QgrBc^iV8oPl)~b?BJc1{#+J(9Lzg!d*RKIuonV&j&yvs#_UOk zM`eJo;elXyz#IPQVrGoYj=;e5tFrd5Wv1d{o(ST0srH&xshZ{PGoSt?YlmblWlt8x zfxI{n7YD%Wg7|GT&m(jcFy-T%ycpMaH27>zzB7`ze*KfvX`}VxV7G1b7fH*9R$~DD zyaS=&q{r>M;GYg(n!N0ud4r$38lNIGi`?Y^|wuqFCH3oiAI6wGN)bTz5i5^Nv2LV*ctonAid4vuFZ_}KH>O+VI($t%Ty5S!W!w~hfAU=^9 z(;(f4LK}rLR{bGkoegERr)52=7rN|k78TP1m8RBYQBhWoi@fIIvZ;?xN@%JxmKD&E~YSGb4 z>UZ7?a;kkc#&{%#KBG;`-+>uIm*D@vi@;myn^yI#DvNowR1pfQ@OeyS0~V# zf!ct0AW&1&5A6BAI#%VMHT8bAsa*Y@s{m5Rnx|_jf3^-o^b=63{(k`5F01p`#Isl2 q(=!K<3EBHD^!1*QCr@0So|z4ucw=U^tLvoSH+j-G>F=6U<^Kc!Ebcx4 literal 0 HcmV?d00001 diff --git a/saw-python/src/rcs.c b/saw-python/src/rcs.c new file mode 100644 index 00000000..1c5f76bd --- /dev/null +++ b/saw-python/src/rcs.c @@ -0,0 +1,13 @@ +#include +#include +#include + +uint32_t RCS(uint32_t bits, uint32_t shift) { + shift %= 64; + if(shift == 0) return bits; + return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); +} + +int main() { + return 0; +} From 6629e8414de9af9069fd55fbc7307b844c919bad Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Thu, 26 May 2022 15:10:24 -0400 Subject: [PATCH 006/120] Update README.md --- saw-python/README.md | 52 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/saw-python/README.md b/saw-python/README.md index 5510ad37..d6596487 100644 --- a/saw-python/README.md +++ b/saw-python/README.md @@ -380,7 +380,7 @@ def Array5AddNewVar_Contract(Contract): ``` ### Postconditions and `points_to` -Consider a contract for the third C function: +Consider another implementation of the previous contract ```python def Array5AddAlias_Contract(Contract): @@ -390,10 +390,10 @@ def Array5AddAlias_Contract(Contract): self.execute_func(a_p, b_p) - (aPost, aPost_p) = ptr_to_fresh(self, array_ty(5, i32), name="aPost") - self.postcondition_f("{aPost} == rowAdd {a} {b}") + (c, c_p) = ptr_to_fresh(self, array_ty(5, i32), name="c") + self.postcondition_f("{c} == rowAdd {a} {b}") - self.returns(aPost_p) + self.returns(c_p) ``` One could replace `self.postcondition_f("{aPost} == rowAdd {a} {b}")` with `self.points_to(aPost_p, cry_f("rowAdd {a} {b}"))`. A SAW symbolic array translates into a Cryptol array and a Cryptol array translates into a SAW symbolic array. @@ -574,6 +574,8 @@ Explicit arrays can be useful when you want to assert a condition on a particula ## Structs +//Dj help + Consider the following struct: ```C @@ -590,9 +592,9 @@ typedef struct { ### Structs in Cryptol +# Ask Sean for name? Compositional Logic? - -# Assumptions and Lemmas +## Assumptions and Lemmas Sometimes one might want to blackbox certain parts of the verification such as functions coming from libraries, especially if they have already been formally verified. For example, the height of [height-balanced binary trees](https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) on n nodes is given by the ceiling of `log(n)`. In C we might use the `math` library to write the helper function @@ -643,6 +645,42 @@ addOneRemoveTwo_result = llvm_verify(mod, 'addOneRemoveTwo', addOneRemoveTwo(), One can think of lemmas as rewrite rules under the hood. Whenever SAW encounters log2 function in the C it will interpret its behavior as what is specified in the `log2Contract()`. -# Uninterpreting Functions +## Uninterpreting Functions An alternative to passing in lemmas is uninterpreting a funciton. This is useful when you don't care about the specifics of the values produced by a total function, but rather, the types of the value produced. For example, hashing algorithms are often total functions. We often don't care about the particular bits of a hash function, but rather, that the hash function returned some 32-bit integer. + +//add in use when function complicated +//add uninterpreting may cause failure (logic needed sometimes). This can be avoided sometimes by making sure for every function you are uninterpreting you are also passing in a corresponding lemma + + +# Advanced Topics and Exercises + +//Dj add stuff + +## Global Variables + +//DJ add stuff + +//maybe something with `extern` + +### Nested DEFINE Statements + +// SAWs limitations +// Worse case: edit the source code + +## Aliasing + +// Talk about in general what aliasing is +// DJ add stuff (specifically with aliases) +// Talk about SAW limitations and lemmas + +### Wrapper Functions + +// Worse case scenario: editing the source code + +## Large Data + +// Dj add the stuff about inputting in a big array + +## Capstone + From ec00731541f8d79a27b5cc536ee400b1d7ba4b3d Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 26 May 2022 21:37:04 -0400 Subject: [PATCH 007/120] Moving saw-python to labs/SAW - to make it a legit lab --- {saw-python => labs/SAW}/README.md | 0 {saw-python => labs/SAW}/proof/rcs.py | 0 {saw-python => labs/SAW}/proof/spec/rcs.cry | 0 {saw-python => labs/SAW}/src/rcs.bc | Bin {saw-python => labs/SAW}/src/rcs.c | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {saw-python => labs/SAW}/README.md (100%) rename {saw-python => labs/SAW}/proof/rcs.py (100%) rename {saw-python => labs/SAW}/proof/spec/rcs.cry (100%) rename {saw-python => labs/SAW}/src/rcs.bc (100%) rename {saw-python => labs/SAW}/src/rcs.c (100%) diff --git a/saw-python/README.md b/labs/SAW/README.md similarity index 100% rename from saw-python/README.md rename to labs/SAW/README.md diff --git a/saw-python/proof/rcs.py b/labs/SAW/proof/rcs.py similarity index 100% rename from saw-python/proof/rcs.py rename to labs/SAW/proof/rcs.py diff --git a/saw-python/proof/spec/rcs.cry b/labs/SAW/proof/spec/rcs.cry similarity index 100% rename from saw-python/proof/spec/rcs.cry rename to labs/SAW/proof/spec/rcs.cry diff --git a/saw-python/src/rcs.bc b/labs/SAW/src/rcs.bc similarity index 100% rename from saw-python/src/rcs.bc rename to labs/SAW/src/rcs.bc diff --git a/saw-python/src/rcs.c b/labs/SAW/src/rcs.c similarity index 100% rename from saw-python/src/rcs.c rename to labs/SAW/src/rcs.c From c136eee2fa2c5653f8203a1b342afe2e3edc87b8 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 26 May 2022 21:55:46 -0400 Subject: [PATCH 008/120] Added some setup text and general polish --- labs/SAW/README.md | 255 +++++++++++++++++++++++++++++++++------------ 1 file changed, 188 insertions(+), 67 deletions(-) diff --git a/labs/SAW/README.md b/labs/SAW/README.md index d6596487..a44366fe 100644 --- a/labs/SAW/README.md +++ b/labs/SAW/README.md @@ -1,6 +1,28 @@ # Setting Everything Up -- Get saw-python to run +To run any of the examples in this lab, you need to first start the +Software Analysis Workbench (SAW) remote API (`saw-remote-api`). If +you are using the development container that comes w/ this course +(`ghcr.io/weaversa/cryptol-course`), you can enter the following +command in your terminal: + +``` +$ start-saw-remote-api +``` + +Otherwise, this tool can be installed by following the instructions +for SAW found in the [Installation lab](../../INSTALL.md). Once +installed, to run `saw-remote-api`, enter the following commands into +your terminal: + +``` +$ export SAW_SERVER_URL=http://0.0.0.0:36691 +$ saw-remote-api http --host 0.0.0.0 --port 36691 & +``` + +Congrats! You now have a server version of SAW running in the +background ready to accept commands on port `36691`. + - directory layout # Python Contracts Introduction @@ -8,7 +30,7 @@ Here's the general layout for SAW in Python: ```python -# Import junk +# Import SAW's Python modules class contractName(Contract): def specification(self): @@ -34,7 +56,8 @@ from saw_client.proofscript import * from saw_client.llvm_type import * ``` -We put `*` for simplicity, but if one only wanted certain functions, they could refer to this table for some common imports: +We put `*` for simplicity, but if one only wanted certain functions, +they could refer to this table for some common imports: |Package | Values | |---------------------|--------| @@ -44,7 +67,8 @@ We put `*` for simplicity, but if one only wanted certain functions, they could ## Right Circular Shift Example -To see contracts in action we need an example. Here's some C code for right circular shift we want to verify: +To see contracts in action we need an example. Here's some C code for +right circular shift we want to verify: ```C uint32_t RCS(uint32_t bits, uint32_t shift) { @@ -59,7 +83,8 @@ RCS : [32] -> [32] -> [32] RCS xs shift = xs >>> shift ``` -For the SAW Python API we make a `Contract` object with the required `specificaiton` function: +For the SAW Python API we make a `Contract` object with the required +`specificaiton` function: ```python class RCS_Contract(Contract): @@ -76,46 +101,67 @@ Let's break down `specification` piece by piece. ### Fresh Symbolic Variables -The command `self.fresh_var(type, name)` creates a new symbolic variable of type `type` and name `name`, where `name` is a string. Names for fresh symbolic variables -are not optional inputs, and they mostly serve for error messages. The string can be anything, but it makes sense to give it the name of the variable being defined. +The command `self.fresh_var(type, name)` creates a new symbolic +variable of type `type` and name `name`, where `name` is a +string. Names for fresh symbolic variables are not optional inputs, +and they mostly serve for error messages. The string can be anything, +but it makes sense to give it the name of the variable being defined. ### Execute Functions -The command `self.execute_func(input1, input2, ...)` will symbolically execute the function. There should be exactly as many comma separated inputs as there are -in the C function. One can only place preconditions before this command and postconditions after this command. + +The command `self.execute_func(input1, input2, ...)` will symbolically +execute the function. There should be exactly as many comma separated +inputs as there are in the C function. One can only place +preconditions before this command and postconditions after this +command. ### Return Statements -The command `self.returns_f(string)` asserts the current function returns the Cryptol term parsed from a Python string. To use Python variables in scope within the string use -`{variable_name}`. For example, + +The command `self.returns_f(string)` asserts the current function +returns the Cryptol term parsed from a Python string. To use Python +variables in scope within the string use `{variable_name}`. For +example, |`self.`|`returns_f(`|`"RCS {bits} {shift}"`|)| |-------|-----------|---------------------|----| |In this contract| assert the current function returns the Cryptol term | right circular shift `bits` by `shift` |.| -Sometimes we don't want to return a Cryptol term. In this case we can just use `returns(someSetupValue)`. The specification function of a Contract must -**always** have a `self.returns(someSetupValue)` or `self.returns_f(string)` statement. If the function returns `void` one can use `self.returns(void)`. +Sometimes we don't want to return a Cryptol term. In this case we can +just use `returns(someSetupValue)`. The specification function of a +Contract must **always** have a `self.returns(someSetupValue)` or +`self.returns_f(string)` statement. If the function returns `void` one +can use `self.returns(void)`. ### Terms from Cryptol -The command `cry(string)` converts a Python string into a `CryptolTerm` that can be used in SAW. The `_f` indicates one can pass in Python local variables into the strings by surrounding -the variable with braces as we did in `{bits} >>> {shift}`. In fact, `returns_f` is just syntactic sugar for `returns(cry_f(string))`. +The command `cry(string)` converts a Python string into a +`CryptolTerm` that can be used in SAW. The `_f` indicates one can pass +in Python local variables into the strings by surrounding the variable +with braces as we did in `{bits} >>> {shift}`. In fact, `returns_f` is +just syntactic sugar for `returns(cry_f(string))`. -The `CryptolTerm` class is a subclass of `SetupVal`. This allows using `CryptolTerm` as a `SetupVal`. +The `CryptolTerm` class is a subclass of `SetupVal`. This allows using +`CryptolTerm` as a `SetupVal`. -Braces are sometimes used in Cryptol to assign type parameters or record types. To have the parser parse a literal brace in a Cryptol string repeat the `{` or `}` symbols. -For example, consider the following made up SAW line: +Braces are sometimes used in Cryptol to assign type parameters or +record types. To have the parser parse a literal brace in a Cryptol +string repeat the `{` or `}` symbols. For example, consider the +following made up SAW line: ```python self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} blah }} == foo `blah") ``` -Suppose `blah` is a local Python variable equal to `23` and `N` is a local Python variable equal to `2`. The above string pasrses in Cryptol as +Suppose `blah` is a local Python variable equal to `23` and `N` is a +local Python variable equal to `2`. The above string pasrses in +Cryptol as ```cryptol {a = take`{5} 23, b = take`{2} 23} == foo `blah ``` -where `foo` is some Cryptol function returning a record, and `blah` is some -Cryptol type in the specification loaded. +where `foo` is some Cryptol function returning a record, and `blah` is +some Cryptol type in the specification loaded. ## Unit Testing @@ -137,25 +183,38 @@ class RCSTest(unittest.TestCase): self.assertIs(RCS_result.is_success(), True) ``` -For a contract the specification function should be called `specification`. For tests, it doesn't matter what you name your tests. Here we named it `test_RCS`. These tests will be ran when you try running the Python. +For a contract the specification function should be called +`specification`. For tests, it doesn't matter what you name your +tests. Here we named it `test_RCS`. These tests will be ran when you +try running the Python. Let's break down the first few lines of this function: -- The command `connect(reset_server=True)` connects to the server so we can use the SAW Python API -- The line `if __name__ == "__main__": view(LogResults(verbose_failure=True))` allows us to view the output with verbose error messages. If you don't want verbose error messages, then just use `if __name__ == "__main__": view(LogResults())` -- The line `bcname = "/some/path/to/your/file.bc"` declares which bitcode file we're analyzing. If you have multiple bitcodes files, then make a variable for each file. -- The line `mod = llvm_load_module(bcname)` creates the object we will pass to verification that represents the bitcode. -- The line `cryname = "/some/path/to/your/file.cry"` specifies the path to the Cryptol specification. +- The command `connect(reset_server=True)` connects to the server so + we can use the SAW Python API +- The line `if __name__ == "__main__": + view(LogResults(verbose_failure=True))` allows us to view the output + with verbose error messages. If you don't want verbose error + messages, then just use `if __name__ == "__main__": + view(LogResults())` +- The line `bcname = "/some/path/to/your/file.bc"` declares which + bitcode file we're analyzing. If you have multiple bitcodes files, + then make a variable for each file. +- The line `mod = llvm_load_module(bcname)` creates the object we will + pass to verification that represents the bitcode. +- The line `cryname = "/some/path/to/your/file.cry"` specifies the + path to the Cryptol specification. - The line `cryptol_load_file(cryname)` loads the Cryptol specification. -Now that we've set up our environment, let's actually verify our contract! This is done at the line +Now that we've set up our environment, let's actually verify our +contract! This is done at the line |`RCS_result =` | `llvm_verify(` | `mod,` | `'RCS',` | `RCS_Contract()`| `)`| |----------|-----------|----|------|---------------|----| |Assign this variable| to the result of trying to verify| the bitcode| function with this name| using this contract|.| -Now that we have the result, we want to assert this result succeeded using `self.assertIs(RCS_result.is_success(), True)`. - +Now that we have the result, we want to assert this result succeeded +using `self.assertIs(RCS_result.is_success(), True)`. ## Debugging C with SAW @@ -203,10 +262,14 @@ we get the output put output here ``` -SAW alerted us about potentially undefined behavior mentioned in the [C specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) has the following to say about bit shifts: +SAW alerted us about potentially undefined behavior mentioned in the +[C +specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) +has the following to say about bit shifts: -> If the value of the right operand is negative or is -> greater than or equal to the width of the promoted left operand, the behavior is undefined. +> If the value of the right operand is negative or is greater than or +> equal to the width of the promoted left operand, the behavior is +> undefined. As expected, this alerts us of a bug: @@ -218,7 +281,7 @@ One remedy to this is the following: ``` uint32_t RCS(uint32_t bits, uint32_t shift) { - shift %= 64 + shift %= sizeof(bits)*8; return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); } ``` @@ -233,13 +296,14 @@ Aha! We forgot about the case when `shift` is zero! Let's try again with ``` uint32_t RCS(uint32_t bits, uint32_t shift) { - shift %= 64 + shift %= sizeof(bits)*8; if(shift == 0) return bits; return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); } ``` -Finally, SAW is happy! +Finally, SAW is happy (or, more importantly, the C is correct and free +of undefined behavior)! ``` @@ -249,14 +313,16 @@ Finally, SAW is happy! ## Pointers and Arrays -We'll begin by writing a function that given two arrays adds the second to the first. One way to write this is to -write a function that just mutates the first array and returns nothing: +We'll begin by writing a function that given two arrays adds the +second to the first. One way to write this is to write a function that +just mutates the first array and returns nothing: ```C ``` -An alternative way to do this would be to create a new array storing the result and return it: +An alternative way to do this would be to create a new array storing +the result and return it: ```C @@ -276,7 +342,8 @@ The corresponding Cryptol specification is: ### Initializing Arrays and Pointers -To initialize the arrays and pointers we'll use the `alloc` command and `array_ty` type: +To initialize the arrays and pointers we'll use the `alloc` command +and `array_ty` type: ```python def Array5AddMutate_Contract(Contract): @@ -293,22 +360,29 @@ def Array5AddMutate_Contract(Contract): self.returns(void) ``` -- The `array_ty(type, length)` command creates a type representing an array with entries of type `type` and length `length`. -- The `alloc` command will initialize a symbolic pointer. Let's break down the initialization of `b_p`: +- The `array_ty(type, length)` command creates a type representing an + array with entries of type `type` and length `length`. +- The `alloc` command will initialize a symbolic pointer. Let's break + down the initialization of `b_p`: | `b_p` | `self.alloc(` | `array_ty(5, i32)`|`,`|`points_to=b`|`,`|`read_only=True`|`)`| |-------|---------------|-------------------|---|-------------|---|----------------|---| | Assign this variable to | a symbolic pointer in the current contract | that has the following type | and | points to this object | with | read only permissions| . | -Since arrays are passed as pointers in C, when we call `execute_func` we supply `a_p` and `b_p` rather than `a` and `b`. +Since arrays are passed as pointers in C, when we call `execute_func` +we supply `a_p` and `b_p` rather than `a` and `b`. -To verify correctness, we assert that after function exectution `a_p` points to what the Cryptol specification claims it should using `self.points_to(a_p, cry_f("rowAdd {a} {b}"))`. +To verify correctness, we assert that after function exectution `a_p` +points to what the Cryptol specification claims it should using +`self.points_to(a_p, cry_f("rowAdd {a} {b}"))`. -A specification must contain a `self.returns` or `self.returns_f` line. Since our function returns nothing, we use `self.returns(void)`. +A specification must contain a `self.returns` or `self.returns_f` +line. Since our function returns nothing, we use `self.returns(void)`. ### Helper Functions -To limit code reuse we can define helper functions in Python. For example, the following construct is often used: +To limit code reuse we can define helper functions in Python. For +example, the following construct is often used: ```python def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None, read_only : Optional[bool] = False) -> Tuple[FreshVar, SetupVal]: @@ -317,7 +391,11 @@ def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None, read_ return (var, ptr) ``` -Given a contract and a type this function outputs a tuple `(var, ptr)` where `var` is a fresh symbolic variable of the given type and `ptr` is a pointer pointing to this variable. We give optional arguments to name the fresh symbolic variable and to force read only pointer constraints. +Given a contract and a type this function outputs a tuple `(var, ptr)` +where `var` is a fresh symbolic variable of the given type and `ptr` +is a pointer pointing to this variable. We give optional arguments to +name the fresh symbolic variable and to force read only pointer +constraints. To see this in action, let's rewrite our previous contract: @@ -341,7 +419,11 @@ A SAW contract is strictly divided into three parts: 2. Execution 3. Postconditions -SAW will complain if you place a precondition after `execute_func` and similarly for postcondition. If a function returns a value that was not passed through `execute_func`, then you will have to initialize new fresh symbolic variables. For example, consider the proposed contract for the next C function: +SAW will complain if you place a precondition after `execute_func` and +similarly for postcondition. If a function returns a value that was +not passed through `execute_func`, then you will have to initialize +new fresh symbolic variables. For example, consider the proposed +contract for the next C function: ```python def Array5AddNewVar_Contract(Contract): @@ -363,7 +445,12 @@ Running a unit test yields the following error message: SAW error message ``` -Think about the precondition block, the part before `execute_func`, as setting up the symbolic variables **before** they enter the function. With this in mind, it doesn't make sense to declare `c_p` in this block because `c_p` is defined **within** the C function. The fix to the previous contract is moving the declaration of `c_p` to the postcondition block: +Think about the precondition block, the part before `execute_func`, as +setting up the symbolic variables **before** they enter the +function. With this in mind, it doesn't make sense to declare `c_p` in +this block because `c_p` is defined **within** the C function. The fix +to the previous contract is moving the declaration of `c_p` to the +postcondition block: ```python def Array5AddNewVar_Contract(Contract): @@ -396,7 +483,10 @@ def Array5AddAlias_Contract(Contract): self.returns(c_p) ``` -One could replace `self.postcondition_f("{aPost} == rowAdd {a} {b}")` with `self.points_to(aPost_p, cry_f("rowAdd {a} {b}"))`. A SAW symbolic array translates into a Cryptol array and a Cryptol array translates into a SAW symbolic array. +One could replace `self.postcondition_f("{aPost} == rowAdd {a} {b}")` +with `self.points_to(aPost_p, cry_f("rowAdd {a} {b}"))`. A SAW +symbolic array translates into a Cryptol array and a Cryptol array +translates into a SAW symbolic array. ### Parameterized Contracts @@ -406,13 +496,19 @@ Suppose our C code was written as ``` -where the length of the array is not specified. The corresponding Cryptol code might be +where the length of the array is not specified. The corresponding +Cryptol code might be ```cryptol ``` -SAW does not have inductive reasoning capabilities. Suppose later in our program we realize we want to verify the above function not just for arrays of size 5, but also those of a different fixed size, say 10. One option would be to just make a new contract. However, rewriting the same contract over and over is a waste of code. Instead, we could supply parameters to the Contract and write it once: +SAW does not have inductive reasoning capabilities. Suppose later in +our program we realize we want to verify the above function not just +for arrays of size 5, but also those of a different fixed size, +say 10. One option would be to just make a new contract. However, +rewriting the same contract over and over is a waste of code. Instead, +we could supply parameters to the Contract and write it once: ```python def arrayAddNewVar_Contract(Contract): @@ -536,12 +632,14 @@ class ArrayTests(unittest.TestCase): ### Preconditions and Indices -A common situation where one might use `precondition_f` is when an integer is passed into a function and is used to index into an array: - +A common situation where one might use `precondition_f` is when an +integer is passed into a function and is used to index into an array: ### Explicit Arrays -Another way to initialize arrays is through the `array` command. For example, suppose I have the following C function (taken from [here](https://github.com/GaloisInc/saw-script/blob/337ca6c9edff3bcbcd6924289471abd5ee714c82/saw-remote-api/python/tests/saw/test-files/llvm_array_swap.c)): +Another way to initialize arrays is through the `array` command. For +example, suppose I have the following C function (taken from +[here](https://github.com/GaloisInc/saw-script/blob/337ca6c9edff3bcbcd6924289471abd5ee714c82/saw-remote-api/python/tests/saw/test-files/llvm_array_swap.c)): ```C #include @@ -553,7 +651,8 @@ void array_swap(uint32_t a[2]) { } ``` -Here's a contract that doesn't even require Cryptol (taken from [here](https://github.com/GaloisInc/saw-script/blob/337ca6c9edff3bcbcd6924289471abd5ee714c82/saw-remote-api/python/tests/saw/test_llvm_array_swap.py)): +Here's a contract that doesn't even require Cryptol (taken from +[here](https://github.com/GaloisInc/saw-script/blob/337ca6c9edff3bcbcd6924289471abd5ee714c82/saw-remote-api/python/tests/saw/test_llvm_array_swap.py)): ```python class ArraySwapContract(Contract): @@ -569,7 +668,12 @@ class ArraySwapContract(Contract): self.returns(void) ``` -Explicit arrays can be useful when you want to assert a condition on a particular element of the array. Of course, a drawback is you have to initialize every member of the array. However, this can be problematic for large arrays. Ideally, one would just have to declare the array implicitly as before and then pass this array to a Cryptol specification for postconditions as was done in the previous examples. +Explicit arrays can be useful when you want to assert a condition on a +particular element of the array. Of course, a drawback is you have to +initialize every member of the array. However, this can be problematic +for large arrays. Ideally, one would just have to declare the array +implicitly as before and then pass this array to a Cryptol +specification for postconditions as was done in the previous examples. ## Structs @@ -585,18 +689,21 @@ typedef struct { } ``` - ### Struct Initialization and Explicit Structs - - ### Structs in Cryptol # Ask Sean for name? Compositional Logic? ## Assumptions and Lemmas -Sometimes one might want to blackbox certain parts of the verification such as functions coming from libraries, especially if they have already been formally verified. For example, the height of [height-balanced binary trees](https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) on n nodes is given by the ceiling of `log(n)`. In C we might use the `math` library to write the helper function +Sometimes one might want to blackbox certain parts of the verification +such as functions coming from libraries, especially if they have +already been formally verified. For example, the height of +[height-balanced binary +trees](https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) +on n nodes is given by the ceiling of `log(n)`. In C we might use the +`math` library to write the helper function. ```C #include @@ -607,9 +714,13 @@ uint64_t log2i(uint64_t i) { ``` -we may not care to verify this function because it's a library function, especially if that library has already been formally verified. Also, even if we have verified this function we would like to speed up the verification procedure by black boxing its behavior. +We may not care to verify this function because it's a library +function, especially if that library has already been formally +verified. Also, even if we have verified this function we would like +to speed up the verification procedure by black boxing its behavior. -To accomplish these goals we make a contract outlining what the behavior of this function: +To accomplish these goals we make a contract outlining what the +behavior of this function: ```python class log2iContract(Contract): @@ -629,13 +740,15 @@ log2i_assume = llvm_assume(mod, 'log2i', log2iContract()) ``` -If a C function ever used `log2i`, then we could pass in the assumption as an optional argument to verification: +If a C function ever used `log2i`, then we could pass in the +assumption as an optional argument to verification: ``` getHeight_result = llvm_verify(mod, 'getHeight', getHeightContract(), lemmas=[log2i_assume]) ``` -The optional argument is a list because we can supply multiple assumptions or previous verifications. For example, we might have +The optional argument is a list because we can supply multiple +assumptions or previous verifications. For example, we might have ``` addNode_result = llvm_verify(mod, 'addNode', addNodeContract(), lemmas=[getHeight_result]) @@ -643,11 +756,19 @@ removeNode_result = llvm_verify(mod, 'removeNode', removeNodeContract(), le addOneRemoveTwo_result = llvm_verify(mod, 'addOneRemoveTwo', addOneRemoveTwo(), lemmas=[addNode_result, removeNode_result]) ``` -One can think of lemmas as rewrite rules under the hood. Whenever SAW encounters log2 function in the C it will interpret its behavior as what is specified in the `log2Contract()`. +One can think of lemmas as rewrite rules under the hood. Whenever SAW +encounters log2 function in the C it will interpret its behavior as +what is specified in the `log2Contract()`. ## Uninterpreting Functions -An alternative to passing in lemmas is uninterpreting a funciton. This is useful when you don't care about the specifics of the values produced by a total function, but rather, the types of the value produced. For example, hashing algorithms are often total functions. We often don't care about the particular bits of a hash function, but rather, that the hash function returned some 32-bit integer. +An alternative to passing in lemmas is uninterpreting a funciton. This +is useful when you don't care about the specifics of the values +produced by a total function, but rather, the types of the value +produced. For example, hashing algorithms are often total +functions. We often don't care about the particular bits of a hash +function, but rather, that the hash function returned some 32-bit +integer. //add in use when function complicated //add uninterpreting may cause failure (logic needed sometimes). This can be avoided sometimes by making sure for every function you are uninterpreting you are also passing in a corresponding lemma From 7aa9ca68d680cb95d7ca9e44126a71ec47a43339 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 26 May 2022 22:33:33 -0400 Subject: [PATCH 009/120] Rename README.md to SAW.md --- labs/SAW/{README.md => SAW.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename labs/SAW/{README.md => SAW.md} (100%) diff --git a/labs/SAW/README.md b/labs/SAW/SAW.md similarity index 100% rename from labs/SAW/README.md rename to labs/SAW/SAW.md From e3ee0694d666d2978c9e8caecd1ac973a651a9da Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 26 May 2022 23:16:06 -0400 Subject: [PATCH 010/120] Filled in SAW call and responses for rcs example --- labs/SAW/SAW.md | 166 ++++++++++++++++++++++++++++++------ labs/SAW/proof/rcs.py | 13 ++- labs/SAW/proof/spec/rcs.cry | 8 +- labs/SAW/src/rcs.bc | Bin 2316 -> 2236 bytes labs/SAW/src/rcs.c | 8 +- 5 files changed, 155 insertions(+), 40 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index a44366fe..3e3dd870 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -72,11 +72,44 @@ right circular shift we want to verify: ```C uint32_t RCS(uint32_t bits, uint32_t shift) { - return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); + return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); } ``` -and the corresponding Cryptol specification: +SAW doesn't actually verify C source, but rather C compiled down to +LLVM intermediate representation (IR), or bitcode. This can be +accomplished via the `clang` compiler. In this instance, we can create +the bitcode by entering the follwing command in a terminal: + +```sh +$ clang -emit-llvm labs/SAW/src/rcs.c -c -o labs/SAW/src/rcs.bc +``` + +We can inspect the bitcode using SAW by loading the module and +printing some metadata. + +```Xsaw-session +$ saw + ┏━━━┓━━━┓━┓━┓━┓ + ┃ ━━┓ ╻ ┃ ┃ ┃ ┃ + ┣━━ ┃ ╻ ┃┓ ╻ ┏┛ + ┗━━━┛━┛━┛┗━┛━┛ version 0.9.0.99 () + +sawscript> r <- llvm_load_module "labs/SAW/src/rcs.bc" +sawscript> print r +Module: rcs.bc +Types: + +Globals: + +External references: + +Definitions: + i32 @RCS(i32 %0, i32 %1) + +`` + +The corresponding Cryptol specification for right circular shift is: ```cryptol RCS : [32] -> [32] -> [32] @@ -84,7 +117,7 @@ RCS xs shift = xs >>> shift ``` For the SAW Python API we make a `Contract` object with the required -`specificaiton` function: +`specification` function: ```python class RCS_Contract(Contract): @@ -221,6 +254,7 @@ using `self.assertIs(RCS_result.is_success(), True)`. The full code is: ```python3 +import os import unittest from saw_client import * from saw_client.crucible import * @@ -242,29 +276,58 @@ class RCSTest(unittest.TestCase): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) - bcname = "/some/path/to/your/file.bc" - cryname = "/some/path/to/your/file.cry" + pwd = os.getcwd() + bcname = pwd + "/../src/rcs.bc" + cryname = pwd + "/spec/rcs.cry" cryptol_load_file(cryname) mod = llvm_load_module(bcname) RCS_result = llvm_verify(mod, 'RCS', RCS_Contract()) self.assertIs(RCS_result.is_success(), True) -``` - -Running the code with -`command` +if __name__ == "__main__": + unittest.main() +``` -we get the output +We can now run the proof script. ``` -put output here +$ cd labs/SAW/proof +$ python3 rcs.py +ifying RCS ... +[03:08:29.987] Simulating RCS ... +[03:08:29.988] Checking proof obligations RCS ... +[03:08:30.007] Subgoal failed: RCS safety assertion: +internal: error: in RCS +Undefined behavior encountered +Details: + Poison value created + The second operand of `shl` was equal to or greater than the number of bits in the first operand + +[03:08:30.007] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 382} +[03:08:30.007] ----------Counterexample---------- +[03:08:30.007] shift0: 134217728 +[03:08:30.007] ---------------------------------- + +F +====================================================================== +FAIL: test_RCS (__main__.RCSTest) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "cryptol-course/labs/SAW/proof/rcs.py", line 31, in test_RCS + self.assertIs(RCS_result.is_success(), True) + AssertionError: False is not True + + ---------------------------------------------------------------------- + Ran 1 test in 0.750s + + FAILED (failures=1) + 🛑 The goal failed to verify. ``` SAW alerted us about potentially undefined behavior mentioned in the -[C -specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) +[C specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) has the following to say about bit shifts: > If the value of the right operand is negative or is greater than or @@ -274,39 +337,94 @@ has the following to say about bit shifts: As expected, this alerts us of a bug: ``` - + The second operand of `shl` was equal to or greater than the number of bits in the first operand + +[03:08:30.007] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 382} +[03:08:30.007] ----------Counterexample---------- +[03:08:30.007] shift0: 134217728 +[03:08:30.007] ---------------------------------- ``` +SAW also provides a handy counterexample, namely, when `shift = +134217728` (clearly larger than 31), we encounter undefined behavior. + One remedy to this is the following: ``` uint32_t RCS(uint32_t bits, uint32_t shift) { - shift %= sizeof(bits)*8; - return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); + shift %= sizeof(bits) * 8; + return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); } ``` -Running SAW gives: - -``` +Recompiling and running SAW gives: ``` - -Aha! We forgot about the case when `shift` is zero! Let's try again with +rcs.py:30): +error: Proof failed. + stdout: +[03:11:54.334] Verifying RCS ... +[03:11:54.334] Simulating RCS ... +[03:11:54.335] Checking proof obligations RCS ... +[03:11:54.351] Subgoal failed: RCS safety assertion: +internal: error: in RCS +Undefined behavior encountered +Details: + Poison value created + The second operand of `shl` was equal to or greater than the number of bits in the first operand + +[03:11:54.351] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 388} +[03:11:54.351] ----------Counterexample---------- +[03:11:54.351] shift0: 0 +[03:11:54.351] ---------------------------------- + +F +====================================================================== +FAIL: test_RCS (__main__.RCSTest) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "cryptol-course/labs/SAW/proof/rcs.py", line 31, in test_RCS + self.assertIs(RCS_result.is_success(), True) + AssertionError: False is not True + + ---------------------------------------------------------------------- + Ran 1 test in 0.723s + + FAILED (failures=1) + 🛑 The goal failed to verify.~ +``` + +Aha! The counter example shows that we forgot about the case when +`shift` is zero! This causes `(sizeof(bits) * 8 - 0)` to be `32`, +which is equal to the wordsize of `bits`, and hence causes `<<` to +exhibit undefined behavior. + +Let's try again with ``` uint32_t RCS(uint32_t bits, uint32_t shift) { shift %= sizeof(bits)*8; if(shift == 0) return bits; - return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); + return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); } ``` -Finally, SAW is happy (or, more importantly, the C is correct and free -of undefined behavior)! +Finally, SAW is happy. More importantly, the C is correct and free of +undefined behavior. ``` +$ clang ../src/rcs.c -o ../src/rcs.bc -c -emit-llvm && python3 rcs.py +[03:14:09.561] Verifying RCS ... +[03:14:09.562] Simulating RCS ... +[03:14:09.563] Checking proof obligations RCS ... +[03:14:09.614] Proof succeeded! RCS +✅ Verified: lemma_RCS_Contract (defined at cryptol-course/labs/SAW/proof/rcs.py:30) +. +---------------------------------------------------------------------- +Ran 1 test in 0.780s +OK +✅ The goal was verified! ``` # Pointers, Arrays, and Structs diff --git a/labs/SAW/proof/rcs.py b/labs/SAW/proof/rcs.py index f93c5efb..f6a5d71b 100644 --- a/labs/SAW/proof/rcs.py +++ b/labs/SAW/proof/rcs.py @@ -1,3 +1,4 @@ +import os import unittest from saw_client import * from saw_client.crucible import * @@ -18,12 +19,16 @@ class RCSTest(unittest.TestCase): def test_RCS(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) - - bcname = "../src/rcs.bc" - cryname = "./spec/rcs.cry" - + + pwd = os.getcwd() + bcname = pwd + "/../src/rcs.bc" + cryname = pwd + "/spec/rcs.cry" + cryptol_load_file(cryname) mod = llvm_load_module(bcname) RCS_result = llvm_verify(mod, 'RCS', RCS_Contract()) self.assertIs(RCS_result.is_success(), True) + +if __name__ == "__main__": + unittest.main() diff --git a/labs/SAW/proof/spec/rcs.cry b/labs/SAW/proof/spec/rcs.cry index 2c9b7656..a1f976d7 100644 --- a/labs/SAW/proof/spec/rcs.cry +++ b/labs/SAW/proof/spec/rcs.cry @@ -1,8 +1,4 @@ module rcs where -increment : [32] -> [32] -increment x = x + 1 - - -RCS : [32] -> Integer -> [32] -RCS xs shift = xs >>> shift \ No newline at end of file +RCS : [32] -> [32] -> [32] +RCS xs shift = xs >>> shift diff --git a/labs/SAW/src/rcs.bc b/labs/SAW/src/rcs.bc index af6a84e3f32d012832985af9485348107bcdd1a8..48879313951a135f25452b81a1ea611d4b7eb61a 100644 GIT binary patch delta 1065 zcmaJ=Z)jUp6hE);HTOl6t<3~e`rgfZ%dBDINLw0LOCebYPRR#15%NX$;?^ybSznSi z|0v`oQSZF

b~v$O;^9I(C-CHv`m&y)#go-ML3R7%gLk^eGJl8cWmzuHgyE*ShO8e zwr!;=gz>Jd7$S$|VqPwj{QuQ=b@jo9+Nmf`G=NR)B&pLwkPreLvolVu;>#-jT+?Fd@8Lfc8@N87uLAjrp~e(slTy1Ahsv}4e%SfC@cL~E6q zb(Rg0QmU4=SP32qM-NuL2i6g&VjuK@t@Lc~rj6XtUt@?xtqDI_Y>APCm~F9QokS9~ zT#76jwTUM06+`4aQ|Aebt=bgGr8SaA;{BxHT_d|n2V0EMt)$ZE^+g*a;@2mA!_o&` z0&uy4GtiGZMw{}`V7$KM9(m~-X{PE6N$@4k$R}S7NhkVV4E}tOX^j&dsr8c>lST{|QFOU+x)j5_o2{f*{o}13XD?;)!&*lEJVQ$96|AZeZOwoyZr4rYIzUw7Z(SwMpWNUB1>~6mJHFn!LC~*mu2}lO=>i0k>n!1wum;H zE3<5g<}H%4*__4V6`F>Fu+%b@Ca3-wmLG(r+XcC~S@oZUr8@<=ZAkTkjMtD7=|-6) zK}?lc++vtCn`P-Z&1*CVE{0QERq27Lv_a`sr3U7zO6{7|smk?&^eB91W07VovY|0I zp+&A~EKy;Z5}W&;rB#}bQ3ya?lz*-I@7$6Poco+Keu3pJnzcw?qzQv1%e3Yeh3TR0 z;*P@7k&D|bv-CX8^c(&VtID$6|Mz)xEVpf?84cNa9PHim)VBi*Fqw8$D2wsP3`UEGTavS)Hl^*K1_KA`2B_W0n$2X??ym zB%9@NK4oh78{AmW_TQ~ZJyYox{C6vIn;UBxex$F7uAyLaT?D{buM=>7;A75T{C83P zs{+nm#M!$`1yAhK$FXI?fjv1019&u}lzk(?rBcy5@p41Zw}vzUzQz#eoBQMm{we*q z-JeR2Q<|o9V!rhAz5V%Zvaq-ROiPa*=+PZLx~E4E_2--g&zW;+_+$hibJ;SJZ>LuC zE0?dDYwxKWE6LRA2W#qdWD57wv+I}E*V60LA5G6sMP{dF)N#>{Y=O_Y;dzz|I`=#} z2hKTqc9URz!ayj($KH&g31@?#7VAp_h&Lj`9Mm=6=pa7>`<@r$J{X8h&0lHRp)Ib$ zHR8H$-cMZh?hsdXz z8|z}J6BI|MU26x$U4E2d6cq&n`k7EYx_52fc}pDg_ensnubqTAJQ6?;vtM;Fuj?1z OH&@plGV6RHj{gIx4Ln%@ diff --git a/labs/SAW/src/rcs.c b/labs/SAW/src/rcs.c index 1c5f76bd..cff451af 100644 --- a/labs/SAW/src/rcs.c +++ b/labs/SAW/src/rcs.c @@ -3,11 +3,7 @@ #include uint32_t RCS(uint32_t bits, uint32_t shift) { - shift %= 64; + shift %= sizeof(bits) * 8; if(shift == 0) return bits; - return (bits << shift) | (bits >> (sizeof(bits)*8 - shift)); -} - -int main() { - return 0; + return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); } From 37d7ccdc3c39fac0737719970aa089063b969696 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 26 May 2022 23:16:32 -0400 Subject: [PATCH 011/120] Removing compiled file --- labs/SAW/src/rcs.bc | Bin 2236 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 labs/SAW/src/rcs.bc diff --git a/labs/SAW/src/rcs.bc b/labs/SAW/src/rcs.bc deleted file mode 100644 index 48879313951a135f25452b81a1ea611d4b7eb61a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2236 zcmaJ?Urbxq89&Z7KG$Hb9YC!a_ugEGG_#_H7*ia31_?0BXvYI-JBdk|xDE+*ri5MF zU>g@r*Vx&0SF3X(wD=*D>@*MTrJ^bid59)sv&^%sh@z^pJcM94lw}TLZ8}9-rR_V; zvb^m`=g)V}cfRxczVCO=8FFQ}dfO0cKnPvbSx&t5(?3jI``5}u(|LJR1#>1HLf^um z>4*s(0r?fEIMR7zu%LcZW3W1Is$#m=8t8Uj{ozH~@WUfRH=BlH25Xz0HvYKLIusv~ z)=ZtlElR>Lc=O1m0{v;5;5ru<{;aYN#f+!Sr;^?C%NK44YXz|Tfq+mm^w#6FR=9Ls z-Fp0P44U0oCfU|98c#IEVxxu=eziEJL1`l`7*(lGbltHaHX?N1h|q_yw`26=vwuTT zNl0s^1Rd=gQRZmu6&0U{3e(DrsMw5(B;r5{XW3kY+sm*h$5u^jCBs$_zh&Y!BYbg? zmx&Id17>9|rPyRVXI8vm%BNGZ z4*tLGdnWGTDp!oKm4Rd6!j_w_WZCUZbH&8&t2pGR!E42b=wL{(r7?shBr7wL;(q}~ z=BpXLs1o)fe9p~&nQ7hzBM^-j?yP0_(kcsJ5| zrLU!%YchitzzhCihTYFJmog9;{_!A~;^kf=p3hRYamt@n=91Vp9u0W#ycq3G5l=yc zfcug}VLlsmB09W;gAjHj4lUtrwg?Um@*8dz-QhRgd=Bt{eF$2bgCo{oMr^5`tdtV8 z05-}mDiFOeDw)`_8+-(+pf`atWYD?*^(0_&K5KBMqc&ObdZ@q%4ldz&Pt-3d;j-BH zXkGIpjiEgha1i0J1^d$2FW_rP?~|~%J|>gT6$Bg>@k|;A2Jp2c11%BPHG49a(%38D z(7NV8#$f{vJl|Mf{mg)UGX9=vlxe^9_ugB-nfUr2o4x$oDi=Zn1TQHn3u@_5|9TH2 zbSVNdxwlq6MYkUvO(wJ_fAm1@T4*t=(Wl_sEm-TJWp-_cooZ9{$6fYVJ~0GT4gm|j zx;>ba&rYsG0wP;6)>MywZ6I6dGlmOPgPvf}}MUrq>q5>gcztUbh z%G8p~jzvFh(eEBIhht2SVU62@_?oz!o1e0L;JhDouDcv7RNwt*UyO2=4%-4Ee&2)7 zdf+zVvofI$xn^b@c-8=BgR(L~D_*1G11h8<$kGx>%^8!+@V|Z?)9j>ly@$>`XM0S z<@%~FXB@n=NL0> zV0JAGh~I2${3kyjnAH@Pe$FILP3>>DZL;JLi2@|2@jF7a85wz1aQp9j|i@~u3hW1jtHHR#El!D52p?0OMTtep^uWLkIe=@ zdaE6w;H1axoA6%=U;fF(+3A^CVe+bb`aL0V+2_CVi)o?T+GXkLKHcq@t*`c=PI|zy zfao5bYoKJHtu3i^qFk<3E>yM(kITgirE>1!-Z^dj-TLH4p}4t!P77ZF`aN(Tjzdk2 zJ~MC%hsUK9_>qU~Io9(Fd+dWu^Zp>AJh;1G&($k$?261+V?Y6Yl@LMy)fn@1S&m?M z@Cidh3PhLWXI#3Sx38$2cccA_6rAz70U-_axYTzy>bwoaqx#d)J|JN-Pu>={fY34M zTh+s_gKGT`z0@#lA%AJlQDjKuM%76Dv87doR?WH&L#`X;HN&C~HCU|6_qLaG=vU40 z<3@B!)SXppjU}skXn;<*sUfXK{H>T)YhELI-;e9iC-g_W@oCm>%62YrHepFbQ(Da( z@hU=Iz(BBC8N=}yDwAQoT!z1$U~8ck56J|7rZqfiO{aj2w_p2!jrplG4CLS> Date: Fri, 27 May 2022 07:08:44 -0400 Subject: [PATCH 012/120] Added new directory to hold struct example Included the C source code in the initial commit. --- labs/SAW/Game/Player.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 labs/SAW/Game/Player.c diff --git a/labs/SAW/Game/Player.c b/labs/SAW/Game/Player.c new file mode 100644 index 00000000..ee461a44 --- /dev/null +++ b/labs/SAW/Game/Player.c @@ -0,0 +1,27 @@ +#include "Player.h" + +uint32_t initializeDefaultPlayer(player_t* player) +{ + // Variables + uint8_t i = 0; + uint32_t hp_default = 10; + uint32_t atk_default = 5; + uint32_t def_default = 4; + uint32_t spd_default = 3; + + // Initialize player according to some defaults + for (i = 0; i < MAX_NAME_LENGTH; i++) + { + // For simplicity, let's use A very cool name + // Assume ignoring the null terminator is fine too + // Remember, this is a dummy example ;) + player->name[i] = 'A'; + } + player->level = 1; + player->hp = hp_default; + player->atk = atk_default; + player->def = def_default; + player->spd = spd_default; + + return SUCCESS; +} From 3d4ce15a8b7f01234f82b3004da8d7c76ad2ab43 Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Fri, 27 May 2022 07:10:40 -0400 Subject: [PATCH 013/120] Added Makefile and header file for struct example Will continue to write up the SAWscript for the struct example. --- labs/SAW/Game/Makefile | 4 ++++ labs/SAW/Game/Player.h | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 labs/SAW/Game/Makefile create mode 100644 labs/SAW/Game/Player.h diff --git a/labs/SAW/Game/Makefile b/labs/SAW/Game/Makefile new file mode 100644 index 00000000..5eca2a23 --- /dev/null +++ b/labs/SAW/Game/Makefile @@ -0,0 +1,4 @@ +all: + clang -c -emit-llvm -o Player.bc Player.c +clean: + rm -f *.bc \ No newline at end of file diff --git a/labs/SAW/Game/Player.h b/labs/SAW/Game/Player.h new file mode 100644 index 00000000..bd713a94 --- /dev/null +++ b/labs/SAW/Game/Player.h @@ -0,0 +1,23 @@ +#ifndef PLAYER_H +#define PLAYER_H + +#include + +#define MAX_NAME_LENGTH 12 +#define SUCCESS 170 // 170 = 0xAA = 10101010 +#define FAILURE 85 // 85 = 0x55 = 01010101 + +typedef struct { + uint8_t name[MAX_NAME_LENGTH]; + uint32_t level; + uint32_t hp; + uint32_t atk; + uint32_t def; + uint32_t spd; +} player_t; + + +// Function prototypes +uint32_t initializeDefaultPlayer(player_t* player); + +#endif \ No newline at end of file From 5db3456f37d1ed083ff1b92ae0b57a0feaa2b210 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Fri, 27 May 2022 13:20:08 -0400 Subject: [PATCH 014/120] Update SAW.md --- labs/SAW/SAW.md | 146 ++++++++++++++++++++++++++++++------------------ 1 file changed, 93 insertions(+), 53 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 3e3dd870..120d086f 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -2,7 +2,7 @@ To run any of the examples in this lab, you need to first start the Software Analysis Workbench (SAW) remote API (`saw-remote-api`). If -you are using the development container that comes w/ this course +you are using the development container that comes with this course (`ghcr.io/weaversa/cryptol-course`), you can enter the following command in your terminal: @@ -41,6 +41,9 @@ class contractName(Contract): class testName(unittest.TestCase): def specificTestName(self): # Verify contracts + +if __name__ == "__main__": + unittest.main() ``` ## Imports @@ -107,7 +110,7 @@ External references: Definitions: i32 @RCS(i32 %0, i32 %1) -`` +``` The corresponding Cryptol specification for right circular shift is: @@ -168,38 +171,33 @@ can use `self.returns(void)`. ### Terms from Cryptol The command `cry(string)` converts a Python string into a -`CryptolTerm` that can be used in SAW. The `_f` indicates one can pass -in Python local variables into the strings by surrounding the variable -with braces as we did in `{bits} >>> {shift}`. In fact, `returns_f` is +`CryptolTerm` that can be used in SAW. The `cry_f(string)` command is similar to `cry`, +but the `_f` indicates one can pass Python local variables into the strings. To do this, surround the variable +with braces as we did in `returns_f("{bits} >>> {shift}")`. In fact, `returns_f` is just syntactic sugar for `returns(cry_f(string))`. The `CryptolTerm` class is a subclass of `SetupVal`. This allows using `CryptolTerm` as a `SetupVal`. -Braces are sometimes used in Cryptol to assign type parameters or -record types. To have the parser parse a literal brace in a Cryptol -string repeat the `{` or `}` symbols. For example, consider the -following made up SAW line: +Braces are sometimes used in Cryptol to assign type parameters or declare records. +The symbols `{{` and `}}` are used to denote literal braces for these cases when parsing Python strings. For example, let's think how to parse the following line: ```python - self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} blah }} == foo `blah") + self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} {blah} }} == foo `") ``` -Suppose `blah` is a local Python variable equal to `23` and `N` is a -local Python variable equal to `2`. The above string pasrses in -Cryptol as +If `blah` is a local Python variable equal to `23` and `N` is a +local Python variable equal to `2`, then the string parses in Cryptol as ```cryptol {a = take`{5} 23, b = take`{2} 23} == foo `blah ``` -where `foo` is some Cryptol function returning a record, and `blah` is +where `foo` is some Cryptol function returning a record and `blah` is some Cryptol type in the specification loaded. ## Unit Testing -Hopefully SAW alerts us of this behavior. To check, we need to make a test: - ```python class RCSTest(unittest.TestCase): def test_RCS(self): @@ -436,26 +434,43 @@ second to the first. One way to write this is to write a function that just mutates the first array and returns nothing: ```C - +void addRow5Mutate(uint32_t a[5], uint32_t b[5]) { + for(int i = 0 ; i < 5; i++) { + a[i] += b[i]; + } + return; +} ``` An alternative way to do this would be to create a new array storing the result and return it: ```C - +uint32_t* addRow5NewVar(uint32_t a[5], uint32_t b[5]) { + uint32_t* c = (uint32_t*) malloc(5*sizeof(uint32_t)); + for(int i = 0 ; i < 5; i++) { + c[i] = a[i] + b[i]; + } + return c; +} ``` Finally, we could mutate the first input and return it: ```C - +uint32_t* addRow5Alias(uint32_t a[5], uint32_t b[5]) { + for(int i = 0 ; i < 5; i++) { + a[i] += b[i]; + } + return a; +} ``` The corresponding Cryptol specification is: ```cryptol - +addRow5 : [4][32] -> [4][32] -> [4][32] +addRow5 a b = a + b ``` ### Initializing Arrays and Pointers @@ -464,7 +479,7 @@ To initialize the arrays and pointers we'll use the `alloc` command and `array_ty` type: ```python -def Array5AddMutate_Contract(Contract): +def addRow5Mutate_Contract(Contract): def specification(self): a = self.fresh_var(array_ty(5, i32), "a") a_p = self.alloc(array_ty(5, i32), points_to=a) @@ -518,7 +533,7 @@ constraints. To see this in action, let's rewrite our previous contract: ```python -def Array5AddMutate_Contract(Contract): +def addRow5Mutate_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) @@ -544,7 +559,7 @@ new fresh symbolic variables. For example, consider the proposed contract for the next C function: ```python -def Array5AddNewVar_Contract(Contract): +def addRow5NewVar_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) @@ -571,7 +586,7 @@ to the previous contract is moving the declaration of `c_p` to the postcondition block: ```python -def Array5AddNewVar_Contract(Contract): +def addRow5NewVar_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) @@ -583,14 +598,33 @@ def Array5AddNewVar_Contract(Contract): self.returns(c_p) ``` + +#### Python Wildcards + +Python supports wildcards, denoted by `_`, like Cryptol. Wildcards are placeholders for values we don't use. For example, we could rewrite the `Array5AddNewVar_Contract` as follows: + +```python +def addRow5NewVar_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + (_, c_p) = ptr_to_fresh(self, array_ty(5, i32), name="c") + self.points_to(c_p, cry_f("rowAdd {a} {b}")) + + self.returns(c_p) +``` + ### Postconditions and `points_to` Consider another implementation of the previous contract ```python -def Array5AddAlias_Contract(Contract): +def addRow5NewVar_Contract(Contract): def specification(self): - (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a", read_only=True) (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) self.execute_func(a_p, b_p) @@ -602,7 +636,7 @@ def Array5AddAlias_Contract(Contract): ``` One could replace `self.postcondition_f("{aPost} == rowAdd {a} {b}")` -with `self.points_to(aPost_p, cry_f("rowAdd {a} {b}"))`. A SAW +with `self.points_to(c_p, cry_f("rowAdd {a} {b}"))`. A SAW symbolic array translates into a Cryptol array and a Cryptol array translates into a SAW symbolic array. @@ -611,41 +645,43 @@ translates into a SAW symbolic array. Suppose our C code was written as ```C - +uint32_t* addRowAlias(uint32_t* a, uint32_t* b, uint32_t length) { + for(int i = 0 ; i < length; i++) { + a[i] += b[i]; + } + return a; +} ``` where the length of the array is not specified. The corresponding Cryptol code might be ```cryptol - +addRow : {length} (fin length) => [n][32] -> [n][32] -> [n][32] +addRow a b = a + b ``` -SAW does not have inductive reasoning capabilities. Suppose later in -our program we realize we want to verify the above function not just -for arrays of size 5, but also those of a different fixed size, -say 10. One option would be to just make a new contract. However, -rewriting the same contract over and over is a waste of code. Instead, -we could supply parameters to the Contract and write it once: +SAW does not have inductive reasoning capabilities. One option would be to make a new contract for +each `length` that's actually used. Instead we can make a single contract with a length input: ```python -def arrayAddNewVar_Contract(Contract): +def addRowAlias_Contract(Contract): def __init__(self, length : int): super().__init__() self.length = length def specification(self): - (a, a_p) = ptr_to_fresh(self, array_ty(length, i32), name="a") - (b, b_p) = ptr_to_fresh(self, array_ty(length, i32), name="b", read_only=True) + (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) self.execute_func(a_p, b_p) - self.points_to(a_p, cry_f("rowAdd {a} {b}")) + self.points_to(a_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) self.returns(void) ``` -Then we have a unit test for each size: +However, we still need to make a test for each length encountered: ```python class ArrayTests(unittest.TestCase): @@ -659,14 +695,16 @@ class ArrayTests(unittest.TestCase): cryptol_load_file(cryname) mod = llvm_load_module(bcname) - arrayAddNewVar05_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(5)) - arrayAddNewVar10_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(10)) - self.assertIs(arrayAddNewVar05_result.is_success(), True) - self.assertIs(arrayAddNewVar10_result.is_success(), True) + arrayAddNewVar05_result = llvm_verify(mod, 'addRowAlias', addRowAlias_Contract(5)) + arrayAddNewVar10_result = llvm_verify(mod, 'addRowAlias', addRowAlias_Contract(10)) + self.assertIs(addRowAlias05_result.is_success(), True) + self.assertIs(addRowAlias10_result.is_success(), True) ``` ### Array Example Full Code +//Fix this + ```python import unittest from saw_client import * @@ -704,14 +742,15 @@ def arrayAddNewVar_Contract(Contract): self.length = length def specification(self): - (a, a_p) = ptr_to_fresh(self, array_ty(length, i32), name="a") - (b, b_p) = ptr_to_fresh(self, array_ty(length, i32), name="b", read_only=True) + (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a", read_only=True) + (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) self.execute_func(a_p, b_p) - self.points_to(a_p, cry_f("rowAdd {a} {b}")) + (_, c_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="c") + self.points_to(c_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) - self.returns(void) + self.returns(c_p) def array5AddAlias_Contract(Contract): def specification(self): @@ -720,10 +759,9 @@ def array5AddAlias_Contract(Contract): self.execute_func(a_p, b_p) - (aPost, aPost_p) = ptr_to_fresh(self, array_ty(5, i32), name="aPost") - self.postcondition_f("{aPost} == rowAdd {a} {b}") + self.points_to(a_p, cry_f("addRow5 {a} {b}")) - self.returns(aPost_p) + self.returns(a_p) class ArrayTests(unittest.TestCase): def test_rowAdds(self): @@ -750,6 +788,8 @@ class ArrayTests(unittest.TestCase): ### Preconditions and Indices +//SHOW SOMETHING FAILING BECAUSE LACK OF PRECONDITION. For this could have a function that takes in an arbitrary sized array, but the function that uses it only has an array of size 5. Then explain how the SAW, when analyzing the first function in isolation, has no way of knowing the input in use cases actually has fixed size 5 so you need a precondition + A common situation where one might use `precondition_f` is when an integer is passed into a function and is used to index into an array: @@ -794,7 +834,7 @@ implicitly as before and then pass this array to a Cryptol specification for postconditions as was done in the previous examples. -## Structs +# Structs //Dj help @@ -811,7 +851,7 @@ typedef struct { ### Structs in Cryptol -# Ask Sean for name? Compositional Logic? +# Using Gained Knowledge ## Assumptions and Lemmas From 3aa38ffa3311bc24f4649c4ad5631748c3ece9ce Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Fri, 27 May 2022 13:20:59 -0400 Subject: [PATCH 015/120] Update SAW.md --- labs/SAW/SAW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 120d086f..67983d9c 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -23,7 +23,7 @@ $ saw-remote-api http --host 0.0.0.0 --port 36691 & Congrats! You now have a server version of SAW running in the background ready to accept commands on port `36691`. -- directory layout +- directory layout //maybe here put something about where to find these files in the directory structure and how to run them? # Python Contracts Introduction From 754f138d1f0dd2321c4c4828e130f25b58ec13e4 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Fri, 27 May 2022 13:21:55 -0400 Subject: [PATCH 016/120] Update SAW.md --- labs/SAW/SAW.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 67983d9c..fa6e95ca 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -427,6 +427,8 @@ OK # Pointers, Arrays, and Structs +// There are many subsections to this section... does it seem cluttered or unorganized? + ## Pointers and Arrays We'll begin by writing a function that given two arrays adds the From 0f907bbf12595551f4bf389159a7e643ef86b3cd Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 27 May 2022 18:49:40 +0000 Subject: [PATCH 017/120] Modified SAW Game example file layout --- labs/SAW/Game/Makefile | 11 +++++++++-- labs/SAW/Game/proof/Player.py | 9 +++++++++ labs/SAW/Game/{ => src}/Player.c | 0 labs/SAW/Game/{ => src}/Player.h | 0 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 labs/SAW/Game/proof/Player.py rename labs/SAW/Game/{ => src}/Player.c (100%) rename labs/SAW/Game/{ => src}/Player.h (100%) diff --git a/labs/SAW/Game/Makefile b/labs/SAW/Game/Makefile index 5eca2a23..cf524bd3 100644 --- a/labs/SAW/Game/Makefile +++ b/labs/SAW/Game/Makefile @@ -1,4 +1,11 @@ +# Variables to hold relative file paths +SRC=src +PROOF=proof +ARTIFACTS=artifacts + all: - clang -c -emit-llvm -o Player.bc Player.c + mkdir $(ARTIFACTS) + clang -c -emit-llvm -o $(ARTIFACTS)/Player.bc $(SRC)/Player.c + clean: - rm -f *.bc \ No newline at end of file + rm -r $(ARTIFACTS) \ No newline at end of file diff --git a/labs/SAW/Game/proof/Player.py b/labs/SAW/Game/proof/Player.py new file mode 100644 index 00000000..2a6fc1e7 --- /dev/null +++ b/labs/SAW/Game/proof/Player.py @@ -0,0 +1,9 @@ +# Imports +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * +from saw_client.proofscript import * +from saw_client.llvm_type import * + +# TODO: Write the SAW contract and verify! \ No newline at end of file diff --git a/labs/SAW/Game/Player.c b/labs/SAW/Game/src/Player.c similarity index 100% rename from labs/SAW/Game/Player.c rename to labs/SAW/Game/src/Player.c diff --git a/labs/SAW/Game/Player.h b/labs/SAW/Game/src/Player.h similarity index 100% rename from labs/SAW/Game/Player.h rename to labs/SAW/Game/src/Player.h From 6942eb43d90cb833f3ab20960e6ef87486b76556 Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 27 May 2022 19:45:33 +0000 Subject: [PATCH 018/120] Added SAW contract setup for simple Player function & modified Game config --- labs/SAW/Game/Makefile | 8 +++++-- labs/SAW/Game/proof/Player.py | 39 ++++++++++++++++++++++++++++++++-- labs/SAW/Game/specs/Player.cry | 4 ++++ labs/SAW/Game/src/Player.c | 7 ++++++ 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 labs/SAW/Game/specs/Player.cry diff --git a/labs/SAW/Game/Makefile b/labs/SAW/Game/Makefile index cf524bd3..b4104be7 100644 --- a/labs/SAW/Game/Makefile +++ b/labs/SAW/Game/Makefile @@ -1,11 +1,15 @@ # Variables to hold relative file paths SRC=src PROOF=proof +SPECS=specs ARTIFACTS=artifacts -all: - mkdir $(ARTIFACTS) +all: artifacts clang -c -emit-llvm -o $(ARTIFACTS)/Player.bc $(SRC)/Player.c + python3 $(PROOF)/Player.py + +artifacts: + mkdir $(ARTIFACTS) clean: rm -r $(ARTIFACTS) \ No newline at end of file diff --git a/labs/SAW/Game/proof/Player.py b/labs/SAW/Game/proof/Player.py index 2a6fc1e7..c12061ec 100644 --- a/labs/SAW/Game/proof/Player.py +++ b/labs/SAW/Game/proof/Player.py @@ -1,4 +1,4 @@ -# Imports +import os import unittest from saw_client import * from saw_client.crucible import * @@ -6,4 +6,39 @@ from saw_client.proofscript import * from saw_client.llvm_type import * -# TODO: Write the SAW contract and verify! \ No newline at end of file + +# Helper functions +def ptr_to_fresh(spec: Contract, ty: LLVMType, + name: str) -> Tuple[FreshVar, SetupVal]: + var = spec.fresh_var(ty, name) + ptr = spec.alloc(ty, points_to = var) + return (var, ptr) + + +# levelUp Contract +class levelUp_Contract(Contract): + def specification (self): + level = self.fresh_var(i32, "level") + + self.execute_func(level) + + self.returns_f("levelUp {level}") + +# levelUp Unit Test +class levelUpTest(unittest.TestCase): + def test_levelUp(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + pwd = os.getcwd() + bitcode_name = pwd + "/artifacts/Player.bc" + cryptol_name = pwd + "/specs/Player.cry" + + cryptol_load_file(cryptol_name) + module = llvm_load_module(bitcode_name) + + levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) + self.assertIs(levelUp_result.is_success(), True) + +if __name__ == "__main__": + unittest.main() diff --git a/labs/SAW/Game/specs/Player.cry b/labs/SAW/Game/specs/Player.cry new file mode 100644 index 00000000..50b58bf8 --- /dev/null +++ b/labs/SAW/Game/specs/Player.cry @@ -0,0 +1,4 @@ +module Player where + +levelUp : [32] -> [32] +levelUp level = level + 1 diff --git a/labs/SAW/Game/src/Player.c b/labs/SAW/Game/src/Player.c index ee461a44..bfa848bb 100644 --- a/labs/SAW/Game/src/Player.c +++ b/labs/SAW/Game/src/Player.c @@ -1,5 +1,12 @@ #include "Player.h" + +uint32_t levelUp(uint32_t level) +{ + return (level + 1); +} + + uint32_t initializeDefaultPlayer(player_t* player) { // Variables From da5dabbbcba312a77c5ed629a06032adb305d3cf Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 27 May 2022 21:41:49 +0000 Subject: [PATCH 019/120] Added working SAW struct example for the Python API --- labs/SAW/Game/Makefile | 3 ++ labs/SAW/Game/proof/Player.py | 75 +++++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/labs/SAW/Game/Makefile b/labs/SAW/Game/Makefile index b4104be7..c040ad02 100644 --- a/labs/SAW/Game/Makefile +++ b/labs/SAW/Game/Makefile @@ -1,3 +1,6 @@ +# Note: Makefile assumes user already started the SAW remote API +# $ start-saw-remote-api + # Variables to hold relative file paths SRC=src PROOF=proof diff --git a/labs/SAW/Game/proof/Player.py b/labs/SAW/Game/proof/Player.py index c12061ec..69344b7b 100644 --- a/labs/SAW/Game/proof/Player.py +++ b/labs/SAW/Game/proof/Player.py @@ -1,3 +1,7 @@ +####################################### +# Imporant Imports +####################################### + import os import unittest from saw_client import * @@ -7,15 +11,35 @@ from saw_client.llvm_type import * -# Helper functions +####################################### +# Defines and Constants +####################################### + +MAX_NAME_LENGTH = 12 +SUCCESS = 170 +FAILURE = 85 + + +####################################### +# SAW Helper Functions +####################################### + def ptr_to_fresh(spec: Contract, ty: LLVMType, name: str) -> Tuple[FreshVar, SetupVal]: var = spec.fresh_var(ty, name) ptr = spec.alloc(ty, points_to = var) return (var, ptr) +#def int_to_32_cryptol(length: int): +# return cryptol("`{i}:[32]".format(i=length)) + + +####################################### +# SAW Contracts +####################################### # levelUp Contract +# uint32_t levelUp(uint32_t level) class levelUp_Contract(Contract): def specification (self): level = self.fresh_var(i32, "level") @@ -24,8 +48,47 @@ def specification (self): self.returns_f("levelUp {level}") -# levelUp Unit Test -class levelUpTest(unittest.TestCase): + +# initializeDefaultPlayer Contract +# uint32_t initializeDefaultPlayer(player_t* player) +class initializeDefaultPlayer_Contract(Contract): + def specification (self): + # Pull the struct from the bitcode + player = self.alloc(alias_ty("struct.player_t")) + + self.execute_func(player) + + # Assert the post-condition behaviors + + # Index 0 = "name" field + # Recall that 0x41 is ASCII for 'A' + self.points_to(player[0], cry_f("[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]]")) + + # Index 1 is the "level" field + self.points_to(player[1], cry_f("1 : [32]")) + + # Index 2 is the "hp" field + self.points_to(player[2], cry_f("10 : [32]")) + + # Index 3 is the "atk" field + self.points_to(player[3], cry_f("5 : [32]")) + + # Index 4 is the "def" field + self.points_to(player[4], cry_f("4 : [32]")) + + # Index 5 is the "spd" field + self.points_to(player[5], cry_f("3 : [32]")) + + self.returns(cry_f("`({SUCCESS}) : [32]")) + +####################################### + + +####################################### +# Unit Tests +####################################### + +class PlayerTests(unittest.TestCase): def test_levelUp(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) @@ -37,8 +100,12 @@ def test_levelUp(self): cryptol_load_file(cryptol_name) module = llvm_load_module(bitcode_name) - levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) + levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) + initializeDefaultPlayer_result = llvm_verify(module, 'initializeDefaultPlayer', initializeDefaultPlayer_Contract()) self.assertIs(levelUp_result.is_success(), True) + self.assertIs(initializeDefaultPlayer_result.is_success(), True) if __name__ == "__main__": unittest.main() + +####################################### \ No newline at end of file From f8f2b1f8911ed2696254f1d712bcf7e971af3dc0 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Sat, 28 May 2022 21:48:33 -0400 Subject: [PATCH 020/120] Added f-string comment --- labs/SAW/SAW.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index fa6e95ca..7b53ca6e 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -171,16 +171,21 @@ can use `self.returns(void)`. ### Terms from Cryptol The command `cry(string)` converts a Python string into a -`CryptolTerm` that can be used in SAW. The `cry_f(string)` command is similar to `cry`, -but the `_f` indicates one can pass Python local variables into the strings. To do this, surround the variable -with braces as we did in `returns_f("{bits} >>> {shift}")`. In fact, `returns_f` is -just syntactic sugar for `returns(cry_f(string))`. +`CryptolTerm` that can be used in SAW. The `cry_f(string)` command is +similar to `cry`, but the `_f` indicates one can pass Python local +variables into the strings. To do this, surround the variable with +braces as we did in `returns_f("{bits} >>> {shift}")`. In fact, +`returns_f` is just syntactic sugar for `returns(cry_f(string))`. In +general, `cry_f` and friends are mostly a wrapper around [formatted string literals](https://docs.python.org/3/tutorial/inputoutput.html#formatted-string-literals) +called "f-strings". The `CryptolTerm` class is a subclass of `SetupVal`. This allows using `CryptolTerm` as a `SetupVal`. -Braces are sometimes used in Cryptol to assign type parameters or declare records. -The symbols `{{` and `}}` are used to denote literal braces for these cases when parsing Python strings. For example, let's think how to parse the following line: +Braces are sometimes used in Cryptol to assign type parameters or +declare records. The symbols `{{` and `}}` are used to denote literal +braces for these cases when parsing Python strings. For example, +let's think how to parse the following line: ```python self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} {blah} }} == foo `") From 1bab983a1f528a2783894f6d2ce90f4e8aa1a874 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Sat, 28 May 2022 21:57:00 -0400 Subject: [PATCH 021/120] Added comment about Coq and inductive reasoning --- labs/SAW/SAW.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 7b53ca6e..67f260cb 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -661,15 +661,23 @@ uint32_t* addRowAlias(uint32_t* a, uint32_t* b, uint32_t length) { ``` where the length of the array is not specified. The corresponding -Cryptol code might be +Cryptol code might be: ```cryptol addRow : {length} (fin length) => [n][32] -> [n][32] -> [n][32] addRow a b = a + b ``` -SAW does not have inductive reasoning capabilities. One option would be to make a new contract for -each `length` that's actually used. Instead we can make a single contract with a length input: +SAW currently does not have inductive reasoning capabilities and so +can only reason about concrete types. However, SAWCore terms can be +[formalized in Coq](https://github.com/GaloisInc/saw-script/tree/master/saw-core-coq), +a much more powerful theorem prover that has inductive reasoning (and +many more) capabilities. This is obviously well beyond the scope of +the course, but worth mentioning. + +One option here is to make a new contract for each `length` that's +actually used. Instead we can make a single contract with a length +input: ```python def addRowAlias_Contract(Contract): From 4675926fd039dcc77f2f2c5f8b9145762d0b9813 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Sat, 28 May 2022 22:01:05 -0400 Subject: [PATCH 022/120] Added comment about EUF --- labs/SAW/SAW.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 67f260cb..5a23eec0 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -935,13 +935,22 @@ what is specified in the `log2Contract()`. ## Uninterpreting Functions -An alternative to passing in lemmas is uninterpreting a funciton. This -is useful when you don't care about the specifics of the values -produced by a total function, but rather, the types of the value -produced. For example, hashing algorithms are often total -functions. We often don't care about the particular bits of a hash -function, but rather, that the hash function returned some 32-bit -integer. +Another way to simplify proofs is to uninterpret a function. This is +useful when you don't care about the specifics of the values produced +by a total function, but rather, the types of the value produced. For +example, hashing algorithms are often total functions. We often don't +care about the particular bits of a hash function, but rather, that +the hash function returned some 32-bit integer. + +This is also useful _in conjunction with lemmas_, reducing proof +complexity by decomposing a complex _specification_ into manageable +logical steps (constrained by the lemmas), much as verifying +function implementations and using results as lemmas does for the +corresponding _implementation_. + +See [SMT: Equality Logic With Uninterpreted Functions](https://www21.in.tum.de/teaching/sar/SS20/6.pdf), which describes +how uninterpreted functions and constraints are applied to +Satisfiability Modulo Theories. //add in use when function complicated //add uninterpreting may cause failure (logic needed sometimes). This can be avoided sometimes by making sure for every function you are uninterpreting you are also passing in a corresponding lemma From fabdf852d24a7517203861cf94e89d04cb5b26ce Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Sat, 28 May 2022 22:04:56 -0400 Subject: [PATCH 023/120] ispell ftw --- labs/SAW/SAW.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 5a23eec0..d9458655 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -82,14 +82,14 @@ uint32_t RCS(uint32_t bits, uint32_t shift) { SAW doesn't actually verify C source, but rather C compiled down to LLVM intermediate representation (IR), or bitcode. This can be accomplished via the `clang` compiler. In this instance, we can create -the bitcode by entering the follwing command in a terminal: +the bitcode by entering the following command in a terminal: ```sh $ clang -emit-llvm labs/SAW/src/rcs.c -c -o labs/SAW/src/rcs.bc ``` We can inspect the bitcode using SAW by loading the module and -printing some metadata. +printing some meta-data. ```Xsaw-session $ saw @@ -234,7 +234,7 @@ Let's break down the first few lines of this function: messages, then just use `if __name__ == "__main__": view(LogResults())` - The line `bcname = "/some/path/to/your/file.bc"` declares which - bitcode file we're analyzing. If you have multiple bitcodes files, + bitcode file we're analyzing. If you have multiple bitcode files, then make a variable for each file. - The line `mod = llvm_load_module(bcname)` creates the object we will pass to verification that represents the bitcode. @@ -399,7 +399,7 @@ Traceback (most recent call last): Aha! The counter example shows that we forgot about the case when `shift` is zero! This causes `(sizeof(bits) * 8 - 0)` to be `32`, -which is equal to the wordsize of `bits`, and hence causes `<<` to +which is equal to the word-size of `bits`, and hence causes `<<` to exhibit undefined behavior. Let's try again with @@ -512,7 +512,7 @@ def addRow5Mutate_Contract(Contract): Since arrays are passed as pointers in C, when we call `execute_func` we supply `a_p` and `b_p` rather than `a` and `b`. -To verify correctness, we assert that after function exectution `a_p` +To verify correctness, we assert that after function execution `a_p` points to what the Cryptol specification claims it should using `self.points_to(a_p, cry_f("rowAdd {a} {b}"))`. @@ -552,7 +552,7 @@ def addRow5Mutate_Contract(Contract): self.returns(void) ``` -### Auxilary Variables +### Auxiliary Variables A SAW contract is strictly divided into three parts: 1. Preconditions @@ -870,7 +870,7 @@ typedef struct { ## Assumptions and Lemmas -Sometimes one might want to blackbox certain parts of the verification +Sometimes one might want to black-box certain parts of the verification such as functions coming from libraries, especially if they have already been formally verified. For example, the height of [height-balanced binary From 2de82aaf261e25ab23f943bf50a6ffb93753b811 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Sat, 28 May 2022 22:21:58 -0400 Subject: [PATCH 024/120] Added each refinement of rotate to make lab more streamlined --- labs/SAW/SAW.md | 12 +++++------- labs/SAW/src/rcs.c | 2 -- labs/SAW/src/rcs2.c | 8 ++++++++ labs/SAW/src/rcs3.c | 9 +++++++++ 4 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 labs/SAW/src/rcs2.c create mode 100644 labs/SAW/src/rcs3.c diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index d9458655..2a2bc220 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -298,7 +298,7 @@ We can now run the proof script. ``` $ cd labs/SAW/proof $ python3 rcs.py -ifying RCS ... +[03:08:29.986] Verifying RCS ... [03:08:29.987] Simulating RCS ... [03:08:29.988] Checking proof obligations RCS ... [03:08:30.007] Subgoal failed: RCS safety assertion: @@ -353,7 +353,7 @@ SAW also provides a handy counterexample, namely, when `shift = One remedy to this is the following: -``` +```C uint32_t RCS(uint32_t bits, uint32_t shift) { shift %= sizeof(bits) * 8; return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); @@ -362,10 +362,8 @@ uint32_t RCS(uint32_t bits, uint32_t shift) { Recompiling and running SAW gives: -``` -rcs.py:30): -error: Proof failed. - stdout: +```sh +$ clang ../src/rcs2.c -o ../src/rcs.bc -c -emit-llvm && python3 rcs.py [03:11:54.334] Verifying RCS ... [03:11:54.334] Simulating RCS ... [03:11:54.335] Checking proof obligations RCS ... @@ -416,7 +414,7 @@ Finally, SAW is happy. More importantly, the C is correct and free of undefined behavior. ``` -$ clang ../src/rcs.c -o ../src/rcs.bc -c -emit-llvm && python3 rcs.py +$ clang ../src/rcs3.c -o ../src/rcs.bc -c -emit-llvm && python3 rcs.py [03:14:09.561] Verifying RCS ... [03:14:09.562] Simulating RCS ... [03:14:09.563] Checking proof obligations RCS ... diff --git a/labs/SAW/src/rcs.c b/labs/SAW/src/rcs.c index cff451af..43cd4cda 100644 --- a/labs/SAW/src/rcs.c +++ b/labs/SAW/src/rcs.c @@ -3,7 +3,5 @@ #include uint32_t RCS(uint32_t bits, uint32_t shift) { - shift %= sizeof(bits) * 8; - if(shift == 0) return bits; return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); } diff --git a/labs/SAW/src/rcs2.c b/labs/SAW/src/rcs2.c new file mode 100644 index 00000000..a69ee8c3 --- /dev/null +++ b/labs/SAW/src/rcs2.c @@ -0,0 +1,8 @@ +#include +#include +#include + +uint32_t RCS(uint32_t bits, uint32_t shift) { + shift %= sizeof(bits) * 8; + return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); +} diff --git a/labs/SAW/src/rcs3.c b/labs/SAW/src/rcs3.c new file mode 100644 index 00000000..cff451af --- /dev/null +++ b/labs/SAW/src/rcs3.c @@ -0,0 +1,9 @@ +#include +#include +#include + +uint32_t RCS(uint32_t bits, uint32_t shift) { + shift %= sizeof(bits) * 8; + if(shift == 0) return bits; + return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); +} From 824fefd799fbe16479485075f45022588a2eff95 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Sat, 28 May 2022 22:29:32 -0400 Subject: [PATCH 025/120] Adding formatting directives to code fences --- labs/SAW/SAW.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 2a2bc220..852e13d7 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -256,7 +256,7 @@ using `self.assertIs(RCS_result.is_success(), True)`. The full code is: -```python3 +```python import os import unittest from saw_client import * @@ -402,7 +402,7 @@ exhibit undefined behavior. Let's try again with -``` +```C uint32_t RCS(uint32_t bits, uint32_t shift) { shift %= sizeof(bits)*8; if(shift == 0) return bits; @@ -413,7 +413,7 @@ uint32_t RCS(uint32_t bits, uint32_t shift) { Finally, SAW is happy. More importantly, the C is correct and free of undefined behavior. -``` +```sh $ clang ../src/rcs3.c -o ../src/rcs.bc -c -emit-llvm && python3 rcs.py [03:14:09.561] Verifying RCS ... [03:14:09.562] Simulating RCS ... @@ -579,7 +579,7 @@ def addRow5NewVar_Contract(Contract): Running a unit test yields the following error message: -``` +```sh SAW error message ``` @@ -626,7 +626,7 @@ def addRow5NewVar_Contract(Contract): Consider another implementation of the previous contract -```python +```python def addRow5NewVar_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a", read_only=True) From fe2375bad6a3a37827b7c1dad1f507de759cfb0a Mon Sep 17 00:00:00 2001 From: djmorel Date: Sun, 29 May 2022 22:42:13 +0000 Subject: [PATCH 026/120] Added new player function for SAW verification --- labs/SAW/Game/proof/Player.py | 77 +++++++++++++++++++++++++++++++--- labs/SAW/Game/specs/Player.cry | 5 +++ labs/SAW/Game/src/Player.c | 20 +++++++++ labs/SAW/Game/src/Player.h | 20 ++++++++- 4 files changed, 116 insertions(+), 6 deletions(-) diff --git a/labs/SAW/Game/proof/Player.py b/labs/SAW/Game/proof/Player.py index 69344b7b..f28e678a 100644 --- a/labs/SAW/Game/proof/Player.py +++ b/labs/SAW/Game/proof/Player.py @@ -18,6 +18,7 @@ MAX_NAME_LENGTH = 12 SUCCESS = 170 FAILURE = 85 +MAX_STAT = 100 ####################################### @@ -30,9 +31,13 @@ def ptr_to_fresh(spec: Contract, ty: LLVMType, ptr = spec.alloc(ty, points_to = var) return (var, ptr) -#def int_to_32_cryptol(length: int): -# return cryptol("`{i}:[32]".format(i=length)) - +# Function that uses an existing SetupVal pointer +# Used for situations where pointer fields change during a function call +def ptr_to_existing(spec: Contract, ty: LLVMType, + name: str, ptr: SetupVal) -> Tuple[FreshVar, SetupVal]: + var = spec.fresh_var(ty, name) + spec.points_to(ptr, var) + return (var, ptr) ####################################### # SAW Contracts @@ -54,7 +59,9 @@ def specification (self): class initializeDefaultPlayer_Contract(Contract): def specification (self): # Pull the struct from the bitcode - player = self.alloc(alias_ty("struct.player_t")) + # Although the function uses player_t, pass character_t to SAW since + # player_t is an alternative typedef name for character_t. + player = self.alloc(alias_ty("struct.character_t")) self.execute_func(player) @@ -81,6 +88,60 @@ def specification (self): self.returns(cry_f("`({SUCCESS}) : [32]")) + +# resolveAttack Contract +# void resolveAttack(character_t* target, uint32_t atk) +class resolveAttack_Contract(Contract): + def __init__(self, case : int): + super().__init__() + self.case = case + # There are 3 possible cases for resolveAttack + # Case 1: Attack mitigated + # Case 2: Immediate KO + # Case 3: Regular attack + # Each case results in a different function behavior for calculating the + # target's remaining HP. While we could make 3 separate contracts to handle + # all of the possible cases, we can pass a parameter to the contract, which + # identifies what preconditions and postconditions to set. + + def specification (self): + #name = array + #hp = self.fresh_var(i32, "hp") + #defense = self.fresh_var(i32, "def") + #target = self.alloc(struct_ty(alias_ty("struct.character_t")), points_to = struct(hp)) + (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") + atk = self.fresh_var(i32, "atk") + + # Assert the precondition that the stats are below the max stat cap + self.precondition_f("{atk} <= `{MAX_STAT}") + self.precondition_f("({target}).2 <= `{MAX_STAT}") + self.precondition_f("({target}).4 <= `{MAX_STAT}") + + # Determine the preconditions based on the case parameter + if (self.case == 1): + # target->def >= atk + self.precondition_f("({target}).4 >= {atk}") + elif (self.case == 2): + # target->hp <= (atk - target->def) + self.precondition_f("(({target}).2 + ({target}).4) <= {atk}") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("({target}).4 < {atk}") + self.precondition_f("(({target}).2 + ({target}).4) > {atk}") + + # Symbolically execute the function + self.execute_func(target_p, atk) + + # Determine the postcondition based on the case parameter + if (self.case == 1): + self.points_to(target_p[2], cry_f("({target}).2 : [32]")) + elif (self.case == 2): + self.points_to(target_p[2], cry_f("0 : [32]")) + else: + self.points_to(target_p[2], cry_f("resolveAttack (({target}).2) (({target}).4) {atk}")) + + self.returns(void) + ####################################### @@ -89,7 +150,7 @@ def specification (self): ####################################### class PlayerTests(unittest.TestCase): - def test_levelUp(self): + def test_Player(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) @@ -102,8 +163,14 @@ def test_levelUp(self): levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) initializeDefaultPlayer_result = llvm_verify(module, 'initializeDefaultPlayer', initializeDefaultPlayer_Contract()) + resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) + resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) + resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) self.assertIs(levelUp_result.is_success(), True) self.assertIs(initializeDefaultPlayer_result.is_success(), True) + self.assertIs(resolveAttack_case1_result.is_success(), True) + self.assertIs(resolveAttack_case2_result.is_success(), True) + self.assertIs(resolveAttack_case3_result.is_success(), True) if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/Game/specs/Player.cry b/labs/SAW/Game/specs/Player.cry index 50b58bf8..aef7800a 100644 --- a/labs/SAW/Game/specs/Player.cry +++ b/labs/SAW/Game/specs/Player.cry @@ -2,3 +2,8 @@ module Player where levelUp : [32] -> [32] levelUp level = level + 1 + +resolveAttack : [32] -> [32] -> [32] -> [32] +resolveAttack hp def atk = hp' + where + hp' = hp - (atk - def) diff --git a/labs/SAW/Game/src/Player.c b/labs/SAW/Game/src/Player.c index bfa848bb..a0d680a6 100644 --- a/labs/SAW/Game/src/Player.c +++ b/labs/SAW/Game/src/Player.c @@ -32,3 +32,23 @@ uint32_t initializeDefaultPlayer(player_t* player) return SUCCESS; } + +void resolveAttack(character_t* target, uint32_t atk) +{ + if ( target->def >= atk) + { + // The target's defense mitigates the attack + target->hp = target->hp; + } + else if ( target->hp <= (atk - target->def) ) + { + // The attack will knock out the target + target->hp = 0; + } + + else + { + // Calculate damage as normal + target->hp = target->hp - (atk - target->def); + } +} diff --git a/labs/SAW/Game/src/Player.h b/labs/SAW/Game/src/Player.h index bd713a94..bca9b47c 100644 --- a/labs/SAW/Game/src/Player.h +++ b/labs/SAW/Game/src/Player.h @@ -6,6 +6,8 @@ #define MAX_NAME_LENGTH 12 #define SUCCESS 170 // 170 = 0xAA = 10101010 #define FAILURE 85 // 85 = 0x55 = 01010101 +#define MAX_STAT 100 + typedef struct { uint8_t name[MAX_NAME_LENGTH]; @@ -14,10 +16,26 @@ typedef struct { uint32_t atk; uint32_t def; uint32_t spd; -} player_t; +} character_t; + +typedef character_t player_t; +typedef struct { + uint32_t id; + uint32_t quantity; +} item_t; + +typedef struct { + item_t* item; + uint32_t numItems; +} inventory_t; // Function prototypes +uint32_t levelUp(uint32_t level); uint32_t initializeDefaultPlayer(player_t* player); +void resolveAttack(character_t* target, uint32_t atk); +void battle(player_t* player, character_t* enemy); + + #endif \ No newline at end of file From 969c1457cf34355a74b09e38c0190c71cadca6c3 Mon Sep 17 00:00:00 2001 From: djmorel Date: Sun, 29 May 2022 22:46:02 +0000 Subject: [PATCH 027/120] Renamed SAW Player.* example files to Game.* --- labs/SAW/Game/Makefile | 4 ++-- labs/SAW/Game/proof/{Player.py => Game.py} | 4 ++-- labs/SAW/Game/specs/{Player.cry => Game.cry} | 2 +- labs/SAW/Game/src/{Player.c => Game.c} | 2 +- labs/SAW/Game/src/{Player.h => Game.h} | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) rename labs/SAW/Game/proof/{Player.py => Game.py} (98%) rename labs/SAW/Game/specs/{Player.cry => Game.cry} (89%) rename labs/SAW/Game/src/{Player.c => Game.c} (98%) rename labs/SAW/Game/src/{Player.h => Game.h} (90%) diff --git a/labs/SAW/Game/Makefile b/labs/SAW/Game/Makefile index c040ad02..2487318f 100644 --- a/labs/SAW/Game/Makefile +++ b/labs/SAW/Game/Makefile @@ -8,8 +8,8 @@ SPECS=specs ARTIFACTS=artifacts all: artifacts - clang -c -emit-llvm -o $(ARTIFACTS)/Player.bc $(SRC)/Player.c - python3 $(PROOF)/Player.py + clang -c -emit-llvm -o $(ARTIFACTS)/Game.bc $(SRC)/Game.c + python3 $(PROOF)/Game.py artifacts: mkdir $(ARTIFACTS) diff --git a/labs/SAW/Game/proof/Player.py b/labs/SAW/Game/proof/Game.py similarity index 98% rename from labs/SAW/Game/proof/Player.py rename to labs/SAW/Game/proof/Game.py index f28e678a..8ccc4f79 100644 --- a/labs/SAW/Game/proof/Player.py +++ b/labs/SAW/Game/proof/Game.py @@ -155,8 +155,8 @@ def test_Player(self): if __name__ == "__main__": view(LogResults(verbose_failure=True)) pwd = os.getcwd() - bitcode_name = pwd + "/artifacts/Player.bc" - cryptol_name = pwd + "/specs/Player.cry" + bitcode_name = pwd + "/artifacts/Game.bc" + cryptol_name = pwd + "/specs/Game.cry" cryptol_load_file(cryptol_name) module = llvm_load_module(bitcode_name) diff --git a/labs/SAW/Game/specs/Player.cry b/labs/SAW/Game/specs/Game.cry similarity index 89% rename from labs/SAW/Game/specs/Player.cry rename to labs/SAW/Game/specs/Game.cry index aef7800a..e3807746 100644 --- a/labs/SAW/Game/specs/Player.cry +++ b/labs/SAW/Game/specs/Game.cry @@ -1,4 +1,4 @@ -module Player where +module Game where levelUp : [32] -> [32] levelUp level = level + 1 diff --git a/labs/SAW/Game/src/Player.c b/labs/SAW/Game/src/Game.c similarity index 98% rename from labs/SAW/Game/src/Player.c rename to labs/SAW/Game/src/Game.c index a0d680a6..e7c038f6 100644 --- a/labs/SAW/Game/src/Player.c +++ b/labs/SAW/Game/src/Game.c @@ -1,4 +1,4 @@ -#include "Player.h" +#include "Game.h" uint32_t levelUp(uint32_t level) diff --git a/labs/SAW/Game/src/Player.h b/labs/SAW/Game/src/Game.h similarity index 90% rename from labs/SAW/Game/src/Player.h rename to labs/SAW/Game/src/Game.h index bca9b47c..23929e2c 100644 --- a/labs/SAW/Game/src/Player.h +++ b/labs/SAW/Game/src/Game.h @@ -1,5 +1,5 @@ -#ifndef PLAYER_H -#define PLAYER_H +#ifndef GAME_H +#define GAME_H #include From 65d9b125b4f7ac3d2aaa977c6f7c70c3699c4117 Mon Sep 17 00:00:00 2001 From: djmorel Date: Tue, 31 May 2022 12:48:49 +0000 Subject: [PATCH 028/120] Added initial SAW pointer field example --- labs/SAW/Game/proof/Game.py | 50 +++++++++++++++++++++++++++---------- labs/SAW/Game/src/Game.c | 10 ++++++++ labs/SAW/Game/src/Game.h | 2 +- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 8ccc4f79..1df3aa8c 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -31,13 +31,6 @@ def ptr_to_fresh(spec: Contract, ty: LLVMType, ptr = spec.alloc(ty, points_to = var) return (var, ptr) -# Function that uses an existing SetupVal pointer -# Used for situations where pointer fields change during a function call -def ptr_to_existing(spec: Contract, ty: LLVMType, - name: str, ptr: SetupVal) -> Tuple[FreshVar, SetupVal]: - var = spec.fresh_var(ty, name) - spec.points_to(ptr, var) - return (var, ptr) ####################################### # SAW Contracts @@ -47,10 +40,13 @@ def ptr_to_existing(spec: Contract, ty: LLVMType, # uint32_t levelUp(uint32_t level) class levelUp_Contract(Contract): def specification (self): + # Declare level variable level = self.fresh_var(i32, "level") + # Symbolically execute the function self.execute_func(level) + # Assert the function's return behavior self.returns_f("levelUp {level}") @@ -105,10 +101,7 @@ def __init__(self, case : int): # identifies what preconditions and postconditions to set. def specification (self): - #name = array - #hp = self.fresh_var(i32, "hp") - #defense = self.fresh_var(i32, "def") - #target = self.alloc(struct_ty(alias_ty("struct.character_t")), points_to = struct(hp)) + # Declare variables (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") atk = self.fresh_var(i32, "atk") @@ -142,6 +135,35 @@ def specification (self): self.returns(void) + +# resetInventoryItems Contract +# void resetInventoryItems(inventory_t* inventory) +class resetInventoryItems_Contract(Contract): + def __init__(self, numItems : int): + super().__init__() + self.numItems = numItems + # Note: The inventory_t struct defines its item field as a item_t pointer. + # Instead of declaring a fixed array size, the pointer enables for a + # variable size depending on the memory allocation configured by its + # caller. + # While this is valid C syntax, SAW and Cryptol require the known + # size ahead of time. Here, our contract takes the numItems parameter + # to declare a fixed array size. + + def specification (self): + # Declare variables + (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) + + # Symbolically execute the function + self.execute_func(inventory_p) + + # Assert the postconditions + for i in range(self.numItems): + self.points_to(inventory_p[0][1], cry_f("0 : [32]")) + + self.returns(void) + ####################################### @@ -149,8 +171,8 @@ def specification (self): # Unit Tests ####################################### -class PlayerTests(unittest.TestCase): - def test_Player(self): +class GameTests(unittest.TestCase): + def test_Game(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) @@ -166,11 +188,13 @@ def test_Player(self): resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) + resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) self.assertIs(levelUp_result.is_success(), True) self.assertIs(initializeDefaultPlayer_result.is_success(), True) self.assertIs(resolveAttack_case1_result.is_success(), True) self.assertIs(resolveAttack_case2_result.is_success(), True) self.assertIs(resolveAttack_case3_result.is_success(), True) + self.assertIs(resetInventoryItems_result.is_success(), True) if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index e7c038f6..cd9369f1 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -52,3 +52,13 @@ void resolveAttack(character_t* target, uint32_t atk) target->hp = target->hp - (atk - target->def); } } + + +void resetInventoryItems(inventory_t* inventory) +{ + // Iterate through the inventory and set the quantity field to 0 + for (int i = 0; i < inventory->numItems; i++) + { + inventory->item->quantity = 0; + } +} \ No newline at end of file diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index 23929e2c..a09c3d54 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -35,7 +35,7 @@ uint32_t levelUp(uint32_t level); uint32_t initializeDefaultPlayer(player_t* player); void resolveAttack(character_t* target, uint32_t atk); void battle(player_t* player, character_t* enemy); - +void resetInventoryItems(inventory_t* inventory); #endif \ No newline at end of file From b67b26813d05c39fa83c12bbfc9f721ef8e9bef0 Mon Sep 17 00:00:00 2001 From: djmorel Date: Tue, 31 May 2022 13:16:55 +0000 Subject: [PATCH 029/120] Updated SAW pointer field example with fixed size rework --- labs/SAW/Game/proof/Game.py | 2 +- labs/SAW/Game/src/Game.c | 2 +- labs/SAW/Game/src/Game.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 1df3aa8c..4839872c 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -160,7 +160,7 @@ def specification (self): # Assert the postconditions for i in range(self.numItems): - self.points_to(inventory_p[0][1], cry_f("0 : [32]")) + self.points_to(inventory_p[0][i][1], cry_f("0 : [32]")) self.returns(void) diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index cd9369f1..02d07b09 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -59,6 +59,6 @@ void resetInventoryItems(inventory_t* inventory) // Iterate through the inventory and set the quantity field to 0 for (int i = 0; i < inventory->numItems; i++) { - inventory->item->quantity = 0; + inventory->item[i].quantity = 0; } } \ No newline at end of file diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index a09c3d54..8835fe7c 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -26,7 +26,8 @@ typedef struct { } item_t; typedef struct { - item_t* item; + //item_t* item; + item_t item[5]; uint32_t numItems; } inventory_t; From 3690e536f67f7c73028bf7946c72dec956a159b9 Mon Sep 17 00:00:00 2001 From: djmorel Date: Tue, 31 May 2022 13:57:09 +0000 Subject: [PATCH 030/120] Added incorrect alternatives for struct postconditions --- labs/SAW/Game/proof/Game.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 4839872c..0e312c0d 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -62,7 +62,8 @@ def specification (self): self.execute_func(player) # Assert the post-condition behaviors - + # Note: The following explicit points_to method is currently the only way + # to assert struct field contents. # Index 0 = "name" field # Recall that 0x41 is ASCII for 'A' self.points_to(player[0], cry_f("[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]]")) @@ -82,6 +83,13 @@ def specification (self): # Index 5 is the "spd" field self.points_to(player[5], cry_f("3 : [32]")) + # Incorrect Alternative 1: Invalid label in record update. + #self.points_to(player, cry_f("{{ [(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) + + # Incorrect Alternative 2: SAW doesn't yet support translating Cryptol's + # record type(s) into crucible-llvm's type system. + #self.points_to(player, cry_f("{{ name=[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) + self.returns(cry_f("`({SUCCESS}) : [32]")) From 2ba24d9eb21ea574d10f42f69268cfcf9f3f8c6a Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Tue, 31 May 2022 12:26:56 -0400 Subject: [PATCH 031/120] Update SAW.md --- labs/SAW/SAW.md | 99 ++++++++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 58 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 852e13d7..41c39ce8 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -88,6 +88,8 @@ the bitcode by entering the following command in a terminal: $ clang -emit-llvm labs/SAW/src/rcs.c -c -o labs/SAW/src/rcs.bc ``` +(you may need to adjust the path name). + We can inspect the bitcode using SAW by loading the module and printing some meta-data. @@ -428,15 +430,11 @@ OK ✅ The goal was verified! ``` -# Pointers, Arrays, and Structs - -// There are many subsections to this section... does it seem cluttered or unorganized? +# Pointers and Arrays -## Pointers and Arrays - -We'll begin by writing a function that given two arrays adds the -second to the first. One way to write this is to write a function that -just mutates the first array and returns nothing: +We'll begin by writing a function that given two arrays of a common fixed size, say five, adds the +second to the first. One way to accomplish this is to pass in the two arrays, mutate the first +and return nothing: ```C void addRow5Mutate(uint32_t a[5], uint32_t b[5]) { @@ -474,14 +472,14 @@ uint32_t* addRow5Alias(uint32_t a[5], uint32_t b[5]) { The corresponding Cryptol specification is: ```cryptol -addRow5 : [4][32] -> [4][32] -> [4][32] +addRow5 : [5][32] -> [5][32] -> [5][32] addRow5 a b = a + b ``` ### Initializing Arrays and Pointers To initialize the arrays and pointers we'll use the `alloc` command -and `array_ty` type: +and `array_ty` type constructor: ```python def addRow5Mutate_Contract(Contract): @@ -510,12 +508,14 @@ def addRow5Mutate_Contract(Contract): Since arrays are passed as pointers in C, when we call `execute_func` we supply `a_p` and `b_p` rather than `a` and `b`. -To verify correctness, we assert that after function execution `a_p` -points to what the Cryptol specification claims it should using -`self.points_to(a_p, cry_f("rowAdd {a} {b}"))`. +To verify correctness, we assert after execution `a_p` +points to what the Cryptol specification claims it should: + +| `self.points_to(` | `a_p,` | `cry_f( "rowAdd {a} {b}"` | `))` | +|-------------------|--------|---------------------------|------| +| Assert in the current contract that the pointer | with this name | points to this Cryptol term | . | -A specification must contain a `self.returns` or `self.returns_f` -line. Since our function returns nothing, we use `self.returns(void)`. +Finally, `specification` must contain `self.returns` or `self.returns_f`, so we use `self.returns(void)`. ### Helper Functions @@ -532,7 +532,7 @@ def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None, read_ Given a contract and a type this function outputs a tuple `(var, ptr)` where `var` is a fresh symbolic variable of the given type and `ptr` is a pointer pointing to this variable. We give optional arguments to -name the fresh symbolic variable and to force read only pointer +name the fresh symbolic variable and to assert read-only pointer constraints. To see this in action, let's rewrite our previous contract: @@ -561,7 +561,7 @@ SAW will complain if you place a precondition after `execute_func` and similarly for postcondition. If a function returns a value that was not passed through `execute_func`, then you will have to initialize new fresh symbolic variables. For example, consider the proposed -contract for the next C function: +contract for `addRow5NewVar`: ```python def addRow5NewVar_Contract(Contract): @@ -604,9 +604,7 @@ def addRow5NewVar_Contract(Contract): self.returns(c_p) ``` -#### Python Wildcards - -Python supports wildcards, denoted by `_`, like Cryptol. Wildcards are placeholders for values we don't use. For example, we could rewrite the `Array5AddNewVar_Contract` as follows: +Python supports wildcards, denoted by `_`, like Cryptol. Wildcards are placeholders for values we don't use. For example, we could rewrite the `addRow5NewVar_Contract` as follows: ```python def addRow5NewVar_Contract(Contract): @@ -624,7 +622,7 @@ def addRow5NewVar_Contract(Contract): ### Postconditions and `points_to` -Consider another implementation of the previous contract +One could replace the `points_to` line with a `postcondition_f` line to get an equivalent contract: ```python def addRow5NewVar_Contract(Contract): @@ -640,10 +638,7 @@ def addRow5NewVar_Contract(Contract): self.returns(c_p) ``` -One could replace `self.postcondition_f("{aPost} == rowAdd {a} {b}")` -with `self.points_to(c_p, cry_f("rowAdd {a} {b}"))`. A SAW -symbolic array translates into a Cryptol array and a Cryptol array -translates into a SAW symbolic array. +While Cryptol has arrays as primitives, it does not have any notion of pointer, so we can not pass a symbolic pointer into `cry_f` (or any function with `_f`). For example, if we wanted to assert `a_p` points to `b_p` then we can use `self.points_to(a_p, b_p)` but **NOT** `self.postcondition_f(a_p, "{b_p}")`. ### Parameterized Contracts @@ -691,7 +686,7 @@ def addRowAlias_Contract(Contract): self.points_to(a_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) - self.returns(void) + self.returns(a_p) ``` However, we still need to make a test for each length encountered: @@ -716,8 +711,6 @@ class ArrayTests(unittest.TestCase): ### Array Example Full Code -//Fix this - ```python import unittest from saw_client import * @@ -726,7 +719,7 @@ from saw_client.llvm import * from saw_client.proofscript import * from saw_client.llvm_type import * -def array5AddMutate_Contract(Contract): +def addRow5Mutate_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) @@ -737,7 +730,7 @@ def array5AddMutate_Contract(Contract): self.returns(void) -def array5AddNewVar_Contract(Contract): +def addRow5NewVar_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) @@ -749,30 +742,18 @@ def array5AddNewVar_Contract(Contract): self.returns(c_p) -def arrayAddNewVar_Contract(Contract): +def addRowAlias_Contract(Contract): def __init__(self, length : int): super().__init__() self.length = length def specification(self): - (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a", read_only=True) + (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) self.execute_func(a_p, b_p) - (_, c_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="c") - self.points_to(c_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) - - self.returns(c_p) - -def array5AddAlias_Contract(Contract): - def specification(self): - (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") - (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) - - self.execute_func(a_p, b_p) - - self.points_to(a_p, cry_f("addRow5 {a} {b}")) + self.points_to(a_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) self.returns(a_p) @@ -781,22 +762,24 @@ class ArrayTests(unittest.TestCase): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) - bcname = "/some/path/to/your/file.bc" + bcname1 = "/some/path/to/your/file.bc" + bcname2 = "/some/path/to/your/file.bc" cryname = "/some/path/to/your/file.cry" cryptol_load_file(cryname) - mod = llvm_load_module(bcname) + mod1 = llvm_load_module(bcname) + mod2 = llvm_load_module(bcname) + + addRow5Mutate_result = llvm_verify(mod1, 'addRowMutate', addRowMutate_Contract()) + self.assertIs(addRow5Mutate_result.is_success(), True) - arrayAddNewMutate_result = llvm_verify(mod, 'arrayAddMutate', array5AddMutate_Contract()) - arrayAddNewVar5_result = llvm_verify(mod, 'arrayAddNewVar', array5AddNewVar_Contract()) - arrayAddNewVar05_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(5)) - arrayAddNewVar10_result = llvm_verify(mod, 'arrayAddNewVar', arrayAddNewVar_Contract(10)) - array5AddAlias_result = llvm_verify(mod, 'arrayAddAlias', array5AddAlias_Contract()) - self.assertIs(arrayAddNewMutate_result.is_success(), True) - self.assertIs( arrayAddNewVar5_result.is_success(), True) - self.assertIs( arrayAddNewVar05_result.is_success(), True) - self.assertIs( arrayAddNewVar10_result.is_success(), True) - self.assertIs( array5AddAlias_result.is_success(), True) + addRow5NewVar_result = llvm_verify(mod1, 'addRowNewVar', addRowNewVar_Contract()) + self.assertIs(addRow5NewVar_result.is_success(), True) + + addRowAlias05_result = llvm_verify(mod2, 'addRowAlias', addRowAlias_Contract(5)) + addAddAlias10_result = llvm_verify(mod2, 'addRowAlias', addRowAlias_Contract(10)) + self.assertIs(addRowAlias05_result.is_success(), True) + self.assertIs(addRowAlias10_result.is_success(), True) ``` ### Preconditions and Indices @@ -841,7 +824,7 @@ class ArraySwapContract(Contract): Explicit arrays can be useful when you want to assert a condition on a particular element of the array. Of course, a drawback is you have to -initialize every member of the array. However, this can be problematic +initialize every member of the array. This can be problematic for large arrays. Ideally, one would just have to declare the array implicitly as before and then pass this array to a Cryptol specification for postconditions as was done in the previous examples. From 3967104d2e3c2fe188cbbf82cea95d4975329f66 Mon Sep 17 00:00:00 2001 From: djmorel Date: Tue, 31 May 2022 17:20:17 +0000 Subject: [PATCH 032/120] Added simple SAW struct lemma example --- labs/SAW/Game/proof/Game.py | 84 ++++++++++++++++++++++++++++++----- labs/SAW/Game/src/Game.c | 87 ++++++++++++++++++++++++++++++++++++- labs/SAW/Game/src/Game.h | 25 +++++++++-- 3 files changed, 181 insertions(+), 15 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 0e312c0d..0257457f 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,3 +1,10 @@ +# TODO +# - Aliasing / Memory region disjoint example (resolveAttack) +# - Try the alloc_pointsto_buffer example & see its limitations + +# Done +# - Nested defines example (handled here...) (Mention in some SAW configs, this may be an issue) + ####################################### # Imporant Imports ####################################### @@ -19,6 +26,9 @@ SUCCESS = 170 FAILURE = 85 MAX_STAT = 100 +SCREEN_ROWS = 15 +SCREEN_COLS = 10 +SCREEN_TILES = SCREEN_ROWS * SCREEN_COLS ####################################### @@ -50,9 +60,9 @@ def specification (self): self.returns_f("levelUp {level}") -# initializeDefaultPlayer Contract -# uint32_t initializeDefaultPlayer(player_t* player) -class initializeDefaultPlayer_Contract(Contract): +# initDefaultPlayer Contract +# uint32_t initDefaultPlayer(player_t* player) +class initDefaultPlayer_Contract(Contract): def specification (self): # Pull the struct from the bitcode # Although the function uses player_t, pass character_t to SAW since @@ -172,6 +182,56 @@ def specification (self): self.returns(void) + +# initScreen Contract +# uint32_t initScreen(screen_t* screen, uint8_t assetID) +class initScreen_Contract(Contract): + def specification (self): + # Declare variables + screen = self.alloc(alias_ty("struct.screen_t")) + assetID = self.fresh_var(i8, "assetID") + + # Symbolically execute the function + self.execute_func(screen, assetID) + + # Assert the postcondition + self.points_to(screen[0], cry_f("[(i*0 + {assetID}) | i <- [1..{SCREEN_TILES}]]")) + + self.returns(cry_f("`({SUCCESS}) : [32]")) + + +# quickBattle Contract +# void quickBattle(player_t* player, character_t* opponent) +class quickBattle_Contract(Contract): + def specification (self): + # Declare variables + (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") + (opponent, opponent_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="opponent") + #player = self.alloc(alias_ty("struct.character_t")) + #opponent = self.alloc(alias_ty("struct.character_t")) + + # Assert the precondition that both HPs are greater than 0 + self.precondition_f("({player}).2 > 0") + self.precondition_f("({opponent}).2 > 0") + + # Assert the precondition that character stats are below the max stat cap + # Question: Explain why the proof fails when the following preconditions + # are commented out. + self.precondition_f("({player}).2 <= `{MAX_STAT}") + self.precondition_f("({player}).3 <= `{MAX_STAT}") + self.precondition_f("({player}).4 <= `{MAX_STAT}") + self.precondition_f("({player}).5 <= `{MAX_STAT}") + self.precondition_f("({opponent}).2 <= `{MAX_STAT}") + self.precondition_f("({opponent}).3 <= `{MAX_STAT}") + self.precondition_f("({opponent}).4 <= `{MAX_STAT}") + self.precondition_f("({opponent}).5 <= `{MAX_STAT}") + + # Symbolically execute the function + self.execute_func(player_p, opponent_p) + + # Assert the postcondition + self.returns(void) + ####################################### @@ -191,18 +251,22 @@ def test_Game(self): cryptol_load_file(cryptol_name) module = llvm_load_module(bitcode_name) - levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) - initializeDefaultPlayer_result = llvm_verify(module, 'initializeDefaultPlayer', initializeDefaultPlayer_Contract()) - resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) - resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) - resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) - resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) + levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) + initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) + resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) + resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) + resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) + resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) + initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) + quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) self.assertIs(levelUp_result.is_success(), True) - self.assertIs(initializeDefaultPlayer_result.is_success(), True) + self.assertIs(initDefaultPlayer_result.is_success(), True) self.assertIs(resolveAttack_case1_result.is_success(), True) self.assertIs(resolveAttack_case2_result.is_success(), True) self.assertIs(resolveAttack_case3_result.is_success(), True) self.assertIs(resetInventoryItems_result.is_success(), True) + self.assertIs(initScreen_result.is_success(), True) + self.assertIs(quickBattle_result.is_success(), True) if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index 02d07b09..cf549583 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -7,7 +7,7 @@ uint32_t levelUp(uint32_t level) } -uint32_t initializeDefaultPlayer(player_t* player) +uint32_t initDefaultPlayer(player_t* player) { // Variables uint8_t i = 0; @@ -45,7 +45,6 @@ void resolveAttack(character_t* target, uint32_t atk) // The attack will knock out the target target->hp = 0; } - else { // Calculate damage as normal @@ -61,4 +60,88 @@ void resetInventoryItems(inventory_t* inventory) { inventory->item[i].quantity = 0; } +} + + +uint32_t initScreen(screen_t* screen, uint8_t assetID) +{ + // Iterate through the screen tiles and set their asset ID + for (int i = 0; i < SCREEN_TILES; i++) + { + screen->tiles[i] = assetID; + } + + return SUCCESS; +} + + +// Simulates a simple battle where only the faster character attacks. +void quickBattle(player_t* player, character_t* opponent) +{ + if (player->spd >= opponent->spd) + { + resolveAttack(opponent, player->atk); + } + else + { + resolveAttack(player, opponent->atk); + } +} + + +// Simulates a complex battle where characters can counterattack as long as +// they survive to do so. +uint32_t counterBattle(player_t* player, character_t* opponent) +{ + enum battleResult result = NEUTRAL; + + // Check spd to determine who goes first + if (player->spd >= opponent->spd) + { + // Player attacks first + resolveAttack(opponent, player->atk); + + // Check if there is a counterattack + if (opponent->hp > 0) + { + // Opponent counterattacks + resolveAttack(player, opponent->atk); + + if (player->hp == 0) + { + result = DEFEAT_PLAYER; + } + } + else + { + // Opponent defeated + player->level = player->level + 1; + result = DEFEAT_OPPONENT; + } + } + else + { + // Opponent attacks first + resolveAttack(player, opponent->atk); + + // Check if there is a counterattack + if (player->hp > 0) + { + // Player counterattacks + resolveAttack(opponent, player->atk); + + if (opponent->hp == 0) + { + result = DEFEAT_OPPONENT; + player->level = player->level + 1; + } + } + else + { + // Player defeated + result = DEFEAT_PLAYER; + } + } + + return result; } \ No newline at end of file diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index 8835fe7c..fdaf9843 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -7,8 +7,11 @@ #define SUCCESS 170 // 170 = 0xAA = 10101010 #define FAILURE 85 // 85 = 0x55 = 01010101 #define MAX_STAT 100 +#define SCREEN_ROWS 15 +#define SCREEN_COLS 10 +#define SCREEN_TILES SCREEN_ROWS*SCREEN_COLS - +// Struct containing character information typedef struct { uint8_t name[MAX_NAME_LENGTH]; uint32_t level; @@ -20,23 +23,39 @@ typedef struct { typedef character_t player_t; +// Enum containing possible battle outcomes +enum battleResult{ + NEUTRAL = 0, + DEFEAT_PLAYER = 1, + DEFEAT_OPPONENT = 2 +}; + +// Struct containing item information typedef struct { uint32_t id; uint32_t quantity; } item_t; +// Struct containing inventory information typedef struct { //item_t* item; item_t item[5]; uint32_t numItems; } inventory_t; +// Struct containing screen information +typedef struct { + uint8_t tiles[SCREEN_TILES]; // Holds asset ID for each screen tile +} screen_t; + // Function prototypes uint32_t levelUp(uint32_t level); -uint32_t initializeDefaultPlayer(player_t* player); +uint32_t initDefaultPlayer(player_t* player); void resolveAttack(character_t* target, uint32_t atk); -void battle(player_t* player, character_t* enemy); void resetInventoryItems(inventory_t* inventory); +uint32_t initScreen(screen_t* screen, uint8_t assetID); +void quickBattle(player_t* player, character_t* opponent); +uint32_t counterBattle(player_t* player, character_t* opponent); #endif \ No newline at end of file From 5e8c1d6b8d78b615f1b3e9c008589e8d47aebc82 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Tue, 31 May 2022 14:20:27 -0400 Subject: [PATCH 033/120] Update SAW.md --- labs/SAW/SAW.md | 90 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 77 insertions(+), 13 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 41c39ce8..9bf29368 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -23,8 +23,6 @@ $ saw-remote-api http --host 0.0.0.0 --port 36691 & Congrats! You now have a server version of SAW running in the background ready to accept commands on port `36691`. -- directory layout //maybe here put something about where to find these files in the directory structure and how to run them? - # Python Contracts Introduction Here's the general layout for SAW in Python: @@ -517,7 +515,7 @@ points to what the Cryptol specification claims it should: Finally, `specification` must contain `self.returns` or `self.returns_f`, so we use `self.returns(void)`. -### Helper Functions +### Python Helper Functions To limit code reuse we can define helper functions in Python. For example, the following construct is often used: @@ -709,7 +707,7 @@ class ArrayTests(unittest.TestCase): self.assertIs(addRowAlias10_result.is_success(), True) ``` -### Array Example Full Code +## Array Example Full Code and Debugging SAW ```python import unittest @@ -782,14 +780,10 @@ class ArrayTests(unittest.TestCase): self.assertIs(addRowAlias10_result.is_success(), True) ``` -### Preconditions and Indices +If we try to run this Python we get: -//SHOW SOMETHING FAILING BECAUSE LACK OF PRECONDITION. For this could have a function that takes in an arbitrary sized array, but the function that uses it only has an array of size 5. Then explain how the SAW, when analyzing the first function in isolation, has no way of knowing the input in use cases actually has fixed size 5 so you need a precondition -A common situation where one might use `precondition_f` is when an -integer is passed into a function and is used to index into an array: - -### Explicit Arrays +## Explicit Arrays Another way to initialize arrays is through the `array` command. For example, suppose I have the following C function (taken from @@ -829,6 +823,76 @@ for large arrays. Ideally, one would just have to declare the array implicitly as before and then pass this array to a Cryptol specification for postconditions as was done in the previous examples. +## Null Pointers and Symbolic Pointers + +Consider the following C code that checks if a pointer is null: + +```C +int f(int *x) { + return (x == (int*)0); +} +``` + +What do you think is the behavior of running the following contract? Keep in mind that booleans in C are encoded as `0` for false and `1` for true. + +```Python +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * + + +class FContract(Contract): + def specification(self): + p = self.alloc(i32) + + self.execute_func(p) + + self.returns(cry("1 : [32]")) + +class LLVMAssertNullTest(unittest.TestCase): + def test_llvm_assert_null(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + bcname = '../src/null.bc' + mod = llvm_load_module(bcname) + + result = llvm_verify(mod, 'f', FContract()) + self.assertIs(result.is_success(), True) + + +if __name__ == "__main__": + unittest.main() +``` + +

+ Click here to see if your guess is correct! + + It turns out the contract above will fail! + + ``` + saw failure + ``` + + An initialized symbolic pointer that hasn't been assigned a symbolic variable to point to is **NOT** equivalent to a symbolic null pointer. We can use `null()` in situations where we want a null pointer. For example, if we change the contract above to + + ``` + class FContract(Contract): + def specification(self): + self.execute_func(null()) + + self.returns(cry("1 : [32]")) + ``` + + then SAW is a happy: + + ``` + saw error message + ``` +
+ +This example is from [here]() + # Structs @@ -851,9 +915,9 @@ typedef struct { ## Assumptions and Lemmas -Sometimes one might want to black-box certain parts of the verification -such as functions coming from libraries, especially if they have -already been formally verified. For example, the height of +Only functions with implementations in the bitcode can be verified. If one imports a function from a library, then that implementation will not be present in the bitcode generated as above. Instead of verifying these library functions we can use contracts to assume their behavior. + +For example, the height of [height-balanced binary trees](https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) on n nodes is given by the ceiling of `log(n)`. In C we might use the From 0297ee2d9235fa85baf243f42d876f551ff717c5 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Tue, 31 May 2022 15:13:09 -0400 Subject: [PATCH 034/120] Update SAW.md --- labs/SAW/SAW.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 9bf29368..2a3ac82c 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -80,14 +80,12 @@ uint32_t RCS(uint32_t bits, uint32_t shift) { SAW doesn't actually verify C source, but rather C compiled down to LLVM intermediate representation (IR), or bitcode. This can be accomplished via the `clang` compiler. In this instance, we can create -the bitcode by entering the following command in a terminal: +the bitcode by entering the following command in a terminal. ```sh $ clang -emit-llvm labs/SAW/src/rcs.c -c -o labs/SAW/src/rcs.bc ``` -(you may need to adjust the path name). - We can inspect the bitcode using SAW by loading the module and printing some meta-data. @@ -655,7 +653,7 @@ where the length of the array is not specified. The corresponding Cryptol code might be: ```cryptol -addRow : {length} (fin length) => [n][32] -> [n][32] -> [n][32] +addRow : {length} (fin length) => [length][32] -> [length][32] -> [length][32] addRow a b = a + b ``` @@ -823,7 +821,7 @@ for large arrays. Ideally, one would just have to declare the array implicitly as before and then pass this array to a Cryptol specification for postconditions as was done in the previous examples. -## Null Pointers and Symbolic Pointers +## Null Pointers Consider the following C code that checks if a pointer is null: From b24a561ffa59fe5205ab5e0886a5ede438ff34cc Mon Sep 17 00:00:00 2001 From: djmorel Date: Tue, 31 May 2022 19:24:45 +0000 Subject: [PATCH 035/120] Added Memory Disjoint support example --- labs/SAW/Game/proof/Game.py | 135 ++++++++++++++++++++++++++++-------- labs/SAW/Game/src/Game.c | 26 +++++++ 2 files changed, 134 insertions(+), 27 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 0257457f..9642a4cf 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,9 +1,18 @@ # TODO -# - Aliasing / Memory region disjoint example (resolveAttack) +# - Add a function that would check stats prior to callees +# --> Mention in the markdown file that this is referenced for preconditions +# --> Relevant example: resolveAttack (integer overflow/underflow) # - Try the alloc_pointsto_buffer example & see its limitations +# - Global variable +# - Extern +# - Field name (extra debug info) +# - Make a worksheet & solution for the SAW examples # Done -# - Nested defines example (handled here...) (Mention in some SAW configs, this may be an issue) +# - Nested defines example (handled here...) +# --> Mention in some SAW configs (i.e. llvm), this may be an issue +# - Aliasing / Memory Region disjoint example (selfDamage) +# --> Mention in some SAW configs (i.e. llvm), this may be an issue ####################################### # Imporant Imports @@ -23,12 +32,15 @@ ####################################### MAX_NAME_LENGTH = 12 -SUCCESS = 170 -FAILURE = 85 -MAX_STAT = 100 -SCREEN_ROWS = 15 -SCREEN_COLS = 10 -SCREEN_TILES = SCREEN_ROWS * SCREEN_COLS +SUCCESS = 170 +FAILURE = 85 +MAX_STAT = 100 +SCREEN_ROWS = 15 +SCREEN_COLS = 10 +SCREEN_TILES = SCREEN_ROWS * SCREEN_COLS +NEUTRAL = 0 +DEFEAT_PLAYER = 1 +DEFEAT_OPPONENT = 2 ####################################### @@ -124,32 +136,33 @@ def specification (self): atk = self.fresh_var(i32, "atk") # Assert the precondition that the stats are below the max stat cap + # Pop Quiz: Why do we need these preconditions? self.precondition_f("{atk} <= `{MAX_STAT}") - self.precondition_f("({target}).2 <= `{MAX_STAT}") - self.precondition_f("({target}).4 <= `{MAX_STAT}") + self.precondition_f("{target}.2 <= `{MAX_STAT}") + self.precondition_f("{target}.4 <= `{MAX_STAT}") # Determine the preconditions based on the case parameter if (self.case == 1): # target->def >= atk - self.precondition_f("({target}).4 >= {atk}") + self.precondition_f("{target}.4 >= {atk}") elif (self.case == 2): # target->hp <= (atk - target->def) - self.precondition_f("(({target}).2 + ({target}).4) <= {atk}") + self.precondition_f("({target}.2 + {target}.4) <= {atk}") else: # Assume any other case follows the formal attack calculation - self.precondition_f("({target}).4 < {atk}") - self.precondition_f("(({target}).2 + ({target}).4) > {atk}") + self.precondition_f("{target}.4 < {atk}") + self.precondition_f("({target}.2 + {target}.4) > {atk}") # Symbolically execute the function self.execute_func(target_p, atk) # Determine the postcondition based on the case parameter if (self.case == 1): - self.points_to(target_p[2], cry_f("({target}).2 : [32]")) + self.points_to(target_p[2], cry_f("{target}.2 : [32]")) elif (self.case == 2): self.points_to(target_p[2], cry_f("0 : [32]")) else: - self.points_to(target_p[2], cry_f("resolveAttack (({target}).2) (({target}).4) {atk}")) + self.points_to(target_p[2], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) self.returns(void) @@ -207,24 +220,26 @@ def specification (self): # Declare variables (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") (opponent, opponent_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="opponent") + # Pop Quiz: Why does allocating the pointers in the following way yield an + # error? #player = self.alloc(alias_ty("struct.character_t")) #opponent = self.alloc(alias_ty("struct.character_t")) # Assert the precondition that both HPs are greater than 0 - self.precondition_f("({player}).2 > 0") - self.precondition_f("({opponent}).2 > 0") + self.precondition_f("{player}.2 > 0") + self.precondition_f("{opponent}.2 > 0") # Assert the precondition that character stats are below the max stat cap - # Question: Explain why the proof fails when the following preconditions + # Pop Quiz: Explain why the proof fails when the following preconditions # are commented out. - self.precondition_f("({player}).2 <= `{MAX_STAT}") - self.precondition_f("({player}).3 <= `{MAX_STAT}") - self.precondition_f("({player}).4 <= `{MAX_STAT}") - self.precondition_f("({player}).5 <= `{MAX_STAT}") - self.precondition_f("({opponent}).2 <= `{MAX_STAT}") - self.precondition_f("({opponent}).3 <= `{MAX_STAT}") - self.precondition_f("({opponent}).4 <= `{MAX_STAT}") - self.precondition_f("({opponent}).5 <= `{MAX_STAT}") + self.precondition_f("{player}.2 <= `{MAX_STAT}") + self.precondition_f("{player}.3 <= `{MAX_STAT}") + self.precondition_f("{player}.4 <= `{MAX_STAT}") + self.precondition_f("{player}.5 <= `{MAX_STAT}") + self.precondition_f("{opponent}.2 <= `{MAX_STAT}") + self.precondition_f("{opponent}.3 <= `{MAX_STAT}") + self.precondition_f("{opponent}.4 <= `{MAX_STAT}") + self.precondition_f("{opponent}.5 <= `{MAX_STAT}") # Symbolically execute the function self.execute_func(player_p, opponent_p) @@ -232,6 +247,65 @@ def specification (self): # Assert the postcondition self.returns(void) + +# selfDamage Contract +# uint32_t selfDamage(player_t* player) +class selfDamage_Contract(Contract): + def __init__(self, case : int): + super().__init__() + self.case = case + # There are 3 possible cases for resolveAttack + # Case 1: Attack mitigated + # Case 2: Immediate KO + # Case 3: Regular attack + # Each case results in a different function behavior for calculating the + # player's remaining HP. While we could make 3 separate contracts to handle + # all of the possible cases, we can pass a parameter to the contract, which + # identifies what preconditions and postconditions to set. + + def specification (self): + # Declare variables + (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") + + # Assert the preconditions + self.precondition_f("{player}.2 > 5") + self.precondition_f("{player}.3 == 5") + self.precondition_f("{player}.4 == 0") + + # Assert the precondition that character stats are below the max stat cap + # Pop Quiz: Explain why the proof fails when the following preconditions + # are commented out. + self.precondition_f("{player}.2 <= `{MAX_STAT}") + self.precondition_f("{player}.3 <= `{MAX_STAT}") + self.precondition_f("{player}.4 <= `{MAX_STAT}") + self.precondition_f("{player}.5 <= `{MAX_STAT}") + + # Determine the preconditions based on the case parameter + if (self.case == 1): + # player->def >= player->atk + self.precondition_f("{player}.4 >= {player}.3") + elif (self.case == 2): + # player->hp <= (player->atk - player->def) + self.precondition_f("({player}.2 + {player}.4) <= {player}.3") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("{player}.4 < {player}.3") + self.precondition_f("({player}.2 + {player}.4) > {player}.3") + + # Symbolically execute the function + self.execute_func(player_p, player_p) + + # Assert the postcondition + if (self.case == 1): + self.points_to(player_p[2], cry_f("{player}.2 : [32]")) + self.returns(cry_f("`({NEUTRAL}) : [32]")) + elif (self.case == 2): + self.points_to(player_p[2], cry_f("0 : [32]")) + self.returns(cry_f("`({DEFEAT_PLAYER}) : [32]")) + else: + self.points_to(player_p[2], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) + self.returns(cry_f("`({NEUTRAL}) : [32]")) + ####################################### @@ -259,6 +333,10 @@ def test_Game(self): resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + self.assertIs(levelUp_result.is_success(), True) self.assertIs(initDefaultPlayer_result.is_success(), True) self.assertIs(resolveAttack_case1_result.is_success(), True) @@ -267,6 +345,9 @@ def test_Game(self): self.assertIs(resetInventoryItems_result.is_success(), True) self.assertIs(initScreen_result.is_success(), True) self.assertIs(quickBattle_result.is_success(), True) + self.assertIs(selfDamage_case1_result.is_success(), True) + self.assertIs(selfDamage_case2_result.is_success(), True) + self.assertIs(selfDamage_case3_result.is_success(), True) if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index cf549583..90fa3ebd 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -52,6 +52,32 @@ void resolveAttack(character_t* target, uint32_t atk) } } +uint32_t selfDamage(player_t* player) +{ + enum battleResult result = NEUTRAL; + + // Player attacks itself (perhaps due to status condition) + resolveAttack(player, player->atk); + // Note: If SAW ever gives a "Memory not disjoint" error, then what SAW + // actually means is that inputs to an overridden function cannot + // overlap in memory. Given that player->atk is a field within player, + // the inputs overlap. + // Also consider the following way to have the player damage itself: + // quickBattle(player, player); + // This also contains inputs that overlap in memory (i.e. not disjoint). + // Bear in mind that both function calls in this example will not result in + // the Memory Disjoint error for the Python API. However, they will likely + // yield the same error if not using the Python API (i.e. llvm.saw). + + if (player->hp <= 0) + { + // Player's self damage results in a knockout + result = DEFEAT_PLAYER; + } + + return result; +} + void resetInventoryItems(inventory_t* inventory) { From df3112265ad1097c544f7d45a5822caf7a63ae47 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Tue, 31 May 2022 16:26:49 -0400 Subject: [PATCH 036/120] Update SAW.md --- labs/SAW/SAW.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 2a3ac82c..d1d0f405 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -252,7 +252,8 @@ using `self.assertIs(RCS_result.is_success(), True)`. ## Debugging C with SAW -The full code is: +
+ Click here for the full code ```python import os @@ -291,6 +292,17 @@ if __name__ == "__main__": unittest.main() ``` +Remember to always include + +```python +if __name__ == "__main__": + unittest.main() +``` + +or else the python script won't do anything! + +
+ We can now run the proof script. ``` From 50639a2d1f2c9edc83e90d74449607adcf578655 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Tue, 31 May 2022 16:50:26 -0400 Subject: [PATCH 037/120] Update SAW.md --- labs/SAW/SAW.md | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index d1d0f405..0706e77a 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -653,7 +653,7 @@ While Cryptol has arrays as primitives, it does not have any notion of pointer, Suppose our C code was written as ```C -uint32_t* addRowAlias(uint32_t* a, uint32_t* b, uint32_t length) { +uint32_t* addRowAlias(uint32_t* a, uint32_t* b, uint8_t length) { for(int i = 0 ; i < length; i++) { a[i] += b[i]; } @@ -727,6 +727,11 @@ from saw_client.llvm import * from saw_client.proofscript import * from saw_client.llvm_type import * +def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None, read_only : Optional[bool] = False) -> Tuple[FreshVar, SetupVal]: + var = c.fresh_var(ty, name) + ptr = c.alloc(ty, points_to = var, read_only=read_only) + return (var, ptr) + def addRow5Mutate_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") @@ -758,10 +763,13 @@ def addRowAlias_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) + length = fresh_var(i8, "length") - self.execute_func(a_p, b_p) + self.precondition_f("{self.length} == {length}") - self.points_to(a_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) + self.execute_func(a_p, b_p, length) + + self.points_to(a_p, cry_f("rowAdd`{{{length}}} {a} {b}")) self.returns(a_p) @@ -770,29 +778,24 @@ class ArrayTests(unittest.TestCase): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) - bcname1 = "/some/path/to/your/file.bc" - bcname2 = "/some/path/to/your/file.bc" - cryname = "/some/path/to/your/file.cry" + bcname = "../src/addRow.bc" + cryname = "spec/addRow.cry" cryptol_load_file(cryname) - mod1 = llvm_load_module(bcname) - mod2 = llvm_load_module(bcname) + mod = llvm_load_module(bcname) - addRow5Mutate_result = llvm_verify(mod1, 'addRowMutate', addRowMutate_Contract()) + addRow5Mutate_result = llvm_verify(mod, 'addRowMutate', addRowMutate_Contract()) self.assertIs(addRow5Mutate_result.is_success(), True) - addRow5NewVar_result = llvm_verify(mod1, 'addRowNewVar', addRowNewVar_Contract()) + addRow5NewVar_result = llvm_verify(mod, 'addRowNewVar', addRowNewVar_Contract()) self.assertIs(addRow5NewVar_result.is_success(), True) - addRowAlias05_result = llvm_verify(mod2, 'addRowAlias', addRowAlias_Contract(5)) - addAddAlias10_result = llvm_verify(mod2, 'addRowAlias', addRowAlias_Contract(10)) + addRowAlias05_result = llvm_verify(mod, 'addRowAlias', addRowAlias_Contract(5)) + addAddAlias10_result = llvm_verify(mod, 'addRowAlias', addRowAlias_Contract(10)) self.assertIs(addRowAlias05_result.is_success(), True) self.assertIs(addRowAlias10_result.is_success(), True) ``` -If we try to run this Python we get: - - ## Explicit Arrays Another way to initialize arrays is through the `array` command. For From d0a81c0aed25af0ff4b5adf1c83b2133082d7bc6 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Tue, 31 May 2022 16:57:15 -0400 Subject: [PATCH 038/120] Update SAW.md --- labs/SAW/SAW.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 0706e77a..503a6681 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -689,8 +689,11 @@ def addRowAlias_Contract(Contract): def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) + length = fresh_var(i8, "length") - self.execute_func(a_p, b_p) + self.precondition_f("{length} == {self.length}") + + self.execute_func(a_p, b_p, length) self.points_to(a_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) @@ -719,6 +722,8 @@ class ArrayTests(unittest.TestCase): ## Array Example Full Code and Debugging SAW +//remove precondition in previous example and detect an issue here? + ```python import unittest from saw_client import * From 4e8294b0de17479d893059e7e16ed2c35efcbcda Mon Sep 17 00:00:00 2001 From: djmorel Date: Tue, 31 May 2022 21:06:15 +0000 Subject: [PATCH 039/120] Added error documentation for resetInventoryItems contract & added checkStats function verification --- labs/SAW/Game/proof/Game.py | 83 +++++++++++++++++++++++++++++++++++++ labs/SAW/Game/src/Game.c | 21 ++++++++++ labs/SAW/Game/src/Game.h | 1 + 3 files changed, 105 insertions(+) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 9642a4cf..fe0247ce 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -8,11 +8,13 @@ # - Field name (extra debug info) # - Make a worksheet & solution for the SAW examples + # Done # - Nested defines example (handled here...) # --> Mention in some SAW configs (i.e. llvm), this may be an issue # - Aliasing / Memory Region disjoint example (selfDamage) # --> Mention in some SAW configs (i.e. llvm), this may be an issue +# - Expand on the error cases associated with inventory_t's item_t pointer ####################################### # Imporant Imports @@ -115,6 +117,45 @@ def specification (self): self.returns(cry_f("`({SUCCESS}) : [32]")) +# checkStats Contract +# uint32_t checkStats(character_t* character) +# Note: This contract only considers the case where all of the stats are below +# the MAX_STAT cap. We could prove the FAILURE result as long as we have +# at least one field violate the MAX_STAT cap +class checkStats_Contract(Contract): + def __init__(self, shouldPass : bool): + super().__init__() + self.shouldPass = shouldPass + # There are 3 possible cases for resolveAttack + def specification (self): + # Declare variables + (character, character_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="character") + + # Assert preconditions + if (self.shouldPass): + # Set up the preconditions to assure all stats are below the MAX_STAT cap + self.precondition_f("{character}.2 <= `{MAX_STAT}") + self.precondition_f("{character}.3 <= `{MAX_STAT}") + self.precondition_f("{character}.4 <= `{MAX_STAT}") + self.precondition_f("{character}.5 <= `{MAX_STAT}") + else: + # Note: Must meet at least one of the following preconditions to result + # in a failure (feel free to comment out as many as you want) + self.precondition_f("{character}.2 > `{MAX_STAT}") + self.precondition_f("{character}.3 > `{MAX_STAT}") + self.precondition_f("{character}.4 > `{MAX_STAT}") + self.precondition_f("{character}.5 > `{MAX_STAT}") + + # Symbolically execute the function + self.execute_func(character_p) + + # Assert the postcondition behavior + if (self.shouldPass): + self.returns(cry_f("`({SUCCESS}) : [32]")) + else: + self.returns(cry_f("`({FAILURE}) : [32]")) + + # resolveAttack Contract # void resolveAttack(character_t* target, uint32_t atk) class resolveAttack_Contract(Contract): @@ -183,8 +224,46 @@ def __init__(self, numItems : int): def specification (self): # Declare variables + # Note: The setup here does not use item_p for the proof. However, item_p + # is included to show errors that can be encountered with the + # inventory_t struct. (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) + """ + If inventory_t is defined as: + typedef struct { + item_t* item; + uint32_t numItems; + } inventory_t; + + Attempt 1: + ========== + Passing item as so: + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) + + yields the following error: + ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): + error: types not memory-compatible: + { %struct.item_t*, i32 } + { [5 x { i32, i32 }], i32 } + + + stdout: + + Attempt 2: + ========== + Passing item_p as so: + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item_p, cry_f("{self.numItems} : [32]"))) + + yields the following error: + ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): + error: typeOfSetupValue: llvm_elem requires pointer to struct or array, found %struct.item_t** + stdout: + + Considering both of these verification setup attempts, we can see that + defining inventory_t with an item_t pointer is tough for SAW to setup and. + prove. Consequently, it is better to use fixed array lengths for structs! + """ # Symbolically execute the function self.execute_func(inventory_p) @@ -327,6 +406,8 @@ def test_Game(self): levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) + checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) + checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) @@ -339,6 +420,8 @@ def test_Game(self): self.assertIs(levelUp_result.is_success(), True) self.assertIs(initDefaultPlayer_result.is_success(), True) + self.assertIs(checkStats_pass_result.is_success(), True) + self.assertIs(checkStats_fail_result.is_success(), True) self.assertIs(resolveAttack_case1_result.is_success(), True) self.assertIs(resolveAttack_case2_result.is_success(), True) self.assertIs(resolveAttack_case3_result.is_success(), True) diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index 90fa3ebd..3c0ae2b6 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -33,6 +33,27 @@ uint32_t initDefaultPlayer(player_t* player) return SUCCESS; } + +// Checks that the character stats are below MAX_STAT +// Note: MUST be called before running any function that uses character stats! +uint32_t checkStats(character_t* character) +{ + // Assume failure by default + uint32_t result = FAILURE; + + // Check the stats + if (character->hp <= MAX_STAT && + character->atk <= MAX_STAT && + character->def <= MAX_STAT && + character->spd <= MAX_STAT ) + { + result = SUCCESS; + } + + return result; +} + + void resolveAttack(character_t* target, uint32_t atk) { if ( target->def >= atk) diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index fdaf9843..ae12baf1 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -51,6 +51,7 @@ typedef struct { // Function prototypes uint32_t levelUp(uint32_t level); uint32_t initDefaultPlayer(player_t* player); +uint32_t checkStats(character_t* character); void resolveAttack(character_t* target, uint32_t atk); void resetInventoryItems(inventory_t* inventory); uint32_t initScreen(screen_t* screen, uint8_t assetID); From 646bce626fb10e5831e9f4eebe5222739b735f3c Mon Sep 17 00:00:00 2001 From: djmorel Date: Tue, 31 May 2022 21:53:52 +0000 Subject: [PATCH 040/120] Enabled debug symbols in generated bitcode to support SAW field name detection --- labs/SAW/Game/Makefile | 4 ++- labs/SAW/Game/proof/Game.py | 59 ++++++++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/labs/SAW/Game/Makefile b/labs/SAW/Game/Makefile index 2487318f..de95f0c7 100644 --- a/labs/SAW/Game/Makefile +++ b/labs/SAW/Game/Makefile @@ -7,8 +7,10 @@ PROOF=proof SPECS=specs ARTIFACTS=artifacts +# Note: -g flag compiles the code with debug symbols enabled. +# The flag lets us pass field names in addition to indices for structs. all: artifacts - clang -c -emit-llvm -o $(ARTIFACTS)/Game.bc $(SRC)/Game.c + clang -c -g -emit-llvm -o $(ARTIFACTS)/Game.bc $(SRC)/Game.c python3 $(PROOF)/Game.py artifacts: diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index fe0247ce..a507ad21 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,4 +1,5 @@ # TODO +# - Move the fancy Cryptol array init of a given size to Game.cry (initSequence) # - Add a function that would check stats prior to callees # --> Mention in the markdown file that this is referenced for preconditions # --> Relevant example: resolveAttack (integer overflow/underflow) @@ -90,22 +91,34 @@ def specification (self): # to assert struct field contents. # Index 0 = "name" field # Recall that 0x41 is ASCII for 'A' - self.points_to(player[0], cry_f("[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]]")) + self.points_to(player['name'], cry_f("[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player[0], cry_f("[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]]")) # Index 1 is the "level" field - self.points_to(player[1], cry_f("1 : [32]")) + self.points_to(player['level'], cry_f("1 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player[1], cry_f("1 : [32]")) # Index 2 is the "hp" field - self.points_to(player[2], cry_f("10 : [32]")) + self.points_to(player['hp'], cry_f("10 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player[2], cry_f("10 : [32]")) # Index 3 is the "atk" field - self.points_to(player[3], cry_f("5 : [32]")) + self.points_to(player['atk'], cry_f("5 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player[3], cry_f("5 : [32]")) # Index 4 is the "def" field - self.points_to(player[4], cry_f("4 : [32]")) + self.points_to(player['def'], cry_f("4 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player[4], cry_f("4 : [32]")) # Index 5 is the "spd" field - self.points_to(player[5], cry_f("3 : [32]")) + self.points_to(player['spd'], cry_f("3 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player[5], cry_f("3 : [32]")) # Incorrect Alternative 1: Invalid label in record update. #self.points_to(player, cry_f("{{ [(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) @@ -199,11 +212,17 @@ def specification (self): # Determine the postcondition based on the case parameter if (self.case == 1): - self.points_to(target_p[2], cry_f("{target}.2 : [32]")) + self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(target_p[2], cry_f("{target}.2 : [32]")) elif (self.case == 2): - self.points_to(target_p[2], cry_f("0 : [32]")) + self.points_to(target_p['hp'], cry_f("0 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(target_p[2], cry_f("0 : [32]")) else: - self.points_to(target_p[2], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(target_p[2], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) self.returns(void) @@ -270,7 +289,11 @@ def specification (self): # Assert the postconditions for i in range(self.numItems): - self.points_to(inventory_p[0][i][1], cry_f("0 : [32]")) + self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(inventory_p[0][i][1], cry_f("0 : [32]")) + # Note: Even though debug symbols is enabled, SAW cannot resolve the + # "quantity" field, which is why we still use a 1 above. self.returns(void) @@ -287,7 +310,9 @@ def specification (self): self.execute_func(screen, assetID) # Assert the postcondition - self.points_to(screen[0], cry_f("[(i*0 + {assetID}) | i <- [1..{SCREEN_TILES}]]")) + self.points_to(screen['tiles'], cry_f("[(i*0 + {assetID}) | i <- [1..{SCREEN_TILES}]]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(screen[0], cry_f("[(i*0 + {assetID}) | i <- [1..{SCREEN_TILES}]]")) self.returns(cry_f("`({SUCCESS}) : [32]")) @@ -376,13 +401,19 @@ def specification (self): # Assert the postcondition if (self.case == 1): - self.points_to(player_p[2], cry_f("{player}.2 : [32]")) + self.points_to(player_p['hp'], cry_f("{player}.2 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("{player}.2 : [32]")) self.returns(cry_f("`({NEUTRAL}) : [32]")) elif (self.case == 2): - self.points_to(player_p[2], cry_f("0 : [32]")) + self.points_to(player_p['hp'], cry_f("0 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("0 : [32]")) self.returns(cry_f("`({DEFEAT_PLAYER}) : [32]")) else: - self.points_to(player_p[2], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) + self.points_to(player_p['hp'], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) self.returns(cry_f("`({NEUTRAL}) : [32]")) ####################################### From 0a4480e47c7d9ab3273ee3bf6cbf044476770877 Mon Sep 17 00:00:00 2001 From: djmorel Date: Tue, 31 May 2022 22:05:28 +0000 Subject: [PATCH 041/120] Updated SAW Game example TODO list --- labs/SAW/Game/proof/Game.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index a507ad21..3417e28d 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,22 +1,19 @@ # TODO # - Move the fancy Cryptol array init of a given size to Game.cry (initSequence) -# - Add a function that would check stats prior to callees -# --> Mention in the markdown file that this is referenced for preconditions -# --> Relevant example: resolveAttack (integer overflow/underflow) # - Try the alloc_pointsto_buffer example & see its limitations # - Global variable # - Extern -# - Field name (extra debug info) # - Make a worksheet & solution for the SAW examples +# - Update SAW.md to discuss the topics covered in this SAW script +# --> Mention importance of "-g" flag for debug symbols +# --> Mention reason for preconditions (i.e. checkStats caller vs callee) +# Removing the preconditons in resolveAttack leads to integer overflow +# --> Mention in some SAW configs (i.e. llvm), nested defines may be an issue (i.e. initScreen) +# --> Mention in some SAW configs (i.e. llvm), aliasing/non-disjoint memory regions may be an issue (i.e. selfDamage) +# --> Discuss the issues associated with inventory_t's item_t pointer +# - Reorder the functions to match the topic discussion -# Done -# - Nested defines example (handled here...) -# --> Mention in some SAW configs (i.e. llvm), this may be an issue -# - Aliasing / Memory Region disjoint example (selfDamage) -# --> Mention in some SAW configs (i.e. llvm), this may be an issue -# - Expand on the error cases associated with inventory_t's item_t pointer - ####################################### # Imporant Imports ####################################### From ce3928cd3fda02adb74e11dbdca953945c157c05 Mon Sep 17 00:00:00 2001 From: djmorel Date: Wed, 1 Jun 2022 12:23:07 +0000 Subject: [PATCH 042/120] Moved initByteSequence logic to its own Cryptol function --- labs/SAW/Game/proof/Game.py | 9 ++++----- labs/SAW/Game/specs/Game.cry | 5 +++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 3417e28d..6d6c2943 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,5 +1,4 @@ # TODO -# - Move the fancy Cryptol array init of a given size to Game.cry (initSequence) # - Try the alloc_pointsto_buffer example & see its limitations # - Global variable # - Extern @@ -88,9 +87,9 @@ def specification (self): # to assert struct field contents. # Index 0 = "name" field # Recall that 0x41 is ASCII for 'A' - self.points_to(player['name'], cry_f("[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]]")) + self.points_to(player['name'], cry_f("initByteSequence`{{ {MAX_NAME_LENGTH} }} 0x41")) # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player[0], cry_f("[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]]")) + # self.points_to(player[0], cry_f("initByteSequence`{{ {MAX_NAME_LENGTH} }} 0x41")) # Index 1 is the "level" field self.points_to(player['level'], cry_f("1 : [32]")) @@ -307,9 +306,9 @@ def specification (self): self.execute_func(screen, assetID) # Assert the postcondition - self.points_to(screen['tiles'], cry_f("[(i*0 + {assetID}) | i <- [1..{SCREEN_TILES}]]")) + self.points_to(screen['tiles'], cry_f("initByteSequence`{{ {SCREEN_TILES} }} {assetID}")) # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(screen[0], cry_f("[(i*0 + {assetID}) | i <- [1..{SCREEN_TILES}]]")) + # self.points_to(screen[0], cry_f("initByteSequence`{{ {SCREEN_TILES} }} {assetID}")) self.returns(cry_f("`({SUCCESS}) : [32]")) diff --git a/labs/SAW/Game/specs/Game.cry b/labs/SAW/Game/specs/Game.cry index e3807746..0cffc037 100644 --- a/labs/SAW/Game/specs/Game.cry +++ b/labs/SAW/Game/specs/Game.cry @@ -1,5 +1,10 @@ module Game where +initByteSequence : {numBytes} + (fin numBytes, numBytes >= 1, 8 >= width numBytes) + => [8] -> [numBytes][8] +initByteSequence val = [(i*0 + val) | i <- [1..numBytes]] + levelUp : [32] -> [32] levelUp level = level + 1 From 7d4d7718a15ea880c90d6179325867fd9e345804 Mon Sep 17 00:00:00 2001 From: djmorel Date: Wed, 1 Jun 2022 15:15:56 +0000 Subject: [PATCH 043/120] Added global variable function examples --- labs/SAW/Game/proof/Game.py | 102 ++++++++++++++++++++++++++++++++--- labs/SAW/Game/specs/Game.cry | 6 +++ labs/SAW/Game/src/Game.c | 24 +++++++++ labs/SAW/Game/src/Game.h | 13 +++++ 4 files changed, 139 insertions(+), 6 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 6d6c2943..710f9f3a 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,15 +1,25 @@ # TODO +# - Replace the silly initByteSequence function with repeat # - Try the alloc_pointsto_buffer example & see its limitations +# --> See if possible to keep pointer field # - Global variable +# --> Pulling values directly from source code... # - Extern +# - Aligned # - Make a worksheet & solution for the SAW examples # - Update SAW.md to discuss the topics covered in this SAW script +# --> Link to the GaloicInc/saw-script and GaloisInc/saw-demos examples for more Python references +# https://github.com/GaloisInc/saw-script/tree/master/saw-remote-api/python/tests/saw +# https://github.com/GaloisInc/saw-demos/tree/master/demos/signal-protocol # --> Mention importance of "-g" flag for debug symbols +# --> Discuss reasons between self.alloc (only want pointer) and +# ptr_to_fresh (want to set initial contents or reference in postcondition) # --> Mention reason for preconditions (i.e. checkStats caller vs callee) # Removing the preconditons in resolveAttack leads to integer overflow # --> Mention in some SAW configs (i.e. llvm), nested defines may be an issue (i.e. initScreen) # --> Mention in some SAW configs (i.e. llvm), aliasing/non-disjoint memory regions may be an issue (i.e. selfDamage) # --> Discuss the issues associated with inventory_t's item_t pointer +# --> Include a "Troubleshooting/What SAW Says & Means" section # - Reorder the functions to match the topic discussion @@ -40,6 +50,7 @@ NEUTRAL = 0 DEFEAT_PLAYER = 1 DEFEAT_OPPONENT = 2 +ASSET_TABLE_SIZE = 16 ####################################### @@ -71,6 +82,21 @@ def specification (self): self.returns_f("levelUp {level}") +# getDefaultLevel Contract +# uint32_t getDefaultLevel() +class getDefaultLevel_Contract(Contract): + def specification (self): + # Initialize the defaultLevel global variable + defaultLevel_init = global_initializer("defaultLevel") + self.points_to(global_var("defaultLevel"), defaultLevel_init) + + # Symbolically execute the function + self.execute_func() + + # Assert the function's return behavior + self.returns(defaultLevel_init) + + # initDefaultPlayer Contract # uint32_t initDefaultPlayer(player_t* player) class initDefaultPlayer_Contract(Contract): @@ -117,25 +143,25 @@ def specification (self): # self.points_to(player[5], cry_f("3 : [32]")) # Incorrect Alternative 1: Invalid label in record update. - #self.points_to(player, cry_f("{{ [(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) + # self.points_to(player, cry_f("{{ [(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) # Incorrect Alternative 2: SAW doesn't yet support translating Cryptol's # record type(s) into crucible-llvm's type system. - #self.points_to(player, cry_f("{{ name=[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) + # self.points_to(player, cry_f("{{ name=[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) self.returns(cry_f("`({SUCCESS}) : [32]")) # checkStats Contract # uint32_t checkStats(character_t* character) -# Note: This contract only considers the case where all of the stats are below -# the MAX_STAT cap. We could prove the FAILURE result as long as we have -# at least one field violate the MAX_STAT cap class checkStats_Contract(Contract): def __init__(self, shouldPass : bool): super().__init__() self.shouldPass = shouldPass - # There are 3 possible cases for resolveAttack + # There are 2 possible cases for checkStats + # Case 1 (shouldPass == True): All of the stats are below the MAX_STAT cap + # Case 2 (shouldPass == False): At least one stat exceeds the MAX_STAT cap + def specification (self): # Declare variables (character, character_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="character") @@ -313,6 +339,64 @@ def specification (self): self.returns(cry_f("`({SUCCESS}) : [32]")) +# setScreenTile +# uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx) +class setScreenTile_Contract(Contract): + def __init__(self, shouldPass : bool): + super().__init__() + self.shouldPass = shouldPass + # There are 2 possible cases for setScreenTile + # Case 1 (shouldPass == True): Both screenIdx and tableIdx are below their + # limits, SCREEN_TILES and ASSET_TABLE_SIZE, + # respectively. + # Case 2 (shouldPass == False): At least one index exceeds its limits. + + def specification (self): + # Declare variables + (screen, screen_p) = ptr_to_fresh(self, alias_ty("struct.screen_t"), name="screen") + screenIdx = self.fresh_var(i32, "screenIdx") + tableIdx = self.fresh_var(i32, "tableIdx") + # Pop Quiz: Why can't we just declare and pass the screen pointer? + # screen_p = self.alloc(alias_ty("struct.screen_t")) + + # Note: The following global initialization is not needed because + # - SAW already understands what values to use + # - We made a local copy in Game.cry to express the expected behavior + # However, in the event that SAW complains that it does not know what + # assetTable is, include the lines! + # assetTable_init = global_initializer("assetTable") + # self.points_to(global_var("assetTable"), assetTable_init) + + # Assert preconditions depending on the Contract parameter + if (self.shouldPass): + self.precondition_f("{screenIdx} < {SCREEN_TILES}") + self.precondition_f("{tableIdx} < {ASSET_TABLE_SIZE}") + else: + # Note: Only one of the following preconditions is needed + self.precondition_f("{screenIdx} >= {SCREEN_TILES}") + self.precondition_f("{tableIdx} >= {ASSET_TABLE_SIZE}") + + # Symbolically execute the function + self.execute_func(screen_p, screenIdx, tableIdx) + + # Since we just want to check one index, let's have our screen pointer + # point to a new screen_t variable. + screen_post = self.fresh_var(alias_ty("struct.screen_t"), "screen_post") + + # Assert that the original screen pointer now points to the new screen_t + # variable. This will allow us to reference screen_post in Cryptol for our + # later postconditions. + self.points_to(screen_p, screen_post) + + # Assert postconditions depending on the Contract parameter + if (self.shouldPass): + self.postcondition_f("({screen_post}@{screenIdx}) == assetTable@{tableIdx}") + #self.points_to(screen['tiles'][cry_f("{screenIdx}")], cry_f("assetTable@{tableIdx}")) + self.returns_f("`({SUCCESS}) : [32]") + else: + self.returns_f("`({FAILURE}) : [32]") + + # quickBattle Contract # void quickBattle(player_t* player, character_t* opponent) class quickBattle_Contract(Contract): @@ -432,6 +516,7 @@ def test_Game(self): module = llvm_load_module(bitcode_name) levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) + getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) @@ -440,12 +525,15 @@ def test_Game(self): resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) + setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) + setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) self.assertIs(levelUp_result.is_success(), True) + self.assertIs(getDefaultLevel_result.is_success(), True) self.assertIs(initDefaultPlayer_result.is_success(), True) self.assertIs(checkStats_pass_result.is_success(), True) self.assertIs(checkStats_fail_result.is_success(), True) @@ -454,6 +542,8 @@ def test_Game(self): self.assertIs(resolveAttack_case3_result.is_success(), True) self.assertIs(resetInventoryItems_result.is_success(), True) self.assertIs(initScreen_result.is_success(), True) + self.assertIs(setScreenTile_pass_result.is_success(), True) + self.assertIs(setScreenTile_fail_result.is_success(), True) self.assertIs(quickBattle_result.is_success(), True) self.assertIs(selfDamage_case1_result.is_success(), True) self.assertIs(selfDamage_case2_result.is_success(), True) diff --git a/labs/SAW/Game/specs/Game.cry b/labs/SAW/Game/specs/Game.cry index 0cffc037..16a09fef 100644 --- a/labs/SAW/Game/specs/Game.cry +++ b/labs/SAW/Game/specs/Game.cry @@ -1,5 +1,11 @@ module Game where + +assetTable = [0x01, 0x12, 0x23, 0x34, + 0x45, 0x56, 0x67, 0x78, + 0x89, 0x9A, 0xAB, 0xBC, + 0xCD, 0xDE, 0xEF, 0xF0] : [16][8] + initByteSequence : {numBytes} (fin numBytes, numBytes >= 1, 8 >= width numBytes) => [8] -> [numBytes][8] diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index 3c0ae2b6..11f04664 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -7,6 +7,12 @@ uint32_t levelUp(uint32_t level) } +uint32_t getDefaultLevel() +{ + return defaultLevel; +} + + uint32_t initDefaultPlayer(player_t* player) { // Variables @@ -122,6 +128,24 @@ uint32_t initScreen(screen_t* screen, uint8_t assetID) } +// Sets a tile displayed on the screen to an particular assetID held in the +// global assetTable +uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx) +{ + // Initialize return status to FAILURE + uint32_t result = FAILURE; + + // Check for valid bounds + if (screenIdx < SCREEN_TILES && tableIdx < ASSET_TABLE_SIZE) + { + screen->tiles[screenIdx] = assetTable[tableIdx]; + result = SUCCESS; + } + + return result; +} + + // Simulates a simple battle where only the faster character attacks. void quickBattle(player_t* player, character_t* opponent) { diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index ae12baf1..d67c493c 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -10,6 +10,17 @@ #define SCREEN_ROWS 15 #define SCREEN_COLS 10 #define SCREEN_TILES SCREEN_ROWS*SCREEN_COLS +#define ASSET_TABLE_SIZE 16 + + +const uint8_t assetTable[ASSET_TABLE_SIZE] = {0x01, 0x12, 0x23, 0x34, + 0x45, 0x56, 0x67, 0x78, + 0x89, 0x9A, 0xAB, 0xBC, + 0xCD, 0xDE, 0xEF, 0xF0}; + +const uint32_t defaultLevel = 1; + +extern const uint8_t secretAssetTable[ASSET_TABLE_SIZE]; // Struct containing character information typedef struct { @@ -50,11 +61,13 @@ typedef struct { // Function prototypes uint32_t levelUp(uint32_t level); +uint32_t getDefaultLevel(); uint32_t initDefaultPlayer(player_t* player); uint32_t checkStats(character_t* character); void resolveAttack(character_t* target, uint32_t atk); void resetInventoryItems(inventory_t* inventory); uint32_t initScreen(screen_t* screen, uint8_t assetID); +uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx); void quickBattle(player_t* player, character_t* opponent); uint32_t counterBattle(player_t* player, character_t* opponent); From 2df89eaf86d1915fb89f0b5718243089bdb430b1 Mon Sep 17 00:00:00 2001 From: djmorel Date: Wed, 1 Jun 2022 15:22:44 +0000 Subject: [PATCH 044/120] Replaced the initByteSequence function with repeat --- labs/SAW/Game/proof/Game.py | 9 ++++----- labs/SAW/Game/specs/Game.cry | 5 ----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 710f9f3a..1e3c9b2c 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,5 +1,4 @@ # TODO -# - Replace the silly initByteSequence function with repeat # - Try the alloc_pointsto_buffer example & see its limitations # --> See if possible to keep pointer field # - Global variable @@ -113,9 +112,9 @@ def specification (self): # to assert struct field contents. # Index 0 = "name" field # Recall that 0x41 is ASCII for 'A' - self.points_to(player['name'], cry_f("initByteSequence`{{ {MAX_NAME_LENGTH} }} 0x41")) + self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player[0], cry_f("initByteSequence`{{ {MAX_NAME_LENGTH} }} 0x41")) + # self.points_to(player[0], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) # Index 1 is the "level" field self.points_to(player['level'], cry_f("1 : [32]")) @@ -332,9 +331,9 @@ def specification (self): self.execute_func(screen, assetID) # Assert the postcondition - self.points_to(screen['tiles'], cry_f("initByteSequence`{{ {SCREEN_TILES} }} {assetID}")) + self.points_to(screen['tiles'], cry_f("repeat {assetID} : [{SCREEN_TILES}][8]")) # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(screen[0], cry_f("initByteSequence`{{ {SCREEN_TILES} }} {assetID}")) + # self.points_to(screen[0], cry_f("repeat {assetID} : [{SCREEN_TILES}][8]")) self.returns(cry_f("`({SUCCESS}) : [32]")) diff --git a/labs/SAW/Game/specs/Game.cry b/labs/SAW/Game/specs/Game.cry index 16a09fef..559a14d4 100644 --- a/labs/SAW/Game/specs/Game.cry +++ b/labs/SAW/Game/specs/Game.cry @@ -6,11 +6,6 @@ assetTable = [0x01, 0x12, 0x23, 0x34, 0x89, 0x9A, 0xAB, 0xBC, 0xCD, 0xDE, 0xEF, 0xF0] : [16][8] -initByteSequence : {numBytes} - (fin numBytes, numBytes >= 1, 8 >= width numBytes) - => [8] -> [numBytes][8] -initByteSequence val = [(i*0 + val) | i <- [1..numBytes]] - levelUp : [32] -> [32] levelUp level = level + 1 From 3ef065e808cf701812ff3578ef3117d1f1a39513 Mon Sep 17 00:00:00 2001 From: djmorel Date: Wed, 1 Jun 2022 16:03:24 +0000 Subject: [PATCH 045/120] Modified setScreenTile function to be an extern SAW example --- labs/SAW/Game/proof/Game.py | 16 ++++++---------- labs/SAW/Game/src/Assets.c | 6 ++++++ labs/SAW/Game/src/Game.h | 7 +------ 3 files changed, 13 insertions(+), 16 deletions(-) create mode 100644 labs/SAW/Game/src/Assets.c diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 1e3c9b2c..8e3fab0f 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,9 +1,6 @@ # TODO # - Try the alloc_pointsto_buffer example & see its limitations # --> See if possible to keep pointer field -# - Global variable -# --> Pulling values directly from source code... -# - Extern # - Aligned # - Make a worksheet & solution for the SAW examples # - Update SAW.md to discuss the topics covered in this SAW script @@ -358,13 +355,12 @@ def specification (self): # Pop Quiz: Why can't we just declare and pass the screen pointer? # screen_p = self.alloc(alias_ty("struct.screen_t")) - # Note: The following global initialization is not needed because - # - SAW already understands what values to use - # - We made a local copy in Game.cry to express the expected behavior - # However, in the event that SAW complains that it does not know what - # assetTable is, include the lines! - # assetTable_init = global_initializer("assetTable") - # self.points_to(global_var("assetTable"), assetTable_init) + # Initialize Game.h's assetTable according to Assets.c + # Note: The contents of assetTable from Assets.c was copied into Game.cry + # Required because the global variable in Game.h is declared as an extern, + # and the current way Game.bc is compiled does not include Assets.c in the + # Makefile. + self.points_to(global_var("assetTable"), cry_f("assetTable")) # Assert preconditions depending on the Contract parameter if (self.shouldPass): diff --git a/labs/SAW/Game/src/Assets.c b/labs/SAW/Game/src/Assets.c new file mode 100644 index 00000000..def3e4de --- /dev/null +++ b/labs/SAW/Game/src/Assets.c @@ -0,0 +1,6 @@ +#include "Game.h" + +const uint8_t assetTable[ASSET_TABLE_SIZE] = {0x01, 0x12, 0x23, 0x34, + 0x45, 0x56, 0x67, 0x78, + 0x89, 0x9A, 0xAB, 0xBC, + 0xCD, 0xDE, 0xEF, 0xF0}; \ No newline at end of file diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index d67c493c..333d17ed 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -13,14 +13,9 @@ #define ASSET_TABLE_SIZE 16 -const uint8_t assetTable[ASSET_TABLE_SIZE] = {0x01, 0x12, 0x23, 0x34, - 0x45, 0x56, 0x67, 0x78, - 0x89, 0x9A, 0xAB, 0xBC, - 0xCD, 0xDE, 0xEF, 0xF0}; - const uint32_t defaultLevel = 1; -extern const uint8_t secretAssetTable[ASSET_TABLE_SIZE]; +extern const uint8_t assetTable[ASSET_TABLE_SIZE]; // Struct containing character information typedef struct { From 3eb2ef2d9244d6d2f2d2c1dad96a2ba0139d2270 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Wed, 1 Jun 2022 12:29:30 -0400 Subject: [PATCH 046/120] Update SAW.md --- labs/SAW/SAW.md | 138 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 22 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 503a6681..e7f7843e 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -466,17 +466,6 @@ uint32_t* addRow5NewVar(uint32_t a[5], uint32_t b[5]) { } ``` -Finally, we could mutate the first input and return it: - -```C -uint32_t* addRow5Alias(uint32_t a[5], uint32_t b[5]) { - for(int i = 0 ; i < 5; i++) { - a[i] += b[i]; - } - return a; -} -``` - The corresponding Cryptol specification is: ```cryptol @@ -646,7 +635,7 @@ def addRow5NewVar_Contract(Contract): self.returns(c_p) ``` -While Cryptol has arrays as primitives, it does not have any notion of pointer, so we can not pass a symbolic pointer into `cry_f` (or any function with `_f`). For example, if we wanted to assert `a_p` points to `b_p` then we can use `self.points_to(a_p, b_p)` but **NOT** `self.postcondition_f(a_p, "{b_p}")`. +While Cryptol has arrays, it doesn't have a notion of pointer, so we can't pass a symbolic pointer into `cry_f`. For example, if we wanted to assert `a_p` points to `b_p` then we can use `self.points_to(a_p, b_p)` but **NOT** `self.postcondition_f("{a_p} == {b_p}")`. ### Parameterized Contracts @@ -677,8 +666,8 @@ many more) capabilities. This is obviously well beyond the scope of the course, but worth mentioning. One option here is to make a new contract for each `length` that's -actually used. Instead we can make a single contract with a length -input: +actually used. Instead we can make a single contract with a `length` +parameter: ```python def addRowAlias_Contract(Contract): @@ -691,8 +680,6 @@ def addRowAlias_Contract(Contract): (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) length = fresh_var(i8, "length") - self.precondition_f("{length} == {self.length}") - self.execute_func(a_p, b_p, length) self.points_to(a_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) @@ -722,8 +709,6 @@ class ArrayTests(unittest.TestCase): ## Array Example Full Code and Debugging SAW -//remove precondition in previous example and detect an issue here? - ```python import unittest from saw_client import * @@ -770,8 +755,6 @@ def addRowAlias_Contract(Contract): (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) length = fresh_var(i8, "length") - self.precondition_f("{self.length} == {length}") - self.execute_func(a_p, b_p, length) self.points_to(a_p, cry_f("rowAdd`{{{length}}} {a} {b}")) @@ -801,6 +784,117 @@ class ArrayTests(unittest.TestCase): self.assertIs(addRowAlias10_result.is_success(), True) ``` +What do you think will happen if we run this code? + +
+ Click here to find out! + Running the code, SAW verifies the first two contracts + + ``` + $ python3 addRow.py + [15:40:51.330] Verifying addRow5Mutate ... + [15:40:51.330] Simulating addRow5Mutate ... + [15:40:51.335] Checking proof obligations addRow5Mutate ... + [15:40:51.362] Proof succeeded! addRow5Mutate + ✅ Verified: lemma_addRow5Mutate_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/addRow.py:64) + [15:40:51.430] Verifying addRow5NewVar ... + [15:40:51.430] Simulating addRow5NewVar ... + [15:40:51.435] Checking proof obligations addRow5NewVar ... + [15:40:51.462] Proof succeeded! addRow5NewVar + ✅ Verified: lemma_addRow5NewVar_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/addRow.py:67) + ``` + + ...but fails to verify the third contract. It alerts us there is a memory error + + ``` + [15:40:51.527] Verifying addRowAlias ... + [15:40:51.528] Simulating addRowAlias ... + [15:40:51.532] Symbolic simulation completed with side conditions. + [15:40:51.535] Checking proof obligations addRowAlias ... + [15:40:51.575] Subgoal failed: addRowAlias safety assertion: + internal: error: in addRowAlias + Error during memory load + ``` + + and even produces the following counterexample: + + ``` + [15:40:51.575] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 331} + [15:40:51.575] ----------Counterexample---------- + [15:40:51.575] length0: 6 + [15:40:51.575] : False + [15:40:51.575] ---------------------------------- + ⚠️ Failed to verify: lemma_addRowAlias_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/addRow.py:37): + error: Proof failed. + stdout: + [15:40:51.527] Verifying addRowAlias ... + [15:40:51.528] Simulating addRowAlias ... + [15:40:51.532] Symbolic simulation completed with side conditions. + [15:40:51.535] Checking proof obligations addRowAlias ... + [15:40:51.575] Subgoal failed: addRowAlias safety assertion: + internal: error: in addRowAlias + Error during memory load + + [15:40:51.575] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 331} + [15:40:51.575] ----------Counterexample---------- + [15:40:51.575] length0: 6 + [15:40:51.575] : False + [15:40:51.575] ---------------------------------- + F + ====================================================================== + FAIL: test_rowAdds (__main__.ArrayTests) + ---------------------------------------------------------------------- + Traceback (most recent call last): + File "/home/cryptol/cryptol-course/labs/SAW/proof/addRow.py", line 71, in test_rowAdds + self.assertIs(addRowAlias05_result.is_success(), True) + AssertionError: False is not True + + ---------------------------------------------------------------------- + Ran 1 test in 1.735s + + FAILED (failures=1) + 🛑 1 out of 3 goals failed to verify. + ``` + + SAW is telling us we forgot to add a precondition to assert our symbolic `length` agrees with our Python parameter `self.length`. This is an easy fix: + + ``` + def addRowAlias_Contract(Contract): + def __init__(self, length : int): + super().__init__() + self.length = length + + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) + length = fresh_var(i8, "length") + + self.precondition_f("{self.length} == {length}") + + self.execute_func(a_p, b_p, length) + + self.points_to(a_p, cry_f("rowAdd`{{{length}}} {a} {b}")) + + self.returns(a_p) + ``` + +And now SAW happily verifies the third contract! + + ``` + $ python3 addRow.py + ✅ Verified: lemma_addRow5Mutate_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/addRow.py:64) + ✅ Verified: lemma_addRow5NewVar_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/addRow.py:67) + ✅ Verified: lemma_addRowAlias_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/addRow.py:37) + . + ---------------------------------------------------------------------- + Ran 1 test in 1.985s + + OK + ✅ All 3 goals verified! + ``` + +
+ ## Explicit Arrays Another way to initialize arrays is through the `array` command. For @@ -843,7 +937,7 @@ specification for postconditions as was done in the previous examples. ## Null Pointers -Consider the following C code that checks if a pointer is null: +Consider the following C code to check if a pointer is null: ```C int f(int *x) { @@ -851,7 +945,7 @@ int f(int *x) { } ``` -What do you think is the behavior of running the following contract? Keep in mind that booleans in C are encoded as `0` for false and `1` for true. +The following contract has possibly unexpected behavior (in C booleans are encoded as `0` for false and `1` for true). ```Python import unittest From 0746144b2ef4a55379aa6686a99f616aba4368ce Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:00:14 -0400 Subject: [PATCH 047/120] Update SAW.md --- labs/SAW/SAW.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index e7f7843e..790699b5 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -554,7 +554,7 @@ A SAW contract is strictly divided into three parts: 2. Execution 3. Postconditions -SAW will complain if you place a precondition after `execute_func` and +SAW complains if you place a precondition after `execute_func` and similarly for postcondition. If a function returns a value that was not passed through `execute_func`, then you will have to initialize new fresh symbolic variables. For example, consider the proposed @@ -576,8 +576,18 @@ def addRow5NewVar_Contract(Contract): Running a unit test yields the following error message: -```sh -SAW error message +``` +[16:42:51.066] Subgoal failed: addRow5NewVar safety assertion: +internal: error: in _SAW_verify_prestate SAWServer +The following pointers had to alias, but they didn't: + (12, 0x0:[64]) + (7, 0x0:[64]) + + +[16:42:51.067] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 29} +[16:42:51.067] ----------Counterexample---------- +[16:42:51.067] <> +[16:42:51.067] ---------------------------------- ``` Think about the precondition block, the part before `execute_func`, as From 1b37a6360ef60305d7b59397ad7bf74f9a8a0511 Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:10:29 -0400 Subject: [PATCH 048/120] Create GameOutline.md --- labs/SAW/Game/GameOutline.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 labs/SAW/Game/GameOutline.md diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md new file mode 100644 index 00000000..8fc4774c --- /dev/null +++ b/labs/SAW/Game/GameOutline.md @@ -0,0 +1 @@ +# Initial Commit From 7ec8a08d9c379336eaac8906effcf93d971e4095 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Wed, 1 Jun 2022 13:45:57 -0400 Subject: [PATCH 049/120] Update SAW.md --- labs/SAW/SAW.md | 76 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 790699b5..f8cc0bdc 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -186,17 +186,17 @@ braces for these cases when parsing Python strings. For example, let's think how to parse the following line: ```python - self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} {blah} }} == foo `") + self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} {blah} }} == foo `eel") ``` If `blah` is a local Python variable equal to `23` and `N` is a local Python variable equal to `2`, then the string parses in Cryptol as ```cryptol -{a = take`{5} 23, b = take`{2} 23} == foo `blah +{a = take`{5} 23, b = take`{2} 23} == foo `eel ``` -where `foo` is some Cryptol function returning a record and `blah` is +where `foo` is some Cryptol function returning a record and `eel` is some Cryptol type in the specification loaded. ## Unit Testing @@ -453,19 +453,6 @@ void addRow5Mutate(uint32_t a[5], uint32_t b[5]) { } ``` -An alternative way to do this would be to create a new array storing -the result and return it: - -```C -uint32_t* addRow5NewVar(uint32_t a[5], uint32_t b[5]) { - uint32_t* c = (uint32_t*) malloc(5*sizeof(uint32_t)); - for(int i = 0 ; i < 5; i++) { - c[i] = a[i] + b[i]; - } - return c; -} -``` - The corresponding Cryptol specification is: ```cryptol @@ -549,7 +536,19 @@ def addRow5Mutate_Contract(Contract): ### Auxiliary Variables -A SAW contract is strictly divided into three parts: +In this section we discuss an alternative way add two rows by using an auxillary variable. For example, consider the function + +```C +uint32_t* addRow5NewVar(uint32_t a[5], uint32_t b[5]) { + uint32_t* c = (uint32_t*) malloc(5*sizeof(uint32_t)); + for(int i = 0 ; i < 5; i++) { + c[i] = a[i] + b[i]; + } + return c; +} +``` + +Recall that a SAW contract is strictly divided into three parts: 1. Preconditions 2. Execution 3. Postconditions @@ -649,7 +648,7 @@ While Cryptol has arrays, it doesn't have a notion of pointer, so we can't pass ### Parameterized Contracts -Suppose our C code was written as +Now let's consider a third possible way to write the same function. Here we are given two arrays of arbitrary size and a length. ```C uint32_t* addRowAlias(uint32_t* a, uint32_t* b, uint8_t length) { @@ -660,8 +659,7 @@ uint32_t* addRowAlias(uint32_t* a, uint32_t* b, uint8_t length) { } ``` -where the length of the array is not specified. The corresponding -Cryptol code might be: +A corresponding Cryptol specification might is ```cryptol addRow : {length} (fin length) => [length][32] -> [length][32] -> [length][32] @@ -675,8 +673,7 @@ a much more powerful theorem prover that has inductive reasoning (and many more) capabilities. This is obviously well beyond the scope of the course, but worth mentioning. -One option here is to make a new contract for each `length` that's -actually used. Instead we can make a single contract with a `length` +We could make a new contract for each value of `length` used in the C code. Instead we make a single contract with a `length` parameter: ```python @@ -993,10 +990,26 @@ if __name__ == "__main__": It turns out the contract above will fail! ``` - saw failure + $ python3 null.py + [17:21:44.701] Verifying isNull ... + [17:21:44.701] Simulating isNull ... + [17:21:44.703] Checking proof obligations isNull ... + [17:21:44.724] Subgoal failed: isNull safety assertion: + internal: error: in _SAW_verify_prestate SAWServer + Literal equality postcondition + [more error message] + FAILED (failures=1) + 🛑 The goal failed to verify. ``` - An initialized symbolic pointer that hasn't been assigned a symbolic variable to point to is **NOT** equivalent to a symbolic null pointer. We can use `null()` in situations where we want a null pointer. For example, if we change the contract above to + The counterexample produced may seem mystical. + ``` + [17:21:44.725] ----------Counterexample---------- + [17:21:44.725] <> + [17:21:44.725] ---------------------------------- + ``` + + However, if **every** setting is a counterexample, then this is telling us the pointer must have been the null pointer! An initialized symbolic pointer that hasn't been assigned a symbolic variable to point to is **NOT** equivalent to a null pointer in SAW. We can use `null()` in situations where we want a null pointer. For example, if we change the contract above to ``` class FContract(Contract): @@ -1006,10 +1019,21 @@ if __name__ == "__main__": self.returns(cry("1 : [32]")) ``` - then SAW is a happy: + then SAW is a happy. ``` - saw error message + $ python3 null.py + [17:33:50.802] Verifying isNull ... + [17:33:50.802] Simulating isNull ... + [17:33:50.804] Checking proof obligations isNull ... + [17:33:50.804] Proof succeeded! isNull + ✅ Verified: lemma_isNull_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/null.py:22) + . + ---------------------------------------------------------------------- + Ran 1 test in 1.301s + + OK + ✅ The goal was verified! ``` From 48070397ea0c0f0240bb96cff2b06fec90b7eafc Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:13:31 -0400 Subject: [PATCH 050/120] Updated initDefaultPlayer_Contract's incorrect alternatives with repeat --- labs/SAW/Game/proof/Game.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 8e3fab0f..9fdb7b8f 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -139,11 +139,11 @@ def specification (self): # self.points_to(player[5], cry_f("3 : [32]")) # Incorrect Alternative 1: Invalid label in record update. - # self.points_to(player, cry_f("{{ [(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) + # self.points_to(player, cry_f("{{ [repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) # Incorrect Alternative 2: SAW doesn't yet support translating Cryptol's # record type(s) into crucible-llvm's type system. - # self.points_to(player, cry_f("{{ name=[(i*0 + 0x41) | i <- [1..{MAX_NAME_LENGTH}]], level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) + # self.points_to(player, cry_f("{{ name=[repeat 0x41 : [{MAX_NAME_LENGTH}][8], level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) self.returns(cry_f("`({SUCCESS}) : [32]")) @@ -547,4 +547,4 @@ def test_Game(self): if __name__ == "__main__": unittest.main() -####################################### \ No newline at end of file +####################################### From 2e8e90bb0d3a1028afe8a3a3f149e038992b7743 Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:15:21 -0400 Subject: [PATCH 051/120] Updated initDefaultPlayer_Contract's incorrect alternatives --- labs/SAW/Game/proof/Game.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 9fdb7b8f..41f07c04 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -139,11 +139,11 @@ def specification (self): # self.points_to(player[5], cry_f("3 : [32]")) # Incorrect Alternative 1: Invalid label in record update. - # self.points_to(player, cry_f("{{ [repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) + # self.points_to(player, cry_f("{{ repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) # Incorrect Alternative 2: SAW doesn't yet support translating Cryptol's # record type(s) into crucible-llvm's type system. - # self.points_to(player, cry_f("{{ name=[repeat 0x41 : [{MAX_NAME_LENGTH}][8], level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) + # self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) self.returns(cry_f("`({SUCCESS}) : [32]")) From 0195bcd3e46309f5c73bd510886e6c078e2bf76f Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:22:42 -0400 Subject: [PATCH 052/120] Update GameOutline.md --- labs/SAW/Game/GameOutline.md | 81 ++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md index 8fc4774c..bf7ce220 100644 --- a/labs/SAW/Game/GameOutline.md +++ b/labs/SAW/Game/GameOutline.md @@ -1 +1,82 @@ # Initial Commit + +Below is a list of functions included in Game/. + +## levelUp(uint32_t level); +**Goal:** How to set up and verify a very simple function in a SAW contract. + +**Lessons Learned** +- How to declare a fresh variable in SAW +- How to call a Cryptol function +- How to pass SAW types to Cryptol function (curly braces) + +**DJ's Notes:** +- This function was more of a proof of concept for me to get familiar with SAW's Python API. +- Lessons Learned here are already covered in previous SAW.md sections + + +## getDefaultLevel(); +**Goal:** How to handle global variables in a SAW contract. + +**Lessons Learned:** +- How and when to use `global_initializer` + - When the source code already initializes the value and SAW should just use that value +- How to declare a global variable in SAW (`global_var`) +- How to set a global variable to an initialized value in a SAW contract (`points_to`) +- When to pass no inputs to `execute_func` + + +## initDefaultPlayer(player_t* player); +**Goal:** How to setup a struct variable in a SAW contract. + +**Lessons Learned:** +- How and when to use `alias_ty` +- How and when to use `alloc` + - Use `alloc` when you only consider the pointer + - Use `ptr_to_fresh` when you care about the values being referenced (i.e. for pre/postconditions) and the pointer +- SAW only recognizes the base struct name (`player_t` vs `character_t`) +- Passing global variables/parameters/defines defined in SAW to contracts + - Values copied from the source code's defines + - Examples: `MAX_NAME_LENGTH`, `SUCCESS` +- How to assert postconditions using `points_to` +- Compiling clang with the `-g` flag provides debug symbols, so you can reference fields names rather than just indices +- SAW has issues using the `points_to` postcondition for describing behavior associated for a struct with many fields + - See the **Errors to Explore** section below + - This contract requires explicitly checking each and every struct field + +**Errors to Explore:** +- "Invalid label in record update" + - Generated when using the following postcondition (not using struct field names) +```python +self.points_to(player, cry_f("{{ repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) +``` +- "SAW doesn't yet support translating Cryptol's record type(s) into crucible-llvm's type system" + - Generated when using the following postcondition (with struct field names) +```python +self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) +``` + +## checkStats(character_t* character); + + +## resolveAttack(character_t* target, uint32_t atk); + + +## resetInventoryItems(inventory_t* inventory); + + +## initScreen(screen_t* screen, uint8_t assetID); + + +## setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx); + + +## quickBattle(player_t* player, character_t* opponent); + + +## counterBattle(player_t* player, character_t* opponent); +Note: No Contract for this one. Considering dropping due to how complex the Contract needs to be (multiple possible outcomes depending on the inputs - requires specific preconditions and postconditions) + + +SAW finds something not true, counterexample. +- Logical error in the functions From 8e46ea476d44d1aa0ca8f77094acc01f8a16d5d5 Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 14:44:17 -0400 Subject: [PATCH 053/120] Update GameOutline.md --- labs/SAW/Game/GameOutline.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md index bf7ce220..0df473c9 100644 --- a/labs/SAW/Game/GameOutline.md +++ b/labs/SAW/Game/GameOutline.md @@ -3,7 +3,7 @@ Below is a list of functions included in Game/. ## levelUp(uint32_t level); -**Goal:** How to set up and verify a very simple function in a SAW contract. +**Goal:** Determine how to set up and verify a very simple function in a SAW contract. **Lessons Learned** - How to declare a fresh variable in SAW @@ -16,7 +16,7 @@ Below is a list of functions included in Game/. ## getDefaultLevel(); -**Goal:** How to handle global variables in a SAW contract. +**Goal:** Determine how to handle global variables in a SAW contract. **Lessons Learned:** - How and when to use `global_initializer` @@ -27,7 +27,7 @@ Below is a list of functions included in Game/. ## initDefaultPlayer(player_t* player); -**Goal:** How to setup a struct variable in a SAW contract. +**Goal:** Determine how to setup a struct variable in a SAW contract. **Lessons Learned:** - How and when to use `alias_ty` @@ -57,7 +57,22 @@ self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), le ``` ## checkStats(character_t* character); +**Goal:** Determine how to use parameterized contracts to set preconditions and postconditions. +**Lessons Learned:** +- Understand that when a function's behavior depends on specific input value ranges, it may be necessary to define multiple cases for a given SAW contract + - Determine how to break a function into its fundamental case behaviors (i.e. `SUCCESS` and `FAILURE`) +- How to parameterize a SAW contract + - Understand which parameter type is best for the job (i.e. `int`, `bool`, `string`, etc) +- How to pass and use parameters within a SAW contract + - Note that SAW's Python API takes advantage of if-statements, which reduces the number of SAW contracts/specs that we need to write +- Understand that `ptr_to_fresh` is useful for setting preconditions for a struct's fields +- How to use contract parameters to assert two possible postconditions + +**DJ's Notes:** +- Note that the `checkStats` function would be used in the Game library to check character stats every instance before such stats would be referenced/used for gameplay. +- In terms of the example, `checkStats` provides gameplay balancing by limiting how well characters can perform. +- Given this policy, all other functions included in the library assume that `checkStats` is called before them. ## resolveAttack(character_t* target, uint32_t atk); From 03de51d7baac4efea0ecad8d4ba5e2fce560a536 Mon Sep 17 00:00:00 2001 From: Taylor Ball Ball Date: Wed, 1 Jun 2022 19:23:16 +0000 Subject: [PATCH 054/120] added module files --- labs/SAW/proof/rcs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labs/SAW/proof/rcs.py b/labs/SAW/proof/rcs.py index f6a5d71b..afefdf0c 100644 --- a/labs/SAW/proof/rcs.py +++ b/labs/SAW/proof/rcs.py @@ -21,7 +21,7 @@ def test_RCS(self): if __name__ == "__main__": view(LogResults(verbose_failure=True)) pwd = os.getcwd() - bcname = pwd + "/../src/rcs.bc" + bcname = pwd + "/../src/rcs3.bc" cryname = pwd + "/spec/rcs.cry" cryptol_load_file(cryname) From bd449eb19924a9140c97624c5d3c772b8f27ed0e Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 15:26:18 -0400 Subject: [PATCH 055/120] Update GameOutline.md --- labs/SAW/Game/GameOutline.md | 101 ++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md index 0df473c9..bed72968 100644 --- a/labs/SAW/Game/GameOutline.md +++ b/labs/SAW/Game/GameOutline.md @@ -68,13 +68,101 @@ self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), le - Note that SAW's Python API takes advantage of if-statements, which reduces the number of SAW contracts/specs that we need to write - Understand that `ptr_to_fresh` is useful for setting preconditions for a struct's fields - How to use contract parameters to assert two possible postconditions +- How to represent multiple Unit Tests on the same contract with different input parameters **DJ's Notes:** - Note that the `checkStats` function would be used in the Game library to check character stats every instance before such stats would be referenced/used for gameplay. - In terms of the example, `checkStats` provides gameplay balancing by limiting how well characters can perform. - Given this policy, all other functions included in the library assume that `checkStats` is called before them. + - This means we will use the preconditions from `checkStats_Contract` in their contracts + - We will show an example in `resolveAttack` about what happens when the preconditions aren't used +- Discuss the coding & security tradeoffs between checks made in callers vs callees ## resolveAttack(character_t* target, uint32_t atk); +**Goal:** Expand upon the lessons learned with `checkStats` to get a contract ready for overrides/lemmas. + +**Lessons Learned:** +- All of the points referenced in `checkStats` **Lessons Learned** +- Understand how to write cases for a function with more than 2 possible behaviors +- Understand when it is appropriate to include preconditions for functions that lack input checks + - Goes back to the caller vs callee tradeoffs mentioned in `checkStats` **DJ's Notes** + +**Errors to Explore:** +- Explain why the verification fails when the preconditions for `resolveAttack` are commented out. + - Spoiler alert, integer overflow/underflow/wrap-around +- Explain why SAW produces a counterexample when... + - the contract looks like: +```python +# Contract +class resolveAttack_Contract(Contract): + def __init__(self, case : int): + super().__init__() + self.case = case + # There are 3 possible cases for resolveAttack + # Case 2: Immediate KO + # Case 1: Attack mitigated + # Case 3: Regular attack + + def specification (self): + # Declare variables + (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") + atk = self.fresh_var(i32, "atk") + + # Assert the precondition that the stats are below the max stat cap + self.precondition_f("{atk} <= `{MAX_STAT}") + self.precondition_f("{target}.2 <= `{MAX_STAT}") + self.precondition_f("{target}.4 <= `{MAX_STAT}") + + # Determine the preconditions based on the case parameter + if (self.case == 1): + # target->hp <= (atk - target->def) + self.precondition_f("({target}.2 + {target}.4) <= {atk}") + elif (self.case == 2): + # target->def >= atk + self.precondition_f("{target}.4 >= {atk}") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("{target}.4 < {atk}") + self.precondition_f("({target}.2 + {target}.4) > {atk}") + + # Symbolically execute the function + self.execute_func(target_p, atk) + + # Determine the postcondition based on the case parameter + if (self.case == 1): + self.points_to(target_p['hp'], cry_f("0 : [32]")) + elif (self.case == 2): + self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) + else: + self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + + self.returns(void) +``` + - and the source code looks like: +```c +// Source code +void resolveAttack(character_t* target, uint32_t atk) +{ + if ( target->hp <= (atk - target->def) ) + { + // The attack will knock out the target + target->hp = 0; + } + else if ( target->def >= atk) + { + // The target's defense mitigates the attack + target->hp = target->hp; + } + else + { + // Calculate damage as normal + target->hp = target->hp - (atk - target->def); + } +} +``` + - Spoiler alert: Logical error in the source code where the first condition already covers the second condition + +## selfDamage(player_t* player); ## resetInventoryItems(inventory_t* inventory); @@ -93,5 +181,14 @@ self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), le Note: No Contract for this one. Considering dropping due to how complex the Contract needs to be (multiple possible outcomes depending on the inputs - requires specific preconditions and postconditions) -SAW finds something not true, counterexample. -- Logical error in the functions +# TODO +- Add `selfDamage` prototype to `Game.h` +- Move `quickBattle` to come right after `selfDamage`, which is after `resolveAttack` given that it makes sense in the lesson plan +- Consider moving `resetInventoryItems`, `initScreen`, and `setScreenTile` earlier in the lesson plan + - Recall that `setScreenTile` shows extern global variables +- Confirm the error cases yield the expected error results + - Necessary because some of these errors were encountered and resolved already. + - Want to properly recreate them for learning purposes. +- Determine if the resolveAttack logical error should be kept +- Determine if counterBattle should be kept + - Current thoughts are no given how many behavior states must be considered From 539a5c6c208c720b0175068331d3ae2660609d0d Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Wed, 1 Jun 2022 15:28:33 -0400 Subject: [PATCH 056/120] Update SAW.md --- labs/SAW/SAW.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index f8cc0bdc..3a7cd15e 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -460,7 +460,7 @@ addRow5 : [5][32] -> [5][32] -> [5][32] addRow5 a b = a + b ``` -### Initializing Arrays and Pointers +## Initializing Arrays and Pointers To initialize the arrays and pointers we'll use the `alloc` command and `array_ty` type constructor: @@ -534,7 +534,7 @@ def addRow5Mutate_Contract(Contract): self.returns(void) ``` -### Auxiliary Variables +## Auxiliary Variables In this section we discuss an alternative way add two rows by using an auxillary variable. For example, consider the function @@ -646,7 +646,7 @@ def addRow5NewVar_Contract(Contract): While Cryptol has arrays, it doesn't have a notion of pointer, so we can't pass a symbolic pointer into `cry_f`. For example, if we wanted to assert `a_p` points to `b_p` then we can use `self.points_to(a_p, b_p)` but **NOT** `self.postcondition_f("{a_p} == {b_p}")`. -### Parameterized Contracts +## Parameterized Contracts Now let's consider a third possible way to write the same function. Here we are given two arrays of arbitrary size and a length. @@ -714,7 +714,7 @@ class ArrayTests(unittest.TestCase): self.assertIs(addRowAlias10_result.is_success(), True) ``` -## Array Example Full Code and Debugging SAW +## Full Code Example and Debugging SAW ```python import unittest From e14a7adcebfd902c33ae17f78d909ddb0692fcc6 Mon Sep 17 00:00:00 2001 From: Taylor Ball Ball Date: Wed, 1 Jun 2022 19:29:10 +0000 Subject: [PATCH 057/120] added files used in examples --- labs/SAW/proof/addRow.py | 74 +++++++++++++++++++++++++++++++++ labs/SAW/proof/null.py | 26 ++++++++++++ labs/SAW/proof/spec/addRow.cry | 9 ++++ labs/SAW/proof/spec/addRow.cry~ | 9 ++++ 4 files changed, 118 insertions(+) create mode 100644 labs/SAW/proof/addRow.py create mode 100644 labs/SAW/proof/null.py create mode 100644 labs/SAW/proof/spec/addRow.cry create mode 100644 labs/SAW/proof/spec/addRow.cry~ diff --git a/labs/SAW/proof/addRow.py b/labs/SAW/proof/addRow.py new file mode 100644 index 00000000..a2fe0164 --- /dev/null +++ b/labs/SAW/proof/addRow.py @@ -0,0 +1,74 @@ +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * +from saw_client.proofscript import * +from saw_client.llvm_type import * + +def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None, read_only : Optional[bool] = False) -> Tuple[FreshVar, SetupVal]: + var = c.fresh_var(ty, name) + ptr = c.alloc(ty, points_to = var, read_only=read_only) + return (var, ptr) + +class addRow5Mutate_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + self.points_to(a_p, cry_f("addRow {a} {b}")) + self.returns(void) + +class addRow5NewVar_Contract(Contract): + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(5, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(5, i32), name="b", read_only=True) + + self.execute_func(a_p, b_p) + + (c, c_p) = ptr_to_fresh(self, array_ty(5, i32), name="c") + self.points_to(c_p, cry_f("addRow {a} {b}")) + + self.returns(c_p) + +class addRowAlias_Contract(Contract): + def __init__(self, length : int): + super().__init__() + self.length = length + + def specification(self): + (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a") + (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) + length = self.fresh_var(i8, "length") + + self.precondition_f("{length} == {self.length}") + + self.execute_func(a_p, b_p, length) + + self.points_to(a_p, cry_f("addRow`{{{self.length}}} {a} {b}")) + + self.returns(a_p) + +class ArrayTests(unittest.TestCase): + def test_rowAdds(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + bcname = "../src/addRow.bc" + cryname = "spec/addRow.cry" + + cryptol_load_file(cryname) + mod = llvm_load_module(bcname) + + addRow5Mutate_result = llvm_verify(mod, 'addRow5Mutate', addRow5Mutate_Contract()) + self.assertIs(addRow5Mutate_result.is_success(), True) + + addRow5NewVar_result = llvm_verify(mod, 'addRow5NewVar', addRow5NewVar_Contract()) + self.assertIs(addRow5NewVar_result.is_success(), True) + + addRowAlias05_result = llvm_verify(mod, 'addRowAlias', addRowAlias_Contract(5)) + self.assertIs(addRowAlias05_result.is_success(), True) + +if __name__ == "__main__": + unittest.main() diff --git a/labs/SAW/proof/null.py b/labs/SAW/proof/null.py new file mode 100644 index 00000000..749602e0 --- /dev/null +++ b/labs/SAW/proof/null.py @@ -0,0 +1,26 @@ +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * + + +class isNull_Contract(Contract): + def specification(self): + # p = self.alloc(i32) + + self.execute_func(null()) + + self.returns(cry("1 : [32]")) + +class LLVMAssertNullTest(unittest.TestCase): + def test_llvm_assert_null(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + bcname = '../src/null.bc' + mod = llvm_load_module(bcname) + + result = llvm_verify(mod, 'isNull', isNull_Contract()) + self.assertIs(result.is_success(), True) + +if __name__ == "__main__": + unittest.main() diff --git a/labs/SAW/proof/spec/addRow.cry b/labs/SAW/proof/spec/addRow.cry new file mode 100644 index 00000000..bd7f4469 --- /dev/null +++ b/labs/SAW/proof/spec/addRow.cry @@ -0,0 +1,9 @@ +module addRow where + + +addRow5 : [5][32] -> [5][32] -> [5][32] +addRow5 a b = a + b + +addRow : {length} (fin length) => [length][32] -> [length][32] -> [length][32] +addRow a b = a + b + diff --git a/labs/SAW/proof/spec/addRow.cry~ b/labs/SAW/proof/spec/addRow.cry~ new file mode 100644 index 00000000..f3b3d061 --- /dev/null +++ b/labs/SAW/proof/spec/addRow.cry~ @@ -0,0 +1,9 @@ +module addRow where + + +addRow5 : [5][32] -> [5][32] -> [5][32] +addRow5 a b = a + b + +addRow : {length} (fin length) => [n][32] -> [n][32] -> [n][32] +addRow a b = a + b + From a73f7e1ca2c042a8d966b79abecc810d4ffb6b90 Mon Sep 17 00:00:00 2001 From: Taylor Ball Ball Date: Wed, 1 Jun 2022 19:34:24 +0000 Subject: [PATCH 058/120] added files used in course --- labs/SAW/src/addRow.bc | Bin 0 -> 3372 bytes labs/SAW/src/addRow.c | 25 +++++++++++++++++++++++++ labs/SAW/src/null.bc | Bin 0 -> 2152 bytes labs/SAW/src/null.c | 5 +++++ labs/SAW/src/rcs.bc | Bin 0 -> 2240 bytes labs/SAW/src/rcs3.bc | Bin 0 -> 2304 bytes 6 files changed, 30 insertions(+) create mode 100644 labs/SAW/src/addRow.bc create mode 100644 labs/SAW/src/addRow.c create mode 100644 labs/SAW/src/null.bc create mode 100644 labs/SAW/src/null.c create mode 100644 labs/SAW/src/rcs.bc create mode 100644 labs/SAW/src/rcs3.bc diff --git a/labs/SAW/src/addRow.bc b/labs/SAW/src/addRow.bc new file mode 100644 index 0000000000000000000000000000000000000000..d67eafae9b7ced0e527adc4010ca57334200752f GIT binary patch literal 3372 zcmaJ@e@q+K9e?JG??TMok;K{Ljz%8jxLu?XP?C}}CB4V|K`()N9( zB$~ADN$=jhci;DY-_Pg0@8?~Mn4UXWjZis4=%iZMb?o>D-F^R>II*=|YE!_NUX9Rt z8A4mj^avXCC{!|Bf6+0eJgQ=imP?AL`tfqAMyLGsNvZnjvX)C*TgF&pwVBd9Q(=5= z#Oa;X*SBtyW31y+S?3h>PBkwc8R7q;Fg_R6>@n<#H=ZAV@go1>6!?9WM`#efO(#`3$x5K(=hfcyahzc0wa|zibk>?EZ0Jt*9CV>q0$X@>8 z^f$FPGul~6J6*&MXVbcA54T*@E~F|~_1dYhuAq<^t} zwR5S;TPfYFLN_bvvJP%0#VsiW2o)A3OVf!ViX8fX)0gzxk22cnFgI)80TAXqy4f^0 zpQ@bIbIS@X@>76X_91F0D4UWb&=Qp7L9guJ3`Wu|q;%5?ekrWWdbm$gmGj^P*w}ux%BOe@lal$5=3*>502J&^%P4xu##vy!Y_k!UmwD0CmCxaCx3J_XF^ zK6ijCopew`o=+2|4#J<7&&5eoMPvLJk4#8Zvl= zoad$iphI`l!=X2Iw>`Qn!~_0;w4@f6C_H5BN@=qa@{je?l(3HHY-ow4R|IAeu}eHtEYOPu)!G_;cb;B% z(cd6!L)gbfOFn0tP}rtKOANcROoT0I(HbQz52vrL(S31x)lJ_ksFo1ZSD=e_W*H?! zGpsU7*stZR6JcvKV$F+n3Boqvfa%Ri!c7;`^gS1iiu6|n)x9|V**v{yr~8u3BF}s! zFnw-%t)TjnrPn1|vOqkrI*MAa1CWR;d7uyb3u@>?9h= zsNUQVgg8118leQO*gotY=GEJyQi880aDO+PN9+A^YcgVz#ld0=cJ! z^zIhQOJH!5O4@p>VurwRU!2NP? zJ0!^`D0x65`#?St;k}S)tMl}?X~vweXzX$bczD8I^BbI!}b=Z9X8-h5U@{pHA=p2vGlW@j-IwvoVsB5

g^5mHT0f2<8A0W>mT&`&b;jF^7~=EdP>na%vLw4 zTU~jUYEnjG%H4)~lj(ia<@XM~d-y%$V@>akU;ShI@Xkj{g9qL<9{N*zSt*6yg1mqv zL|ZF-1~_kIsKc88j5*j@OCdMB#N9|$E<12K02RxneSGx59XI`%3rpkzJ|rcf9GAi* z{6OH-8@{_Jc<|lm_tb(sMOQMjH)2<|QZ22Tnq7AtnzD*4|1^6nJH;Pmve>3v1(QVqT~!hmkJ}rD z`HFHpzsIh||A{|WJ=+3Jsd#vVAdLTtZ?xi-EM*f3CSKKvy0?_oW;Oj3dmz!pj!3LyimH$7YNkZHLo-RWJo5m9 zxgNcyOc`S8OY6Kk^F3sNeMF_&K>?l@FdoHA!@VqvdEda%7{{FA;^7h61MMcym|x)e zhcOH&TMZ8|gi9DWrO<1qjv!dg(TGD*c7J3#hWYM!7R7vc?BAs}2qqwjEI& z4Y3!bVWB~A&q$hKMdEP{RY4Gy$_tg$6B_CGu(ZcvqDB-O@&4oi?*kAozT1p6yEBTu zR%+tMCE~AG=@IIJyPy%;ZNz90Fh40e6r`El3K3m#ver?>aasf{5=?`e;JLo0%2(+Sgoecn_1dQbPC Y4eoon|7=6Ue!uV3{teTHQ`jx~4>(&j0{{R3 literal 0 HcmV?d00001 diff --git a/labs/SAW/src/addRow.c b/labs/SAW/src/addRow.c new file mode 100644 index 00000000..dd9f3989 --- /dev/null +++ b/labs/SAW/src/addRow.c @@ -0,0 +1,25 @@ +#include +#include +#include + +void addRow5Mutate(uint32_t a[5], uint32_t b[5]) { + for(int i = 0; i < 5; i++) { + a[i] += b[i]; + } + return; +} + +uint32_t* addRow5NewVar(uint32_t a[5], uint32_t b[5]) { + uint32_t* c = (uint32_t*) malloc(5*sizeof(uint32_t)); + for(int i = 0; i < 5; i++) { + c[i] = a[i] + b[i]; + } + return c; +} + +uint32_t* addRowAlias(uint32_t* a, uint32_t* b, uint8_t length) { + for(int i = 0; i < length; i++) { + a[i] += b[i]; + } + return a; +} diff --git a/labs/SAW/src/null.bc b/labs/SAW/src/null.bc new file mode 100644 index 0000000000000000000000000000000000000000..c5eaa5893a0fd45b9646589d51b486035a9e39af GIT binary patch literal 2152 zcmaJ?Urbxq89(L%=K|*1{80_5 zu#JnRYp%OPR%>%2q*!Vu*_ch9+P>oq zcpZdW*q!hir?3n!BkL+tJj$zi-Z5~`o1hY zTodvUzBqUiT-b4o#WcU0YA(wBflfev7Q7}t#0Epen#2&6kV5=KWlLnYZ3cP{m;* z4m`hDJ^h^#`&9gjJi?v0_4o6)el>ph=EqN7F?@=g2uajnXeu(J>&-Q9f6gHE+rj5G zYY`J{XUj-D)^O{Du@t)^P^8TfV!_1U$bvGZL3WGeXT#r*b7JP0T=$jgWvE#HR3l^ z3LRQzCLDNH2V#RN8E1*NgZKaniO6e*N8I5q_s=v}F*BEQDZZTI0ZA}!yZOA#uSbNu zyZ)=V)0T7E64miuwr|SrdMygL2{vvT)?CfA2%BuaLE`I0qmg-)8#DPWuv& z+1MW%>H87{djOp#>`O#0?HRdBntL?LePixg+jNNK(#u;VVFwHt?zFFH1FPDJXtpor zv=y@b_ttE=Ecn=87+&>4dAqO|0ysuG|NMZfxC~Er0j`EeX2T=N@UKyB#>nlNIUv8; zt^eCDS03e_nY;d4anomOtMqb37T^V0mj%e+fqZgO`nCPObKo9hTh?ravHOsHa5fBf z4wImQ5t^3}It>`}iNlg}N#EJvvkXgJk=TtJU%Ztxny&QsScZNXmw#h2`q8_c2n8oR zZr`|nDtz^am*-|?=cI{g_ssiJ;Hu9*_0t)t$I@-??m63Io2#DoqAqsOyolIdy>qZ& zWG$_UWNd4zQoK~$$vxi6Un*>69`0XgSbDFzxtYsvA6#f?1baRO?1MVg)aWw-rf_&n zNq`?&$ewK@ySUFkNHrfgDCEK2gKDl`dVSBueLV^gFqA0}3}26OPgm3kh6kS_G(;e} zG(Tg??QGww&VEN5ScVcrUlMP~*MPP9 z-n4f?UAE|q@yv>@MPEDUh?jouLYiK(BIE^rD%ZjpPesw6=(5_b!l!r=R;|qP5#6Bw zFx5-@2z07z3_5LernEu{X!JjAgHG=^L#KCWJ3gmt%75IB&*@D6(rS`n{bLZs!3;J` wQ{E~6_vfakpQkO+zjw^Pf-5<0tFJ#ZU6uP literal 0 HcmV?d00001 diff --git a/labs/SAW/src/null.c b/labs/SAW/src/null.c new file mode 100644 index 00000000..551d44fc --- /dev/null +++ b/labs/SAW/src/null.c @@ -0,0 +1,5 @@ + + +int isNull(int *x) { + return (x == (int*)0); +} diff --git a/labs/SAW/src/rcs.bc b/labs/SAW/src/rcs.bc new file mode 100644 index 0000000000000000000000000000000000000000..2fd7764b3ec1dc2549b31ad0f1d21c91276a2560 GIT binary patch literal 2240 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 literal 0 HcmV?d00001 From 7bcddc7f226ca2f6743ca678d51af5a7d267085c Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 15:43:02 -0400 Subject: [PATCH 059/120] Update GameOutline.md --- labs/SAW/Game/GameOutline.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md index bed72968..f591629e 100644 --- a/labs/SAW/Game/GameOutline.md +++ b/labs/SAW/Game/GameOutline.md @@ -163,7 +163,7 @@ void resolveAttack(character_t* target, uint32_t atk) - Spoiler alert: Logical error in the source code where the first condition already covers the second condition ## selfDamage(player_t* player); - +**Goal:** To provide a case study where SAW should have complained about its memory disjoint assertion being violated. Note that SAW's Python API silently resolves this issue. **Consider dropping from the SAW.md discussion**. Can still keep as a bonus example though! ## resetInventoryItems(inventory_t* inventory); @@ -183,6 +183,7 @@ Note: No Contract for this one. Considering dropping due to how complex the Cont # TODO - Add `selfDamage` prototype to `Game.h` +- Remove the first 3 preconditions in `selfDamage_Contract` given that they were used for debugging and are no longer needed - Move `quickBattle` to come right after `selfDamage`, which is after `resolveAttack` given that it makes sense in the lesson plan - Consider moving `resetInventoryItems`, `initScreen`, and `setScreenTile` earlier in the lesson plan - Recall that `setScreenTile` shows extern global variables @@ -190,5 +191,8 @@ Note: No Contract for this one. Considering dropping due to how complex the Cont - Necessary because some of these errors were encountered and resolved already. - Want to properly recreate them for learning purposes. - Determine if the resolveAttack logical error should be kept -- Determine if counterBattle should be kept - - Current thoughts are no given how many behavior states must be considered +- Determine if some functions should be dropped... + - Entirely: `counterBattle` + - It has so many SAW behaviors to consider (so fairly complicated), and I have not written a SAW contract for it + - Just from SAW.md: `selfDamage` + - Intended to be an example of field aliasing and show SAW complaining about "Memory not disjoint", but the Python API resolves it From 1c4ba76eb29acc1ce3ff7380180b40a8c5ee7cf1 Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:02:40 -0400 Subject: [PATCH 060/120] Update GameOutline.md --- labs/SAW/Game/GameOutline.md | 84 ++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md index f591629e..6b96d0d2 100644 --- a/labs/SAW/Game/GameOutline.md +++ b/labs/SAW/Game/GameOutline.md @@ -166,6 +166,88 @@ void resolveAttack(character_t* target, uint32_t atk) **Goal:** To provide a case study where SAW should have complained about its memory disjoint assertion being violated. Note that SAW's Python API silently resolves this issue. **Consider dropping from the SAW.md discussion**. Can still keep as a bonus example though! ## resetInventoryItems(inventory_t* inventory); +**Goal:** To show the problems SAW faces when verifying structs with pointer fields. + +**Lessons Learned:** +- + +**Errors to Explore:** +- Assuming the inventory_t struct is defined as: +```c +typedef struct { + item_t* item; + uint32_t numItems; +} inventory_t; +``` +understand why the following contract setups fail: + +Setup Attempt #1 +```python +class resetInventoryItems_Contract(Contract): + def __init__(self, numItems : int): + super().__init__() + self.numItems = numItems + + def specification (self): + # Declare variables + # Note: The setup here does not use item_p for the proof. However, item_p + # is included to show errors that can be encountered with the + # inventory_t struct. + (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) + + # Symbolically execute the function + self.execute_func(inventory_p) + + # Assert the postconditions + for i in range(self.numItems): + self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) + + self.returns(void) +``` + +```bash +... + ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): + error: types not memory-compatible: + { %struct.item_t*, i32 } + { [5 x { i32, i32 }], i32 } + stdout: +``` + +Setup Attempt #2 +```python +class resetInventoryItems_Contract(Contract): + def __init__(self, numItems : int): + super().__init__() + self.numItems = numItems + + def specification (self): + # Declare variables + # Note: The setup here does not use item_p for the proof. However, item_p + # is included to show errors that can be encountered with the + # inventory_t struct. + (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item_p, cry_f("{self.numItems} : [32]"))) + + # Symbolically execute the function + self.execute_func(inventory_p) + + # Assert the postconditions + for i in range(self.numItems): + self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) + + self.returns(void) +``` + +```bash + ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): + error: typeOfSetupValue: llvm_elem requires pointer to struct or array, found %struct.item_t** + stdout: + Considering both of these verification setup attempts, we can see that + defining inventory_t with an item_t pointer is tough for SAW to setup and. + prove. Consequently, it is better to use fixed array lengths for structs! +``` ## initScreen(screen_t* screen, uint8_t assetID); @@ -182,6 +264,8 @@ Note: No Contract for this one. Considering dropping due to how complex the Cont # TODO +- Define the background of the library (structs, functions, etc) +- Cryptol record/tuples ('(') for initDefaultPlayer - Add `selfDamage` prototype to `Game.h` - Remove the first 3 preconditions in `selfDamage_Contract` given that they were used for debugging and are no longer needed - Move `quickBattle` to come right after `selfDamage`, which is after `resolveAttack` given that it makes sense in the lesson plan From 50a2fc3853ce392184fbfff12c3c46c9a9837008 Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:22:33 -0400 Subject: [PATCH 061/120] Update GameOutline.md --- labs/SAW/Game/GameOutline.md | 66 ++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md index 6b96d0d2..add9e193 100644 --- a/labs/SAW/Game/GameOutline.md +++ b/labs/SAW/Game/GameOutline.md @@ -162,14 +162,16 @@ void resolveAttack(character_t* target, uint32_t atk) ``` - Spoiler alert: Logical error in the source code where the first condition already covers the second condition + ## selfDamage(player_t* player); **Goal:** To provide a case study where SAW should have complained about its memory disjoint assertion being violated. Note that SAW's Python API silently resolves this issue. **Consider dropping from the SAW.md discussion**. Can still keep as a bonus example though! + ## resetInventoryItems(inventory_t* inventory); **Goal:** To show the problems SAW faces when verifying structs with pointer fields. **Lessons Learned:** -- +- Consider rewriting the source code to avoid unallocated pointers as it makes SAW happy and reduces assumptions code makes (improves security) **Errors to Explore:** - Assuming the inventory_t struct is defined as: @@ -251,26 +253,84 @@ class resetInventoryItems_Contract(Contract): ## initScreen(screen_t* screen, uint8_t assetID); +**Goal:** To provide a case study where SAW should have complained about not knowing what nested defines resolve to. Note that SAW's Python API silently resolves this issue. **Consider dropping from the SAW.md discussion**. Can still keep as a bonus example though! ## setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx); +**Goal:** Demonstrate how to initialize an extern global array. + +**Lessons Learned:** +- How to handle extern global variables when they aren't linked into the generated bitcode + - Copy extern definition to Cryptol spec file + - Could be possible to ignore the initialization if the bitcode links the file that implements the extern global (testing required) +- Understand when `ptr_to_fresh` vs `self.alloc` is needed +- Repointing a pointer to a SAW variable (`screen_post`) in order to use that variable for postconditions + +**Errors to Explore:** +- Why can't we just declare and pass the screen pointer, `screen_p`? +```python +class setScreenTile_Contract(Contract): + def __init__(self, shouldPass : bool): + super().__init__() + self.shouldPass = shouldPass + + def specification (self): + # Declare variables + screen_p = self.alloc(alias_ty("struct.screen_t")) + screenIdx = self.fresh_var(i32, "screenIdx") + tableIdx = self.fresh_var(i32, "tableIdx") + + # Initialize Game.h's assetTable according to Assets.c + self.points_to(global_var("assetTable"), cry_f("assetTable")) + + # Assert preconditions depending on the Contract parameter + if (self.shouldPass): + self.precondition_f("{screenIdx} < {SCREEN_TILES}") + self.precondition_f("{tableIdx} < {ASSET_TABLE_SIZE}") + else: + # Note: Only one of the following preconditions is needed + self.precondition_f("{screenIdx} >= {SCREEN_TILES}") + self.precondition_f("{tableIdx} >= {ASSET_TABLE_SIZE}") + + # Symbolically execute the function + self.execute_func(screen_p, screenIdx, tableIdx) + + # Since we just want to check one index, let's have our screen pointer + # point to a new screen_t variable. + screen_post = self.fresh_var(alias_ty("struct.screen_t"), "screen_post") + + # Assert that the original screen pointer now points to the new screen_t + # variable. This will allow us to reference screen_post in Cryptol for our + # later postconditions. + self.points_to(screen_p, screen_post) + + # Assert postconditions depending on the Contract parameter + if (self.shouldPass): + self.postcondition_f("({screen_post}@{screenIdx}) == assetTable@{tableIdx}") + self.returns_f("`({SUCCESS}) : [32]") + else: + self.returns_f("`({FAILURE}) : [32]") +``` ## quickBattle(player_t* player, character_t* opponent); + ## counterBattle(player_t* player, character_t* opponent); Note: No Contract for this one. Considering dropping due to how complex the Contract needs to be (multiple possible outcomes depending on the inputs - requires specific preconditions and postconditions) # TODO +- Polish comments in all Game/ files + - Remove unused code when appropriate - Define the background of the library (structs, functions, etc) - Cryptol record/tuples ('(') for initDefaultPlayer - Add `selfDamage` prototype to `Game.h` - Remove the first 3 preconditions in `selfDamage_Contract` given that they were used for debugging and are no longer needed - Move `quickBattle` to come right after `selfDamage`, which is after `resolveAttack` given that it makes sense in the lesson plan - Consider moving `resetInventoryItems`, `initScreen`, and `setScreenTile` earlier in the lesson plan - - Recall that `setScreenTile` shows extern global variables + - Recall that `setScreenTile` shows extern global variables --> move after `getDefaultLevel` to continue the global variable discussion - Confirm the error cases yield the expected error results - Necessary because some of these errors were encountered and resolved already. - Want to properly recreate them for learning purposes. @@ -280,3 +340,5 @@ Note: No Contract for this one. Considering dropping due to how complex the Cont - It has so many SAW behaviors to consider (so fairly complicated), and I have not written a SAW contract for it - Just from SAW.md: `selfDamage` - Intended to be an example of field aliasing and show SAW complaining about "Memory not disjoint", but the Python API resolves it + - Just from SAW.md: `initScreen` + - Intended to be an example of where SAW cannot resolve nested defines, but the Python API resolves it From b37d86da0f796fdce0e9834e06855fec0a046bf6 Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Wed, 1 Jun 2022 16:37:31 -0400 Subject: [PATCH 062/120] Update GameOutline.md --- labs/SAW/Game/GameOutline.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md index add9e193..5213c9b9 100644 --- a/labs/SAW/Game/GameOutline.md +++ b/labs/SAW/Game/GameOutline.md @@ -314,7 +314,18 @@ class setScreenTile_Contract(Contract): ## quickBattle(player_t* player, character_t* opponent); +**Goal:** To show how to pass overrides (lemmas) to a Unit test. +**Lessons Learned:** +- Must pass preconditions that match the preconditions included in the passed overrides (tied to **Errors to Explore**) + +**Errors to Explore:** +- Explain why `ptr_to_fresh` must be used over `self.alloc` +- Explain why the proof fails when the `MAX_STAT` preconditions are commented out (refer to the first point in **Lessons Learned**) + +**DJ's Notes:** +- The function assumes that both the player and opponent have non-zero HP values, otherwise there wouldn't be a battle! + - Explains why the `> 0` preconditions exist ## counterBattle(player_t* player, character_t* opponent); From b0463d5636a49b849d80a72da5ccf7e77d17398f Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:35:02 -0400 Subject: [PATCH 063/120] Update SAW.md --- labs/SAW/SAW.md | 122 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 115 insertions(+), 7 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 3a7cd15e..5e4faf85 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1042,20 +1042,128 @@ This example is from [here]() # Structs -//Dj help +In this section we learn how to verify code involving structs and global variables by analyzing a game. The code for the game can be found -Consider the following struct: +``` +HERE. +``` + +### Struct Initialization + +The game defines the following player type: ```C typedef struct { - uint32_t *vector; - uint32_t size; -} + uint8_t name[MAX_NAME_LENGTH]; + uint32_t level; + uint32_t hp; + uint32_t atk; + uint32_t def; + uint32_t spd; +} character_t; + +typedef character_t player_t; +``` + +The following function will initialize a player. + +```C +uint32_t initDefaultPlayer(player_t* player) +{ + uint8_t i = 0; + uint32_t hp_default = 10; + uint32_t atk_default = 5; + uint32_t def_default = 4; + uint32_t spd_default = 3; + + for (i = 0; i < MAX_NAME_LENGTH; i++) + { + player->name[i] = 'A'; + } + player->level = 1; + player->hp = hp_default; + player->atk = atk_default; + player->def = def_default; + player->spd = spd_default; + + return SUCCESS; +} +``` + +where `SUCCESS` and `MAX_NAME_LENGTH` are C constants + +```C +#define MAX_NAME_LENGTH 12 +#define SUCCESS 170 +#define FAILURE 85 +``` + +## Struct Initialization and Explicit Structs + +The following constract will verify our initialization: + +```python +class initDefaultPlayer_Contract(Contract): + def specification (self): + player = self.alloc(alias_ty("struct.character_t")) + + self.execute_func(player) + + self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) + self.points_to(player['level'], cry_f("1 : [32]")) + self.points_to(player['hp'], cry_f("10 : [32]")) + self.points_to(player['atk'], cry_f("5 : [32]")) + self.points_to(player['def'], cry_f("4 : [32]")) + self.points_to(player['spd'], cry_f("3 : [32]")) + + self.returns(cry_f("`({SUCCESS}) : [32]")) ``` -### Struct Initialization and Explicit Structs +The command `alias_ty("struct.")` creates a type corresponding to the structure, so `player = self.alloc(alias_ty("struct.character_t"))` just creates a symbolic pointer variable `player` pointing to a structure of type `character_t`. + +Let's breakdown a `points_to` command: + + +If the bitcode is made using the debug flag `-g` then the field names of structs are present in the bitcode. This allows us to use strings instead of remembering the index of every field: + +```python +//place constants here + +class initDefaultPlayer_Contract(Contract): + def specification (self): + player = self.alloc(alias_ty("struct.character_t")) + + self.execute_func(player) + + self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) + self.points_to(player['level'], cry_f("1 : [32]")) + self.points_to(player['hp'], cry_f("10 : [32]")) + self.points_to(player['atk'], cry_f("5 : [32]")) + self.points_to(player['def'], cry_f("4 : [32]")) + self.points_to(player['spd'], cry_f("3 : [32]")) + + self.returns(cry_f("`({SUCCESS}) : [32]")) +``` + +Alternatively, we coud use post conditions + +``` +above with postcoditions +``` + +## Structs in Cryptol + +We can also explicitly define a structure: + +``` +example +``` + +//Cryptol interprets structs as tuples +//But what if a struct has a pointer as a field...? + +## Exercise: Resolving an Attack! -### Structs in Cryptol # Using Gained Knowledge From 33fff237bfe17e52d673c0fefce46d5fd1b42aa5 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 00:35:24 +0000 Subject: [PATCH 064/120] Updated initDefaultPlayer_Contract example --- labs/SAW/SAW.md | 59 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 5e4faf85..f854470b 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1042,11 +1042,8 @@ This example is from [here]() # Structs -In this section we learn how to verify code involving structs and global variables by analyzing a game. The code for the game can be found +In this section we learn how to verify code involving structs and global variables by analyzing a game. The code for the game can be found [here](./Game/src/). -``` -HERE. -``` ### Struct Initialization @@ -1119,12 +1116,46 @@ class initDefaultPlayer_Contract(Contract): self.returns(cry_f("`({SUCCESS}) : [32]")) ``` -The command `alias_ty("struct.")` creates a type corresponding to the structure, so `player = self.alloc(alias_ty("struct.character_t"))` just creates a symbolic pointer variable `player` pointing to a structure of type `character_t`. +The command `alias_ty("struct.")` creates a type corresponding to the structure, so `player = self.alloc(alias_ty("struct.character_t"))` just creates a symbolic pointer variable `player` pointing to a structure of type `character_t`. Even though the function's input parameter is `player_t`, we need to pass `character_t` to `alias_ty` given that `player_t` is just a typedef for `character_t`. -Let's breakdown a `points_to` command: +Let's breakdown a `points_to` command seen above: +```python +self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) +``` -If the bitcode is made using the debug flag `-g` then the field names of structs are present in the bitcode. This allows us to use strings instead of remembering the index of every field: +Here, we assert that the `name` field of the player pointer points to a value specified a Cryptol expression. Notice that we use the string associated with the struct field, `name`, to access our target struct index. We are able to use strings instead of remembering the index of every field when debug symbols are included in the generated bitcode. For the full compilation details, check out the [Makefile](./Game/Makefile) associated with the `Game` directory. However, the `-g` clang flag is what tells the compiler to include the field names of the structs in the bitcode. + +If we didn't have the debug symbols in the bitcode, SAW would throw us an error like so: + +``` +clang -c -emit-llvm -o artifacts/Game.bc src/Game.c +python3 proof/Game.py +⚠️ Failed to verify: lemma_initDefaultPlayer_Contract (defined at proof/Game.py:513): +error: Unable to resolve struct field name: '"name"' +Could not resolve setup value debug information into a struct type. +Perhaps you need to compile with debug symbols enabled. + + stdout: + +F +====================================================================== +FAIL: test_Game (__main__.GameTests) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "proof/Game.py", line 514, in test_Game + self.assertIs(initDefaultPlayer_result.is_success(), True) +AssertionError: False is not True + +---------------------------------------------------------------------- +Ran 1 test in 0.865s + +FAILED (failures=1) +🛑 The goal failed to verify. +make: *** [Makefile:14: all] Error 1 +``` + +And we would need to use the appropriate indices to represent each field: ```python //place constants here @@ -1135,17 +1166,17 @@ class initDefaultPlayer_Contract(Contract): self.execute_func(player) - self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) - self.points_to(player['level'], cry_f("1 : [32]")) - self.points_to(player['hp'], cry_f("10 : [32]")) - self.points_to(player['atk'], cry_f("5 : [32]")) - self.points_to(player['def'], cry_f("4 : [32]")) - self.points_to(player['spd'], cry_f("3 : [32]")) + self.points_to(player[0], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) + self.points_to(player[1], cry_f("1 : [32]")) + self.points_to(player[2], cry_f("10 : [32]")) + self.points_to(player[3], cry_f("5 : [32]")) + self.points_to(player[4], cry_f("4 : [32]")) + self.points_to(player[5], cry_f("3 : [32]")) self.returns(cry_f("`({SUCCESS}) : [32]")) ``` -Alternatively, we coud use post conditions +Alternatively, we could use post conditions ``` above with postcoditions From c2f95893d707333026d09758e51c556b6cfe5e40 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 00:48:00 +0000 Subject: [PATCH 065/120] Updated initDefaultPlayer_Contract example --- labs/SAW/SAW.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index f854470b..b9f33f01 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1155,11 +1155,9 @@ FAILED (failures=1) make: *** [Makefile:14: all] Error 1 ``` -And we would need to use the appropriate indices to represent each field: +As we already mentioned, adding the `-g` flag will resolve the error. However, what if we didn't want to include debug symbols in the bitcode, but still wanted to verify our contract? Well, we can reference the struct fields by using their corresponding indices like so: ```python -//place constants here - class initDefaultPlayer_Contract(Contract): def specification (self): player = self.alloc(alias_ty("struct.character_t")) @@ -1195,6 +1193,8 @@ example ## Exercise: Resolving an Attack! +Feeling pretty confident with our little `player_t` and `character_t` structs? How about we go for a full on attack then? Well, an attack in our game of course between two characters ;) + # Using Gained Knowledge From f4dc11a8bb5f63241cc3b8f3727b22720a585ffa Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 01:19:26 +0000 Subject: [PATCH 066/120] Modified function placemen to make their lesson order flow better --- labs/SAW/Game/GameOutline.md | 5 - labs/SAW/Game/proof/Game.py | 349 ++++++++++++++++++----------------- labs/SAW/Game/src/Game.c | 113 ++++-------- labs/SAW/Game/src/Game.h | 50 +++-- 4 files changed, 258 insertions(+), 259 deletions(-) diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/GameOutline.md index 5213c9b9..68239ad1 100644 --- a/labs/SAW/Game/GameOutline.md +++ b/labs/SAW/Game/GameOutline.md @@ -337,11 +337,6 @@ Note: No Contract for this one. Considering dropping due to how complex the Cont - Remove unused code when appropriate - Define the background of the library (structs, functions, etc) - Cryptol record/tuples ('(') for initDefaultPlayer -- Add `selfDamage` prototype to `Game.h` -- Remove the first 3 preconditions in `selfDamage_Contract` given that they were used for debugging and are no longer needed -- Move `quickBattle` to come right after `selfDamage`, which is after `resolveAttack` given that it makes sense in the lesson plan -- Consider moving `resetInventoryItems`, `initScreen`, and `setScreenTile` earlier in the lesson plan - - Recall that `setScreenTile` shows extern global variables --> move after `getDefaultLevel` to continue the global variable discussion - Confirm the error cases yield the expected error results - Necessary because some of these errors were encountered and resolved already. - Want to properly recreate them for learning purposes. diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 41f07c04..f8938e72 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -78,21 +78,6 @@ def specification (self): self.returns_f("levelUp {level}") -# getDefaultLevel Contract -# uint32_t getDefaultLevel() -class getDefaultLevel_Contract(Contract): - def specification (self): - # Initialize the defaultLevel global variable - defaultLevel_init = global_initializer("defaultLevel") - self.points_to(global_var("defaultLevel"), defaultLevel_init) - - # Symbolically execute the function - self.execute_func() - - # Assert the function's return behavior - self.returns(defaultLevel_init) - - # initDefaultPlayer Contract # uint32_t initDefaultPlayer(player_t* player) class initDefaultPlayer_Contract(Contract): @@ -245,77 +230,121 @@ def specification (self): self.returns(void) -# resetInventoryItems Contract -# void resetInventoryItems(inventory_t* inventory) -class resetInventoryItems_Contract(Contract): - def __init__(self, numItems : int): +# selfDamage Contract +# uint32_t selfDamage(player_t* player) +class selfDamage_Contract(Contract): + def __init__(self, case : int): super().__init__() - self.numItems = numItems - # Note: The inventory_t struct defines its item field as a item_t pointer. - # Instead of declaring a fixed array size, the pointer enables for a - # variable size depending on the memory allocation configured by its - # caller. - # While this is valid C syntax, SAW and Cryptol require the known - # size ahead of time. Here, our contract takes the numItems parameter - # to declare a fixed array size. + self.case = case + # There are 3 possible cases for resolveAttack + # Case 1: Attack mitigated + # Case 2: Immediate KO + # Case 3: Regular attack + # Each case results in a different function behavior for calculating the + # player's remaining HP. While we could make 3 separate contracts to handle + # all of the possible cases, we can pass a parameter to the contract, which + # identifies what preconditions and postconditions to set. def specification (self): # Declare variables - # Note: The setup here does not use item_p for the proof. However, item_p - # is included to show errors that can be encountered with the - # inventory_t struct. - (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") - inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) - """ - If inventory_t is defined as: - typedef struct { - item_t* item; - uint32_t numItems; - } inventory_t; + (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") - Attempt 1: - ========== - Passing item as so: - inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) + # Assert the precondition that the player's HP is positive + # Why? Game logic assumes you can't damage yourself if you're already KO'd! + self.precondition_f("{player}.2 > 0") - yields the following error: - ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): - error: types not memory-compatible: - { %struct.item_t*, i32 } - { [5 x { i32, i32 }], i32 } + # Assert the precondition that character stats are below the max stat cap + # Pop Quiz: Explain why the proof fails when the following preconditions + # are commented out. + self.precondition_f("{player}.2 <= `{MAX_STAT}") + self.precondition_f("{player}.3 <= `{MAX_STAT}") + self.precondition_f("{player}.4 <= `{MAX_STAT}") + self.precondition_f("{player}.5 <= `{MAX_STAT}") + # Determine the preconditions based on the case parameter + if (self.case == 1): + # player->def >= player->atk + self.precondition_f("{player}.4 >= {player}.3") + elif (self.case == 2): + # player->hp <= (player->atk - player->def) + self.precondition_f("({player}.2 + {player}.4) <= {player}.3") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("{player}.4 < {player}.3") + self.precondition_f("({player}.2 + {player}.4) > {player}.3") - stdout: + # Symbolically execute the function + self.execute_func(player_p, player_p) - Attempt 2: - ========== - Passing item_p as so: - inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item_p, cry_f("{self.numItems} : [32]"))) - - yields the following error: - ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): - error: typeOfSetupValue: llvm_elem requires pointer to struct or array, found %struct.item_t** - stdout: + # Assert the postcondition + if (self.case == 1): + self.points_to(player_p['hp'], cry_f("{player}.2 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("{player}.2 : [32]")) + self.returns(cry_f("`({NEUTRAL}) : [32]")) + elif (self.case == 2): + self.points_to(player_p['hp'], cry_f("0 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("0 : [32]")) + self.returns(cry_f("`({DEFEAT_PLAYER}) : [32]")) + else: + self.points_to(player_p['hp'], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) + self.returns(cry_f("`({NEUTRAL}) : [32]")) - Considering both of these verification setup attempts, we can see that - defining inventory_t with an item_t pointer is tough for SAW to setup and. - prove. Consequently, it is better to use fixed array lengths for structs! - """ - # Symbolically execute the function - self.execute_func(inventory_p) +# quickBattle Contract +# void quickBattle(player_t* player, character_t* opponent) +class quickBattle_Contract(Contract): + def specification (self): + # Declare variables + (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") + (opponent, opponent_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="opponent") + # Pop Quiz: Why does allocating the pointers in the following way yield an + # error? + #player = self.alloc(alias_ty("struct.character_t")) + #opponent = self.alloc(alias_ty("struct.character_t")) - # Assert the postconditions - for i in range(self.numItems): - self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(inventory_p[0][i][1], cry_f("0 : [32]")) - # Note: Even though debug symbols is enabled, SAW cannot resolve the - # "quantity" field, which is why we still use a 1 above. + # Assert the precondition that both HPs are greater than 0 + # Why? Game logic assumes you can't attack if you're already KO'd! + self.precondition_f("{player}.2 > 0") + self.precondition_f("{opponent}.2 > 0") + + # Assert the precondition that character stats are below the max stat cap + # Pop Quiz: Explain why the proof fails when the following preconditions + # are commented out. + self.precondition_f("{player}.2 <= `{MAX_STAT}") + self.precondition_f("{player}.3 <= `{MAX_STAT}") + self.precondition_f("{player}.4 <= `{MAX_STAT}") + self.precondition_f("{player}.5 <= `{MAX_STAT}") + self.precondition_f("{opponent}.2 <= `{MAX_STAT}") + self.precondition_f("{opponent}.3 <= `{MAX_STAT}") + self.precondition_f("{opponent}.4 <= `{MAX_STAT}") + self.precondition_f("{opponent}.5 <= `{MAX_STAT}") + # Symbolically execute the function + self.execute_func(player_p, opponent_p) + + # Assert the postcondition self.returns(void) +# getDefaultLevel Contract +# uint32_t getDefaultLevel() +class getDefaultLevel_Contract(Contract): + def specification (self): + # Initialize the defaultLevel global variable + defaultLevel_init = global_initializer("defaultLevel") + self.points_to(global_var("defaultLevel"), defaultLevel_init) + + # Symbolically execute the function + self.execute_func() + + # Assert the function's return behavior + self.returns(defaultLevel_init) + + # initScreen Contract # uint32_t initScreen(screen_t* screen, uint8_t assetID) class initScreen_Contract(Contract): @@ -392,104 +421,76 @@ def specification (self): self.returns_f("`({FAILURE}) : [32]") -# quickBattle Contract -# void quickBattle(player_t* player, character_t* opponent) -class quickBattle_Contract(Contract): +# resetInventoryItems Contract +# void resetInventoryItems(inventory_t* inventory) +class resetInventoryItems_Contract(Contract): + def __init__(self, numItems : int): + super().__init__() + self.numItems = numItems + # Note: The inventory_t struct defines its item field as a item_t pointer. + # Instead of declaring a fixed array size, the pointer enables for a + # variable size depending on the memory allocation configured by its + # caller. + # While this is valid C syntax, SAW and Cryptol require the known + # size ahead of time. Here, our contract takes the numItems parameter + # to declare a fixed array size. + def specification (self): # Declare variables - (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") - (opponent, opponent_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="opponent") - # Pop Quiz: Why does allocating the pointers in the following way yield an - # error? - #player = self.alloc(alias_ty("struct.character_t")) - #opponent = self.alloc(alias_ty("struct.character_t")) - - # Assert the precondition that both HPs are greater than 0 - self.precondition_f("{player}.2 > 0") - self.precondition_f("{opponent}.2 > 0") - - # Assert the precondition that character stats are below the max stat cap - # Pop Quiz: Explain why the proof fails when the following preconditions - # are commented out. - self.precondition_f("{player}.2 <= `{MAX_STAT}") - self.precondition_f("{player}.3 <= `{MAX_STAT}") - self.precondition_f("{player}.4 <= `{MAX_STAT}") - self.precondition_f("{player}.5 <= `{MAX_STAT}") - self.precondition_f("{opponent}.2 <= `{MAX_STAT}") - self.precondition_f("{opponent}.3 <= `{MAX_STAT}") - self.precondition_f("{opponent}.4 <= `{MAX_STAT}") - self.precondition_f("{opponent}.5 <= `{MAX_STAT}") - - # Symbolically execute the function - self.execute_func(player_p, opponent_p) - - # Assert the postcondition - self.returns(void) + # Note: The setup here does not use item_p for the proof. However, item_p + # is included to show errors that can be encountered with the + # inventory_t struct. + (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) + """ + If inventory_t is defined as: + typedef struct { + item_t* item; + uint32_t numItems; + } inventory_t; + Attempt 1: + ========== + Passing item as so: + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) -# selfDamage Contract -# uint32_t selfDamage(player_t* player) -class selfDamage_Contract(Contract): - def __init__(self, case : int): - super().__init__() - self.case = case - # There are 3 possible cases for resolveAttack - # Case 1: Attack mitigated - # Case 2: Immediate KO - # Case 3: Regular attack - # Each case results in a different function behavior for calculating the - # player's remaining HP. While we could make 3 separate contracts to handle - # all of the possible cases, we can pass a parameter to the contract, which - # identifies what preconditions and postconditions to set. + yields the following error: + ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): + error: types not memory-compatible: + { %struct.item_t*, i32 } + { [5 x { i32, i32 }], i32 } - def specification (self): - # Declare variables - (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") - # Assert the preconditions - self.precondition_f("{player}.2 > 5") - self.precondition_f("{player}.3 == 5") - self.precondition_f("{player}.4 == 0") + stdout: - # Assert the precondition that character stats are below the max stat cap - # Pop Quiz: Explain why the proof fails when the following preconditions - # are commented out. - self.precondition_f("{player}.2 <= `{MAX_STAT}") - self.precondition_f("{player}.3 <= `{MAX_STAT}") - self.precondition_f("{player}.4 <= `{MAX_STAT}") - self.precondition_f("{player}.5 <= `{MAX_STAT}") + Attempt 2: + ========== + Passing item_p as so: + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item_p, cry_f("{self.numItems} : [32]"))) + + yields the following error: + ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): + error: typeOfSetupValue: llvm_elem requires pointer to struct or array, found %struct.item_t** + stdout: - # Determine the preconditions based on the case parameter - if (self.case == 1): - # player->def >= player->atk - self.precondition_f("{player}.4 >= {player}.3") - elif (self.case == 2): - # player->hp <= (player->atk - player->def) - self.precondition_f("({player}.2 + {player}.4) <= {player}.3") - else: - # Assume any other case follows the formal attack calculation - self.precondition_f("{player}.4 < {player}.3") - self.precondition_f("({player}.2 + {player}.4) > {player}.3") + Considering both of these verification setup attempts, we can see that + defining inventory_t with an item_t pointer is tough for SAW to setup and. + prove. Consequently, it is better to use fixed array lengths for structs! + """ # Symbolically execute the function - self.execute_func(player_p, player_p) + self.execute_func(inventory_p) - # Assert the postcondition - if (self.case == 1): - self.points_to(player_p['hp'], cry_f("{player}.2 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player_p[2], cry_f("{player}.2 : [32]")) - self.returns(cry_f("`({NEUTRAL}) : [32]")) - elif (self.case == 2): - self.points_to(player_p['hp'], cry_f("0 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player_p[2], cry_f("0 : [32]")) - self.returns(cry_f("`({DEFEAT_PLAYER}) : [32]")) - else: - self.points_to(player_p['hp'], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) + # Assert the postconditions + for i in range(self.numItems): + self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player_p[2], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) - self.returns(cry_f("`({NEUTRAL}) : [32]")) + # self.points_to(inventory_p[0][i][1], cry_f("0 : [32]")) + # Note: Even though debug symbols is enabled, SAW cannot resolve the + # "quantity" field, which is why we still use a 1 above. + + self.returns(void) + ####################################### @@ -510,39 +511,51 @@ def test_Game(self): cryptol_load_file(cryptol_name) module = llvm_load_module(bitcode_name) + # Override(s) associated with basic SAW setup levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) - getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) + + # Override(s) associated with basic struct initialization initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) + + # Overrides(s) associated with preconditions and postconditions that must + # be considered in SAW contracts & unit test overrides checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) - resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) - initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) - setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) - setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) - quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + + # Override(s) associated with global variable handling + getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) + initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) + setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) + setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) + # Override(s) showing limitations with struct pointer fields + resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) + + # Assert the overrides are successful self.assertIs(levelUp_result.is_success(), True) - self.assertIs(getDefaultLevel_result.is_success(), True) self.assertIs(initDefaultPlayer_result.is_success(), True) self.assertIs(checkStats_pass_result.is_success(), True) self.assertIs(checkStats_fail_result.is_success(), True) self.assertIs(resolveAttack_case1_result.is_success(), True) self.assertIs(resolveAttack_case2_result.is_success(), True) self.assertIs(resolveAttack_case3_result.is_success(), True) - self.assertIs(resetInventoryItems_result.is_success(), True) - self.assertIs(initScreen_result.is_success(), True) - self.assertIs(setScreenTile_pass_result.is_success(), True) - self.assertIs(setScreenTile_fail_result.is_success(), True) - self.assertIs(quickBattle_result.is_success(), True) self.assertIs(selfDamage_case1_result.is_success(), True) self.assertIs(selfDamage_case2_result.is_success(), True) self.assertIs(selfDamage_case3_result.is_success(), True) + self.assertIs(quickBattle_result.is_success(), True) + self.assertIs(getDefaultLevel_result.is_success(), True) + self.assertIs(initScreen_result.is_success(), True) + self.assertIs(setScreenTile_pass_result.is_success(), True) + self.assertIs(setScreenTile_fail_result.is_success(), True) + self.assertIs(resetInventoryItems_result.is_success(), True) + if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index 11f04664..1ebe140f 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -1,17 +1,19 @@ #include "Game.h" +/////////////////////////////////////// +// Function(s) with basic SAW setup +/////////////////////////////////////// + uint32_t levelUp(uint32_t level) { return (level + 1); } -uint32_t getDefaultLevel() -{ - return defaultLevel; -} - +/////////////////////////////////////// +// Function(s) with basic struct initialization +/////////////////////////////////////// uint32_t initDefaultPlayer(player_t* player) { @@ -40,6 +42,11 @@ uint32_t initDefaultPlayer(player_t* player) } +/////////////////////////////////////// +// Function(s) with preconditions and postconditions that must +// be considered in SAW contracts & unit test overrides +/////////////////////////////////////// + // Checks that the character stats are below MAX_STAT // Note: MUST be called before running any function that uses character stats! uint32_t checkStats(character_t* character) @@ -79,6 +86,7 @@ void resolveAttack(character_t* target, uint32_t atk) } } + uint32_t selfDamage(player_t* player) { enum battleResult result = NEUTRAL; @@ -106,13 +114,27 @@ uint32_t selfDamage(player_t* player) } -void resetInventoryItems(inventory_t* inventory) +// Simulates a simple battle where only the faster character attacks. +void quickBattle(player_t* player, character_t* opponent) { - // Iterate through the inventory and set the quantity field to 0 - for (int i = 0; i < inventory->numItems; i++) + if (player->spd >= opponent->spd) { - inventory->item[i].quantity = 0; + resolveAttack(opponent, player->atk); } + else + { + resolveAttack(player, opponent->atk); + } +} + + +/////////////////////////////////////// +// Function(s) with global variable handling +/////////////////////////////////////// + +uint32_t getDefaultLevel() +{ + return defaultLevel; } @@ -146,73 +168,16 @@ uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx) } -// Simulates a simple battle where only the faster character attacks. -void quickBattle(player_t* player, character_t* opponent) -{ - if (player->spd >= opponent->spd) - { - resolveAttack(opponent, player->atk); - } - else - { - resolveAttack(player, opponent->atk); - } -} +/////////////////////////////////////// +// Function(s) showing limitations with struct pointer fields +/////////////////////////////////////// -// Simulates a complex battle where characters can counterattack as long as -// they survive to do so. -uint32_t counterBattle(player_t* player, character_t* opponent) +void resetInventoryItems(inventory_t* inventory) { - enum battleResult result = NEUTRAL; - - // Check spd to determine who goes first - if (player->spd >= opponent->spd) - { - // Player attacks first - resolveAttack(opponent, player->atk); - - // Check if there is a counterattack - if (opponent->hp > 0) - { - // Opponent counterattacks - resolveAttack(player, opponent->atk); - - if (player->hp == 0) - { - result = DEFEAT_PLAYER; - } - } - else - { - // Opponent defeated - player->level = player->level + 1; - result = DEFEAT_OPPONENT; - } - } - else + // Iterate through the inventory and set the quantity field to 0 + for (int i = 0; i < inventory->numItems; i++) { - // Opponent attacks first - resolveAttack(player, opponent->atk); - - // Check if there is a counterattack - if (player->hp > 0) - { - // Player counterattacks - resolveAttack(opponent, player->atk); - - if (opponent->hp == 0) - { - result = DEFEAT_OPPONENT; - player->level = player->level + 1; - } - } - else - { - // Player defeated - result = DEFEAT_PLAYER; - } + inventory->item[i].quantity = 0; } - - return result; -} \ No newline at end of file +} diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index 333d17ed..50fe595e 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -12,11 +12,30 @@ #define SCREEN_TILES SCREEN_ROWS*SCREEN_COLS #define ASSET_TABLE_SIZE 16 +/////////////////////////////////////// +// Globals +/////////////////////////////////////// const uint32_t defaultLevel = 1; - extern const uint8_t assetTable[ASSET_TABLE_SIZE]; + +/////////////////////////////////////// +// Enums +/////////////////////////////////////// + +// Enum containing possible battle outcomes +enum battleResult{ + NEUTRAL = 0, + DEFEAT_PLAYER = 1, + DEFEAT_OPPONENT = 2 +}; + + +/////////////////////////////////////// +// Structs +/////////////////////////////////////// + // Struct containing character information typedef struct { uint8_t name[MAX_NAME_LENGTH]; @@ -29,13 +48,6 @@ typedef struct { typedef character_t player_t; -// Enum containing possible battle outcomes -enum battleResult{ - NEUTRAL = 0, - DEFEAT_PLAYER = 1, - DEFEAT_OPPONENT = 2 -}; - // Struct containing item information typedef struct { uint32_t id; @@ -54,17 +66,31 @@ typedef struct { uint8_t tiles[SCREEN_TILES]; // Holds asset ID for each screen tile } screen_t; + +/////////////////////////////////////// // Function prototypes +/////////////////////////////////////// + +// Function(s) with basic SAW setup uint32_t levelUp(uint32_t level); -uint32_t getDefaultLevel(); + +// Function(s) with basic struct initialization uint32_t initDefaultPlayer(player_t* player); + +// Function(s) with preconditions and postconditions that must +// be considered in SAW contracts & unit test overrides uint32_t checkStats(character_t* character); void resolveAttack(character_t* target, uint32_t atk); -void resetInventoryItems(inventory_t* inventory); +uint32_t selfDamage(player_t* player); +void quickBattle(player_t* player, character_t* opponent); + +// Function(s) with global variable handling +uint32_t getDefaultLevel(); uint32_t initScreen(screen_t* screen, uint8_t assetID); uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx); -void quickBattle(player_t* player, character_t* opponent); -uint32_t counterBattle(player_t* player, character_t* opponent); + +// Function(s) showing limitations with struct pointer fields +void resetInventoryItems(inventory_t* inventory); #endif \ No newline at end of file From dd9e2976b10da0e632746fa04ca912271cea45a1 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Wed, 1 Jun 2022 21:48:26 -0400 Subject: [PATCH 067/120] Removing generated files and adding some to .gitignore --- .gitignore | 2 ++ labs/SAW/proof/spec/addRow.cry~ | 9 --------- labs/SAW/src/rcs.bc | Bin 2240 -> 0 bytes labs/SAW/src/rcs3.bc | Bin 2304 -> 0 bytes 4 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 labs/SAW/proof/spec/addRow.cry~ delete mode 100644 labs/SAW/src/rcs.bc delete mode 100644 labs/SAW/src/rcs3.bc diff --git a/.gitignore b/.gitignore index 79b5594d..09a0cf6b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ **/.DS_Store +**/*.bc +**/*~ diff --git a/labs/SAW/proof/spec/addRow.cry~ b/labs/SAW/proof/spec/addRow.cry~ deleted file mode 100644 index f3b3d061..00000000 --- a/labs/SAW/proof/spec/addRow.cry~ +++ /dev/null @@ -1,9 +0,0 @@ -module addRow where - - -addRow5 : [5][32] -> [5][32] -> [5][32] -addRow5 a b = a + b - -addRow : {length} (fin length) => [n][32] -> [n][32] -> [n][32] -addRow a b = a + b - diff --git a/labs/SAW/src/rcs.bc b/labs/SAW/src/rcs.bc deleted file mode 100644 index 2fd7764b3ec1dc2549b31ad0f1d21c91276a2560..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2240 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 From 8d4efca772478c704d451cc8549f7d618d240df2 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Wed, 1 Jun 2022 21:48:46 -0400 Subject: [PATCH 068/120] removing generated file --- labs/SAW/src/addRow.bc | Bin 3372 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 labs/SAW/src/addRow.bc diff --git a/labs/SAW/src/addRow.bc b/labs/SAW/src/addRow.bc deleted file mode 100644 index d67eafae9b7ced0e527adc4010ca57334200752f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3372 zcmaJ@e@q+K9e?JG??TMok;K{Ljz%8jxLu?XP?C}}CB4V|K`()N9( zB$~ADN$=jhci;DY-_Pg0@8?~Mn4UXWjZis4=%iZMb?o>D-F^R>II*=|YE!_NUX9Rt z8A4mj^avXCC{!|Bf6+0eJgQ=imP?AL`tfqAMyLGsNvZnjvX)C*TgF&pwVBd9Q(=5= z#Oa;X*SBtyW31y+S?3h>PBkwc8R7q;Fg_R6>@n<#H=ZAV@go1>6!?9WM`#efO(#`3$x5K(=hfcyahzc0wa|zibk>?EZ0Jt*9CV>q0$X@>8 z^f$FPGul~6J6*&MXVbcA54T*@E~F|~_1dYhuAq<^t} zwR5S;TPfYFLN_bvvJP%0#VsiW2o)A3OVf!ViX8fX)0gzxk22cnFgI)80TAXqy4f^0 zpQ@bIbIS@X@>76X_91F0D4UWb&=Qp7L9guJ3`Wu|q;%5?ekrWWdbm$gmGj^P*w}ux%BOe@lal$5=3*>502J&^%P4xu##vy!Y_k!UmwD0CmCxaCx3J_XF^ zK6ijCopew`o=+2|4#J<7&&5eoMPvLJk4#8Zvl= zoad$iphI`l!=X2Iw>`Qn!~_0;w4@f6C_H5BN@=qa@{je?l(3HHY-ow4R|IAeu}eHtEYOPu)!G_;cb;B% z(cd6!L)gbfOFn0tP}rtKOANcROoT0I(HbQz52vrL(S31x)lJ_ksFo1ZSD=e_W*H?! zGpsU7*stZR6JcvKV$F+n3Boqvfa%Ri!c7;`^gS1iiu6|n)x9|V**v{yr~8u3BF}s! zFnw-%t)TjnrPn1|vOqkrI*MAa1CWR;d7uyb3u@>?9h= zsNUQVgg8118leQO*gotY=GEJyQi880aDO+PN9+A^YcgVz#ld0=cJ! z^zIhQOJH!5O4@p>VurwRU!2NP? zJ0!^`D0x65`#?St;k}S)tMl}?X~vweXzX$bczD8I^BbI!}b=Z9X8-h5U@{pHA=p2vGlW@j-IwvoVsB5

g^5mHT0f2<8A0W>mT&`&b;jF^7~=EdP>na%vLw4 zTU~jUYEnjG%H4)~lj(ia<@XM~d-y%$V@>akU;ShI@Xkj{g9qL<9{N*zSt*6yg1mqv zL|ZF-1~_kIsKc88j5*j@OCdMB#N9|$E<12K02RxneSGx59XI`%3rpkzJ|rcf9GAi* z{6OH-8@{_Jc<|lm_tb(sMOQMjH)2<|QZ22Tnq7AtnzD*4|1^6nJH;Pmve>3v1(QVqT~!hmkJ}rD z`HFHpzsIh||A{|WJ=+3Jsd#vVAdLTtZ?xi-EM*f3CSKKvy0?_oW;Oj3dmz!pj!3LyimH$7YNkZHLo-RWJo5m9 zxgNcyOc`S8OY6Kk^F3sNeMF_&K>?l@FdoHA!@VqvdEda%7{{FA;^7h61MMcym|x)e zhcOH&TMZ8|gi9DWrO<1qjv!dg(TGD*c7J3#hWYM!7R7vc?BAs}2qqwjEI& z4Y3!bVWB~A&q$hKMdEP{RY4Gy$_tg$6B_CGu(ZcvqDB-O@&4oi?*kAozT1p6yEBTu zR%+tMCE~AG=@IIJyPy%;ZNz90Fh40e6r`El3K3m#ver?>aasf{5=?`e;JLo0%2(+Sgoecn_1dQbPC Y4eoon|7=6Ue!uV3{teTHQ`jx~4>(&j0{{R3 From 07fd329c600e512654a50efc250f2c107f15c1a0 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 01:53:22 +0000 Subject: [PATCH 069/120] Added initial documentation for the resolveAttack example --- labs/SAW/SAW.md | 114 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index b9f33f01..7d505ada 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1193,7 +1193,119 @@ example ## Exercise: Resolving an Attack! -Feeling pretty confident with our little `player_t` and `character_t` structs? How about we go for a full on attack then? Well, an attack in our game of course between two characters ;) +Feeling pretty confident with our little `player_t` and `character_t` structs? How about we go for a full on attack then? Well, in our game of course ;) + +Consider the following function: + +```c +void resolveAttack(character_t* target, uint32_t atk) +{ + if ( target->def >= atk) + { + // The target's defense mitigates the attack + target->hp = target->hp; + } + else if ( target->hp <= (atk - target->def) ) + { + // The attack will knock out the target + target->hp = 0; + } + else + { + // Calculate damage as normal + target->hp = target->hp - (atk - target->def); + } +} +``` + +Our function, `resolveAttack`, takes two inputs: +- `character_t* target`: Our defending character +- `uint32_t atk`: The attack stat of our attacker + +Think about how you would set up a SAW contract for this function. Go ahead and try it you would like! We'll wait for you :) + +Got an idea, great! Let's go over the steps we need to go through... + +First, let's set up our basic format for the contract: + +```python +class resolveAttack_Contract(Contract): + def specification (self): + # Declare variables + (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") + atk = self.fresh_var(i32, "atk") + + # Assert preconditions + + # Sybolically execute the function + self.execute_func(target_p, atk) + + # Assert postconditions + + self.returns(void) +``` + +Alright, we got our basic setup! But wait a second, there are 3 possible assignments for the target's hp... Each of these behaviors are determined by the stat ranges for the defender and attacker. How are we going to represent these different behaviors? + +First, let's consider the 3 possible cases for `resolveAttack`: +- Case 1: Attack mitigated + - Precondition: `target->def >= atk` + - Postcondition: `target->hp = target->hp` +- Case 2: Immediate KO + - Precondition: `target->hp <= (atk - target-> def)` + - Postcondition: `target->hp = 0` +- Case 3: Regular attack calculation + - Precondition: Anything other than Cases 1 and 2 + - Postcondition: `target->hp = target->hp - (atk - target->def)` + +With those 3 cases laid out, we now have a battle plan to represent the preconditions and postconditions needed for our proof. Now one option to account for all of these cases is to create 3 different contracts where each one specifies the appropriate precondition and postcondition pair for our case. Do we really want to copy and paste the bulk of our proof across 3 contracts though? Probably not. How about we bring our knowledge of parameterized contracts to battle! Tactics! + +```python +class resolveAttack_Contract(Contract): + def __init__(self, case : int): + super().__init__() + self.case = case + # There are 3 possible cases for resolveAttack + # Case 1: Attack mitigated + # Case 2: Immediate KO + # Case 3: Regular attack + + def specification (self): + # Declare variables + (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") + atk = self.fresh_var(i32, "atk") + + # Assert preconditions + # Determine the preconditions based on the case parameter + if (self.case == 1): + # target->def >= atk + self.precondition_f("{target}.4 >= {atk}") + elif (self.case == 2): + # target->hp <= (atk - target->def) + self.precondition_f("({target}.2 + {target}.4) <= {atk}") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("{target}.4 < {atk}") + self.precondition_f("({target}.2 + {target}.4) > {atk}") + + # Symbolically execute the function + self.execute_func(target_p, atk) + + # Assert postconditions + # Determine the postcondition based on the case parameter + if (self.case == 1): + self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) + elif (self.case == 2): + self.points_to(target_p['hp'], cry_f("0 : [32]")) + else: + self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + + self.returns(void) +``` + +Our contract is looking pretty good now! Let's go ahead and test it out with the following unit test: + + # Using Gained Knowledge From 530857f32c4fba618027f491969eab16cf63de37 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Wed, 1 Jun 2022 22:34:03 -0400 Subject: [PATCH 070/120] Added an introduction and polished the rotate bits --- labs/SAW/SAW.md | 111 ++++++++++++++++++++++++++---------------- labs/SAW/proof/rcs.py | 2 +- 2 files changed, 71 insertions(+), 42 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index b9f33f01..a8cae357 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1,3 +1,21 @@ +# Introduction + +This is a tutorial aimed at training developers how to leverage the +[Software Analysis Workbench (SAW)](https://saw.galois.com) and +Cryptol to develop and verify cryptographic implementations in a +[Continuous Reasoning](https://dl.acm.org/doi/abs/10.1145/3209108.3209109) +paradigm. Continuous Reasoning is, roughly, + +> formal reasoning about a (changing) codebase ... done in a fashion +> which mirrors the iterative, continuous model of software +> development that is increasingly practiced in industry. + +SAW and Cryptol can be used by a Continuous Integration (CI) system to +enforce invariants (safety, security, and functional) that software +must have at certain stages in a software development pipeline. Some +industrial examples include [AWS's s2n](https://link.springer.com/chapter/10.1007/978-3-319-96142-2_26) +and [Supranational's BLST](https://github.com/GaloisInc/BLST-Verification). + # Setting Everything Up To run any of the examples in this lab, you need to first start the @@ -23,6 +41,14 @@ $ saw-remote-api http --host 0.0.0.0 --port 36691 & Congrats! You now have a server version of SAW running in the background ready to accept commands on port `36691`. +# SAW and Python + +If you followed the instructions above, you now have SAW running in +the background, waiting for a connection on port `36691`. Galois +provides a [Python client package](https://pypi.org/project/saw-client/) +that allows you to write formal/logical contracts to enforce +invariants on some given software. + # Python Contracts Introduction Here's the general layout for SAW in Python: @@ -77,10 +103,11 @@ uint32_t RCS(uint32_t bits, uint32_t shift) { } ``` -SAW doesn't actually verify C source, but rather C compiled down to -LLVM intermediate representation (IR), or bitcode. This can be -accomplished via the `clang` compiler. In this instance, we can create -the bitcode by entering the following command in a terminal. +In this example, SAW won't actually verify C source, but rather C +compiled down to LLVM intermediate representation (IR), or +bitcode. This can be accomplished via the `clang` compiler. In this +instance, we can create the bitcode by entering the following command +in a terminal. ```sh $ clang -emit-llvm labs/SAW/src/rcs.c -c -o labs/SAW/src/rcs.bc @@ -138,31 +165,32 @@ Let's break down `specification` piece by piece. The command `self.fresh_var(type, name)` creates a new symbolic variable of type `type` and name `name`, where `name` is a string. Names for fresh symbolic variables are not optional inputs, -and they mostly serve for error messages. The string can be anything, -but it makes sense to give it the name of the variable being defined. +though they mostly serve displaying names in counter-examples and +error messages. The string can be anything, but it makes sense to give +it the name of the variable being defined. ### Execute Functions The command `self.execute_func(input1, input2, ...)` will symbolically -execute the function. There should be exactly as many comma separated -inputs as there are in the C function. One can only place -preconditions before this command and postconditions after this -command. +execute the function we're writing a contract for. There should be +exactly as many comma separated inputs as there are in the C +function. One can only place preconditions before this command and +postconditions after this command. ### Return Statements -The command `self.returns_f(string)` asserts the current function -returns the Cryptol term parsed from a Python string. To use Python -variables in scope within the string use `{variable_name}`. For -example, +The command `self.returns_f(string)` is a postcondition that asserts +the function returns a given Cryptol term (parsed from a Python +string). To use Python variables in scope within the string use +`{variable_name}`. For example, |`self.`|`returns_f(`|`"RCS {bits} {shift}"`|)| |-------|-----------|---------------------|----| |In this contract| assert the current function returns the Cryptol term | right circular shift `bits` by `shift` |.| -Sometimes we don't want to return a Cryptol term. In this case we can -just use `returns(someSetupValue)`. The specification function of a -Contract must **always** have a `self.returns(someSetupValue)` or +Sometimes we don't want to return a Cryptol term. In those cases we +can just use `returns(someSetupValue)`. The specification function of +a Contract must **always** have a `self.returns(someSetupValue)` or `self.returns_f(string)` statement. If the function returns `void` one can use `self.returns(void)`. @@ -183,13 +211,13 @@ The `CryptolTerm` class is a subclass of `SetupVal`. This allows using Braces are sometimes used in Cryptol to assign type parameters or declare records. The symbols `{{` and `}}` are used to denote literal braces for these cases when parsing Python strings. For example, -let's think how to parse the following line: +let's think about how to parse the following line: ```python - self.returns_f("{{a = take`{{5}} {blah}, b = take`{{{N}}} {blah} }} == foo `eel") + self.returns_f("{{a = take`{{5}} {var}, b = take`{{{N}}} {var} }} == foo `eel") ``` -If `blah` is a local Python variable equal to `23` and `N` is a +If `var` is a local Python variable equal to `23` and `N` is a local Python variable equal to `2`, then the string parses in Cryptol as ```cryptol @@ -197,7 +225,7 @@ local Python variable equal to `2`, then the string parses in Cryptol as ``` where `foo` is some Cryptol function returning a record and `eel` is -some Cryptol type in the specification loaded. +some Cryptol type in the currently loaded specification. ## Unit Testing @@ -217,30 +245,30 @@ class RCSTest(unittest.TestCase): self.assertIs(RCS_result.is_success(), True) ``` -For a contract the specification function should be called +For a contract, the specification function should be called `specification`. For tests, it doesn't matter what you name your -tests. Here we named it `test_RCS`. These tests will be ran when you -try running the Python. +tests. Here we named it `test_RCS`. These tests will be run when you +run the Python program. Let's break down the first few lines of this function: - The command `connect(reset_server=True)` connects to the server so - we can use the SAW Python API + we can use the SAW Python API. - The line `if __name__ == "__main__": view(LogResults(verbose_failure=True))` allows us to view the output with verbose error messages. If you don't want verbose error messages, then just use `if __name__ == "__main__": - view(LogResults())` -- The line `bcname = "/some/path/to/your/file.bc"` declares which - bitcode file we're analyzing. If you have multiple bitcode files, - then make a variable for each file. -- The line `mod = llvm_load_module(bcname)` creates the object we will + view(LogResults())`. +- The line `bcname = "/some/path/to/your/file.bc"` declares the + bitcode file to analyze. If there are multiple bitcode files, + make a variable for each file. +- The line `mod = llvm_load_module(bcname)` creates the object to pass to verification that represents the bitcode. - The line `cryname = "/some/path/to/your/file.cry"` specifies the - path to the Cryptol specification. -- The line `cryptol_load_file(cryname)` loads the Cryptol specification. + path to a Cryptol specification. +- The line `cryptol_load_file(cryname)` loads a Cryptol specification. -Now that we've set up our environment, let's actually verify our +Now that the environment is set up, let's actually verify our contract! This is done at the line |`RCS_result =` | `llvm_verify(` | `mod,` | `'RCS',` | `RCS_Contract()`| `)`| @@ -347,7 +375,7 @@ has the following to say about bit shifts: > equal to the width of the promoted left operand, the behavior is > undefined. -As expected, this alerts us of a bug: +As expected, this alerts us to a bug: ``` The second operand of `shl` was equal to or greater than the number of bits in the first operand @@ -406,7 +434,7 @@ Traceback (most recent call last): ``` Aha! The counter example shows that we forgot about the case when -`shift` is zero! This causes `(sizeof(bits) * 8 - 0)` to be `32`, +`shift = 0`! This causes `(sizeof(bits) * 8 - 0)` to be `32`, which is equal to the word-size of `bits`, and hence causes `<<` to exhibit undefined behavior. @@ -420,9 +448,6 @@ uint32_t RCS(uint32_t bits, uint32_t shift) { } ``` -Finally, SAW is happy. More importantly, the C is correct and free of -undefined behavior. - ```sh $ clang ../src/rcs3.c -o ../src/rcs.bc -c -emit-llvm && python3 rcs.py [03:14:09.561] Verifying RCS ... @@ -438,11 +463,15 @@ OK ✅ The goal was verified! ``` +Finally, SAW is happy. More importantly, the C is correct and free of +undefined behavior. + # Pointers and Arrays -We'll begin by writing a function that given two arrays of a common fixed size, say five, adds the -second to the first. One way to accomplish this is to pass in the two arrays, mutate the first -and return nothing: +We'll begin by writing a function that given two arrays of a common +fixed size, say five, adds the second to the first. One way to +accomplish this is to pass in the two arrays, mutate the first and +return nothing: ```C void addRow5Mutate(uint32_t a[5], uint32_t b[5]) { diff --git a/labs/SAW/proof/rcs.py b/labs/SAW/proof/rcs.py index afefdf0c..f6a5d71b 100644 --- a/labs/SAW/proof/rcs.py +++ b/labs/SAW/proof/rcs.py @@ -21,7 +21,7 @@ def test_RCS(self): if __name__ == "__main__": view(LogResults(verbose_failure=True)) pwd = os.getcwd() - bcname = pwd + "/../src/rcs3.bc" + bcname = pwd + "/../src/rcs.bc" cryname = pwd + "/spec/rcs.cry" cryptol_load_file(cryname) From 2ee047302dc9278a7ed4f5f96c3459ce03f87368 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Wed, 1 Jun 2022 22:38:09 -0400 Subject: [PATCH 071/120] spelling fix --- labs/SAW/SAW.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 19993f34..3ac1143a 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1251,7 +1251,7 @@ Our function, `resolveAttack`, takes two inputs: - `character_t* target`: Our defending character - `uint32_t atk`: The attack stat of our attacker -Think about how you would set up a SAW contract for this function. Go ahead and try it you would like! We'll wait for you :) +Think about how to make a SAW contract for this function. Go ahead and try it if you'd like! We'll wait for you :) Got an idea, great! Let's go over the steps we need to go through... @@ -1266,7 +1266,7 @@ class resolveAttack_Contract(Contract): # Assert preconditions - # Sybolically execute the function + # Symbolically execute the function self.execute_func(target_p, atk) # Assert postconditions From f9554fa45f6c13e72a24f011fe4b0331a3a116b8 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 02:47:57 +0000 Subject: [PATCH 072/120] Updated resolveAttack SAW example documentation --- labs/SAW/Game/proof/Game.py | 57 ++++----- labs/SAW/SAW.md | 222 +++++++++++++++++++++++++++++++++++- 2 files changed, 251 insertions(+), 28 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index f8938e72..8910d1d0 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -194,9 +194,11 @@ def specification (self): # Assert the precondition that the stats are below the max stat cap # Pop Quiz: Why do we need these preconditions? + """ self.precondition_f("{atk} <= `{MAX_STAT}") self.precondition_f("{target}.2 <= `{MAX_STAT}") self.precondition_f("{target}.4 <= `{MAX_STAT}") + """ # Determine the preconditions based on the case parameter if (self.case == 1): @@ -204,7 +206,8 @@ def specification (self): self.precondition_f("{target}.4 >= {atk}") elif (self.case == 2): # target->hp <= (atk - target->def) - self.precondition_f("({target}.2 + {target}.4) <= {atk}") + # self.precondition_f("({target}.2 + {target}.4) <= {atk}") + self.precondition_f("{target}.2 <= ({atk} - {target}.4)") else: # Assume any other case follows the formal attack calculation self.precondition_f("{target}.4 < {atk}") @@ -512,49 +515,49 @@ def test_Game(self): module = llvm_load_module(bitcode_name) # Override(s) associated with basic SAW setup - levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) + #levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) # Override(s) associated with basic struct initialization - initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) + #initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) # Overrides(s) associated with preconditions and postconditions that must # be considered in SAW contracts & unit test overrides - checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) - checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) + #checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) + #checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) - selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + #selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + #selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + #selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + #quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) # Override(s) associated with global variable handling - getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) - initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) - setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) - setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) + #getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) + #initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) + #setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) + #setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) # Override(s) showing limitations with struct pointer fields - resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) + #resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) # Assert the overrides are successful - self.assertIs(levelUp_result.is_success(), True) - self.assertIs(initDefaultPlayer_result.is_success(), True) - self.assertIs(checkStats_pass_result.is_success(), True) - self.assertIs(checkStats_fail_result.is_success(), True) + #self.assertIs(levelUp_result.is_success(), True) + #self.assertIs(initDefaultPlayer_result.is_success(), True) + #self.assertIs(checkStats_pass_result.is_success(), True) + #self.assertIs(checkStats_fail_result.is_success(), True) self.assertIs(resolveAttack_case1_result.is_success(), True) self.assertIs(resolveAttack_case2_result.is_success(), True) self.assertIs(resolveAttack_case3_result.is_success(), True) - self.assertIs(selfDamage_case1_result.is_success(), True) - self.assertIs(selfDamage_case2_result.is_success(), True) - self.assertIs(selfDamage_case3_result.is_success(), True) - self.assertIs(quickBattle_result.is_success(), True) - self.assertIs(getDefaultLevel_result.is_success(), True) - self.assertIs(initScreen_result.is_success(), True) - self.assertIs(setScreenTile_pass_result.is_success(), True) - self.assertIs(setScreenTile_fail_result.is_success(), True) - self.assertIs(resetInventoryItems_result.is_success(), True) + #self.assertIs(selfDamage_case1_result.is_success(), True) + #self.assertIs(selfDamage_case2_result.is_success(), True) + #self.assertIs(selfDamage_case3_result.is_success(), True) + #self.assertIs(quickBattle_result.is_success(), True) + #self.assertIs(getDefaultLevel_result.is_success(), True) + #self.assertIs(initScreen_result.is_success(), True) + #self.assertIs(setScreenTile_pass_result.is_success(), True) + #self.assertIs(setScreenTile_fail_result.is_success(), True) + #self.assertIs(resetInventoryItems_result.is_success(), True) if __name__ == "__main__": diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 7d505ada..8cf449a5 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1303,7 +1303,227 @@ class resolveAttack_Contract(Contract): self.returns(void) ``` -Our contract is looking pretty good now! Let's go ahead and test it out with the following unit test: +Our contract is looking pretty good now! Let's set up our unit test: + +```python +class GameTests(unittest.TestCase): + def test_Game(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + pwd = os.getcwd() + bitcode_name = pwd + "/artifacts/Game.bc" + cryptol_name = pwd + "/specs/Game.cry" + + cryptol_load_file(cryptol_name) + module = llvm_load_module(bitcode_name) + + resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) + resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) + resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) + + self.assertIs(resolveAttack_case1_result.is_success(), True) + self.assertIs(resolveAttack_case2_result.is_success(), True) + self.assertIs(resolveAttack_case3_result.is_success(), True) + + +if __name__ == "__main__": + unittest.main() +``` + +Excellent, now for the moment of truth! + +``` +clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c +python3 proof/Game.py +[01:59:53.576] Verifying resolveAttack ... +[01:59:53.577] Simulating resolveAttack ... +[01:59:53.580] Checking proof obligations resolveAttack ... +[01:59:53.594] Proof succeeded! resolveAttack +✅ Verified: lemma_resolveAttack_Contract (defined at proof/Game.py:179) +[01:59:53.658] Verifying resolveAttack ... +[01:59:53.659] Simulating resolveAttack ... +[01:59:53.662] Checking proof obligations resolveAttack ... +[01:59:53.689] Subgoal failed: resolveAttack safety assertion: +internal: error: in llvm_points_to SAWServer +Literal equality postcondition + + +[01:59:53.689] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 438} +[01:59:53.689] ----------Counterexample---------- +[01:59:53.689] target0: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 4294967293, 0, 3, 0) +[01:59:53.689] atk0: 4294967295 +[01:59:53.689] ---------------------------------- +⚠️ Failed to verify: lemma_resolveAttack_Contract0 (defined at proof/Game.py:179): +error: Proof failed. + stdout: + [01:59:53.658] Verifying resolveAttack ... + [01:59:53.659] Simulating resolveAttack ... + [01:59:53.662] Checking proof obligations resolveAttack ... + [01:59:53.689] Subgoal failed: resolveAttack safety assertion: + internal: error: in llvm_points_to SAWServer + Literal equality postcondition + + + [01:59:53.689] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 438} + [01:59:53.689] ----------Counterexample---------- + [01:59:53.689] target0: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 4294967293, 0, 3, 0) + [01:59:53.689] atk0: 4294967295 + [01:59:53.689] ---------------------------------- + +[01:59:53.789] Verifying resolveAttack ... +[01:59:53.789] Simulating resolveAttack ... +[01:59:53.792] Checking proof obligations resolveAttack ... +[01:59:53.905] Proof succeeded! resolveAttack +✅ Verified: lemma_resolveAttack_Contract1 (defined at proof/Game.py:179) +F +====================================================================== +FAIL: test_Game (__main__.GameTests) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "proof/Game.py", line 549, in test_Game + self.assertIs(resolveAttack_case2_result.is_success(), True) +AssertionError: False is not True + +---------------------------------------------------------------------- +Ran 1 test in 1.132s + +FAILED (failures=1) +🛑 1 out of 3 goals failed to verify. +make: *** [Makefile:14: all] Error 1 +``` + +A counterexample?! Let's take a closer look at what SAW provides us and reassess our strategy. + +``` +[01:59:53.689] ----------Counterexample---------- +[01:59:53.689] target0: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 4294967293, 0, 3, 0) +[01:59:53.689] atk0: 4294967295 +[01:59:53.689] ---------------------------------- +``` + +Notice that our unit case failed for case 2, but passed for cases 1 and 3. How about we look at our precondition for case 2 again: + +```python + elif (self.case == 2): + # target->hp <= (atk - target->def) + self.precondition_f("({target}.2 + {target}.4) <= {atk}") +``` + +and if we plug in the counterexample values SAW provides us... + +``` +{target}.2 + {target}.4 <= {atk} +4294967293 + 3 <= 4294967295 + 4294967296 <= 4294967295 +``` + +Well that doesn't make sense, now does it? Well actually, it does make some sense when we recognize one BIG detail. And yes, I'm referring to the big values SAW gave us. Doesn't the atk stat look familiar? Like the upper bound for unsigned 32-bit integers? + +Taking that into consideration, then our efforts to test out the counterexample was incorrect. We forgot to account for integer overflow! + +``` +4294967293 + 3 <= 4294967295 +4294967295 + 1 <= 4294967295 + 0 <= 4294967295 +``` + +*Audible gasp* Good thing we have SAW on our side! Now, let's determine what we can do to fix this issue. First, let's compare our precondition again to the source code's if condition: + +```python +self.precondition_f("({target}.2 + {target}.4) <= {atk}") +``` + +```c +else if ( target->hp <= (atk - target->def) ) +``` + +So maybe we tried being *too* fancy showing off our associative property knowledge. While moving the defense term to the left-hand side of the expression does not violate any mathematical rules on paper, it does violate our expectations in practice when accounting for limited bit widths. + +Fine, let's toss out our fancy math skills and write our contact's precondition for case 2 exactly as we see it in the source code: + +```python + elif (self.case == 2): + # target->hp <= (atk - target->def) + self.precondition_f("{target}.2 <= ({atk} - {target}.4)") +``` + +``` +clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c +python3 proof/Game.py +[02:34:39.387] Verifying resolveAttack ... +[02:34:39.388] Simulating resolveAttack ... +[02:34:39.391] Checking proof obligations resolveAttack ... +[02:34:39.409] Proof succeeded! resolveAttack +✅ Verified: lemma_resolveAttack_Contract (defined at proof/Game.py:179) +[02:34:39.481] Verifying resolveAttack ... +[02:34:39.481] Simulating resolveAttack ... +[02:34:39.483] Checking proof obligations resolveAttack ... +[02:34:39.508] Subgoal failed: resolveAttack safety assertion: +internal: error: in llvm_points_to SAWServer +Literal equality postcondition + + +[02:34:39.508] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 438} +[02:34:39.509] ----------Counterexample---------- +[02:34:39.509] target0: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 134217728, 0, 4184634256, 0) +[02:34:39.509] atk0: 23884688 +[02:34:39.509] ---------------------------------- +⚠️ Failed to verify: lemma_resolveAttack_Contract0 (defined at proof/Game.py:179): +error: Proof failed. + stdout: + [02:34:39.481] Verifying resolveAttack ... + [02:34:39.481] Simulating resolveAttack ... + [02:34:39.483] Checking proof obligations resolveAttack ... + [02:34:39.508] Subgoal failed: resolveAttack safety assertion: + internal: error: in llvm_points_to SAWServer + Literal equality postcondition + + + [02:34:39.508] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 438} + [02:34:39.509] ----------Counterexample---------- + [02:34:39.509] target0: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 134217728, 0, 4184634256, 0) + [02:34:39.509] atk0: 23884688 + [02:34:39.509] ---------------------------------- + +[02:34:39.613] Verifying resolveAttack ... +[02:34:39.613] Simulating resolveAttack ... +[02:34:39.616] Checking proof obligations resolveAttack ... +[02:34:39.735] Proof succeeded! resolveAttack +✅ Verified: lemma_resolveAttack_Contract1 (defined at proof/Game.py:179) +F +====================================================================== +FAIL: test_Game (__main__.GameTests) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "proof/Game.py", line 550, in test_Game + self.assertIs(resolveAttack_case2_result.is_success(), True) +AssertionError: False is not True + +---------------------------------------------------------------------- +Ran 1 test in 1.200s + +FAILED (failures=1) +🛑 1 out of 3 goals failed to verify. +make: *** [Makefile:14: all] Error 1 +``` + +Nope, that didn't work either. But hey, SAW gave us a different counterexample. Let's look at that one: + +``` +[02:34:39.509] ----------Counterexample---------- +[02:34:39.509] target0: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 134217728, 0, 4184634256, 0) +[02:34:39.509] atk0: 23884688 +[02:34:39.509] ---------------------------------- +``` + +Plugging those values into our updated case 2 precondition: + +``` +{target}.2 <= ({atk} - {target}.4) + 134217728 <= 23884688 - 4184634256 + 134217728 <= 134217728 +``` From 6476840673ccfd5acc39bc1b4ba7befb5fbd6035 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 03:35:01 +0000 Subject: [PATCH 073/120] Included checkStats discussion to resolveAttack example --- labs/SAW/Game/proof/Game.py | 60 +++++++++---------- labs/SAW/SAW.md | 112 ++++++++++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+), 30 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 8910d1d0..0f6bd115 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -194,11 +194,9 @@ def specification (self): # Assert the precondition that the stats are below the max stat cap # Pop Quiz: Why do we need these preconditions? - """ self.precondition_f("{atk} <= `{MAX_STAT}") self.precondition_f("{target}.2 <= `{MAX_STAT}") self.precondition_f("{target}.4 <= `{MAX_STAT}") - """ # Determine the preconditions based on the case parameter if (self.case == 1): @@ -206,8 +204,10 @@ def specification (self): self.precondition_f("{target}.4 >= {atk}") elif (self.case == 2): # target->hp <= (atk - target->def) - # self.precondition_f("({target}.2 + {target}.4) <= {atk}") - self.precondition_f("{target}.2 <= ({atk} - {target}.4)") + self.precondition_f("({target}.2 + {target}.4) <= {atk}") + # Pop Quiz: Are the following preconditions better for case 2? + # self.precondition_f("{target}.4 < {atk}") + # self.precondition_f("{target}.2 <= ({atk} - {target}.4)") else: # Assume any other case follows the formal attack calculation self.precondition_f("{target}.4 < {atk}") @@ -515,49 +515,49 @@ def test_Game(self): module = llvm_load_module(bitcode_name) # Override(s) associated with basic SAW setup - #levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) + levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) # Override(s) associated with basic struct initialization - #initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) + initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) # Overrides(s) associated with preconditions and postconditions that must # be considered in SAW contracts & unit test overrides - #checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) - #checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) + checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) + checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) - #selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - #selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - #selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - #quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) # Override(s) associated with global variable handling - #getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) - #initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) - #setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) - #setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) + getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) + initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) + setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) + setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) # Override(s) showing limitations with struct pointer fields - #resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) + resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) # Assert the overrides are successful - #self.assertIs(levelUp_result.is_success(), True) - #self.assertIs(initDefaultPlayer_result.is_success(), True) - #self.assertIs(checkStats_pass_result.is_success(), True) - #self.assertIs(checkStats_fail_result.is_success(), True) + self.assertIs(levelUp_result.is_success(), True) + self.assertIs(initDefaultPlayer_result.is_success(), True) + self.assertIs(checkStats_pass_result.is_success(), True) + self.assertIs(checkStats_fail_result.is_success(), True) self.assertIs(resolveAttack_case1_result.is_success(), True) self.assertIs(resolveAttack_case2_result.is_success(), True) self.assertIs(resolveAttack_case3_result.is_success(), True) - #self.assertIs(selfDamage_case1_result.is_success(), True) - #self.assertIs(selfDamage_case2_result.is_success(), True) - #self.assertIs(selfDamage_case3_result.is_success(), True) - #self.assertIs(quickBattle_result.is_success(), True) - #self.assertIs(getDefaultLevel_result.is_success(), True) - #self.assertIs(initScreen_result.is_success(), True) - #self.assertIs(setScreenTile_pass_result.is_success(), True) - #self.assertIs(setScreenTile_fail_result.is_success(), True) - #self.assertIs(resetInventoryItems_result.is_success(), True) + self.assertIs(selfDamage_case1_result.is_success(), True) + self.assertIs(selfDamage_case2_result.is_success(), True) + self.assertIs(selfDamage_case3_result.is_success(), True) + self.assertIs(quickBattle_result.is_success(), True) + self.assertIs(getDefaultLevel_result.is_success(), True) + self.assertIs(initScreen_result.is_success(), True) + self.assertIs(setScreenTile_pass_result.is_success(), True) + self.assertIs(setScreenTile_fail_result.is_success(), True) + self.assertIs(resetInventoryItems_result.is_success(), True) if __name__ == "__main__": diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index e54a4ce3..b70ff030 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1554,6 +1554,118 @@ Plugging those values into our updated case 2 precondition: 134217728 <= 134217728 ``` +Nice, we got an integer underflow counterexample. Based on these input values, the source code would actually meet the first if condition: + +```c +target->def >= atk +``` + +This means our precondition for case 2 is lacking something. Let's adjust it in this way: + +```python + elif (self.case == 2): + # target->hp <= (atk - target->def) + self.precondition_f("{target}.4 < {atk}") + self.precondition_f("{target}.2 <= ({atk} - {target}.4)") +``` + +``` +clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c +python3 proof/Game.py +[02:55:30.437] Verifying resolveAttack ... +[02:55:30.437] Simulating resolveAttack ... +[02:55:30.440] Checking proof obligations resolveAttack ... +[02:55:30.455] Proof succeeded! resolveAttack +✅ Verified: lemma_resolveAttack_Contract (defined at proof/Game.py:179) +[02:55:30.551] Verifying resolveAttack ... +[02:55:30.551] Simulating resolveAttack ... +[02:55:30.554] Checking proof obligations resolveAttack ... +[02:55:30.570] Proof succeeded! resolveAttack +✅ Verified: lemma_resolveAttack_Contract0 (defined at proof/Game.py:179) +[02:55:30.672] Verifying resolveAttack ... +[02:55:30.673] Simulating resolveAttack ... +[02:55:30.675] Checking proof obligations resolveAttack ... +[02:55:30.789] Proof succeeded! resolveAttack +✅ Verified: lemma_resolveAttack_Contract1 (defined at proof/Game.py:179) +. +---------------------------------------------------------------------- +Ran 1 test in 1.140s + +OK +✅ All 3 goals verified! +``` + +Whoo hoo! We finally won the battle! From our exercise, we found that SAW provides us with some pretty useful counterexamples to consider for edge cases that may be lacking in traditional software unit testing. + +Now let's imagine that very large character stats is a concern in our game. In order to balance characters in our game and avoid problems with very large values, let's say we decided to add a function (`checkStats`) that checks character stats. Let's also assume that this function is always called before functions that use those stats like what we saw in `resolveAttack`. + +```c +uint32_t checkStats(character_t* character) +{ + // Assume failure by default + uint32_t result = FAILURE; + + // Check the stats + if (character->hp <= MAX_STAT && + character->atk <= MAX_STAT && + character->def <= MAX_STAT && + character->spd <= MAX_STAT ) + { + result = SUCCESS; + } + + return result; +} +``` + +Is this a good idea security-wise? Eh, maybe not. The assumption that `checkStats` is ALWAYS called before functions that use character stats may be missed. So what can we do? Using `resolveAttack` as an example, should we rely on its caller to also call `checkStats` and perform error handling ahead of time? An argument could be made for performance benefits, but at the cost of security. Should we call `checkStats` within `resolveAttack`? That strategy would provide more security, but the repeated `checkStats` may be redundant and could hurt performance depending on the use case. As we can see, the answer for `checkStats`'s best placement follows the classic "it depends". + +For the sake of argument, let's go with the assumption that `checkStats` is always called BEFORE the call to `resolveAttack`. While the stat checks aren't performed in `resolveAttack`, we could include them as preconditions in our contract. Let's add the stat checks to our original `resolveAttack_Contract`, problems with case 2's preconditons and all! + +```python +class resolveAttack_Contract(Contract): + def __init__(self, case : int): + super().__init__() + self.case = case + + def specification (self): + # Declare variables + (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") + atk = self.fresh_var(i32, "atk") + + # Assert the precondition that the stats are below the max stat cap + # Suggests checkStats was called before resolveAttack + self.precondition_f("{atk} <= `{MAX_STAT}") + self.precondition_f("{target}.2 <= `{MAX_STAT}") + self.precondition_f("{target}.4 <= `{MAX_STAT}") + + # Determine the preconditions based on the case parameter + if (self.case == 1): + # target->def >= atk + self.precondition_f("{target}.4 >= {atk}") + elif (self.case == 2): + # target->hp <= (atk - target->def) + self.precondition_f("({target}.2 + {target}.4) <= {atk}") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("{target}.4 < {atk}") + self.precondition_f("({target}.2 + {target}.4) > {atk}") + + # Symbolically execute the function + self.execute_func(target_p, atk) + + # Determine the postcondition based on the case parameter + if (self.case == 1): + self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) + elif (self.case == 2): + self.points_to(target_p['hp'], cry_f("0 : [32]")) + else: + self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + + self.returns(void) +``` + +Would this contract pass verification? Absolutely. Given that the `MAX_STAT` preconditions limits our input values, we would never see SAW's counterexample of an integer overflow/underflow from case 2. From 45465856d65b0dd2e3efe9ca2fad926a0be51f7d Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 07:41:54 -0400 Subject: [PATCH 074/120] RCS -> rotl to mesh better with Salsa20 --- labs/SAW/SAW.md | 122 ++++++++++++++--------------- labs/SAW/proof/{rcs.py => rotl.py} | 16 ++-- labs/SAW/proof/spec/rcs.cry | 4 - labs/SAW/proof/spec/rotl.cry | 4 + labs/SAW/src/rcs.c | 7 -- labs/SAW/src/rcs2.c | 8 -- labs/SAW/src/rotl.c | 7 ++ labs/SAW/src/rotl2.c | 8 ++ labs/SAW/src/{rcs3.c => rotl3.c} | 4 +- 9 files changed, 90 insertions(+), 90 deletions(-) rename labs/SAW/proof/{rcs.py => rotl.py} (65%) delete mode 100644 labs/SAW/proof/spec/rcs.cry create mode 100644 labs/SAW/proof/spec/rotl.cry delete mode 100644 labs/SAW/src/rcs.c delete mode 100644 labs/SAW/src/rcs2.c create mode 100644 labs/SAW/src/rotl.c create mode 100644 labs/SAW/src/rotl2.c rename labs/SAW/src/{rcs3.c => rotl3.c} (52%) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index b70ff030..53f55b86 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -92,14 +92,14 @@ they could refer to this table for some common imports: |`saw_client.llvm` | `Contract`, `CryptolTerm`, `SetupVal`, `FreshVar`, `i8`, `i32`, `i64`, `void`, `null`, `array_ty`, `field`, `struct`, `alias_ty` | |`saw_client.llvm_type` | `LLVMType`, `LLVMArrayType` | -## Right Circular Shift Example +## Left Circular Shift Example To see contracts in action we need an example. Here's some C code for -right circular shift we want to verify: +left circular shift we want to verify: ```C -uint32_t RCS(uint32_t bits, uint32_t shift) { - return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); +uint32_t rotl(uint32_t bits, uint32_t shift) { + return (bits << shift) | (bits >> (sizeof(bits) * 8 - shift)); } ``` @@ -110,7 +110,7 @@ instance, we can create the bitcode by entering the following command in a terminal. ```sh -$ clang -emit-llvm labs/SAW/src/rcs.c -c -o labs/SAW/src/rcs.bc +$ clang -emit-llvm labs/SAW/src/rotl.c -c -o labs/SAW/src/rotl.bc ``` We can inspect the bitcode using SAW by loading the module and @@ -123,9 +123,9 @@ $ saw ┣━━ ┃ ╻ ┃┓ ╻ ┏┛ ┗━━━┛━┛━┛┗━┛━┛ version 0.9.0.99 () -sawscript> r <- llvm_load_module "labs/SAW/src/rcs.bc" +sawscript> r <- llvm_load_module "labs/SAW/src/rotl.bc" sawscript> print r -Module: rcs.bc +Module: rotl.bc Types: Globals: @@ -133,29 +133,29 @@ Globals: External references: Definitions: - i32 @RCS(i32 %0, i32 %1) + i32 @rotl(i32 %0, i32 %1) ``` -The corresponding Cryptol specification for right circular shift is: +The corresponding Cryptol specification for left circular shift is: ```cryptol -RCS : [32] -> [32] -> [32] -RCS xs shift = xs >>> shift +rotl : [32] -> [32] -> [32] +rotl xs shift = xs <<< shift ``` For the SAW Python API we make a `Contract` object with the required `specification` function: ```python -class RCS_Contract(Contract): +class rotl_Contract(Contract): def specification(self): bits = self.fresh_var(i32, "bits") shift = self.fresh_var(i32, "shift") self.execute_func(bits, shift) - self.returns_f("RCS {bits} {shift}") + self.returns_f("rotl {bits} {shift}") ``` Let's break down `specification` piece by piece. @@ -184,9 +184,9 @@ the function returns a given Cryptol term (parsed from a Python string). To use Python variables in scope within the string use `{variable_name}`. For example, -|`self.`|`returns_f(`|`"RCS {bits} {shift}"`|)| +|`self.`|`returns_f(`|`"rotl {bits} {shift}"`|)| |-------|-----------|---------------------|----| -|In this contract| assert the current function returns the Cryptol term | right circular shift `bits` by `shift` |.| +|In this contract| assert the current function returns the Cryptol term | left circular shift `bits` by `shift` |.| Sometimes we don't want to return a Cryptol term. In those cases we can just use `returns(someSetupValue)`. The specification function of @@ -230,8 +230,8 @@ some Cryptol type in the currently loaded specification. ## Unit Testing ```python -class RCSTest(unittest.TestCase): - def test_RCS(self): +class rotlTest(unittest.TestCase): + def test_rotl(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) @@ -241,13 +241,13 @@ class RCSTest(unittest.TestCase): cryname = "/some/path/to/your/file.cry" cryptol_load_file(cryname) - RCS_result = llvm_verify(mod, 'RCS', RCS_Contract()) - self.assertIs(RCS_result.is_success(), True) + rotl_result = llvm_verify(mod, 'rotl', rotl_Contract()) + self.assertIs(rotl_result.is_success(), True) ``` For a contract, the specification function should be called `specification`. For tests, it doesn't matter what you name your -tests. Here we named it `test_RCS`. These tests will be run when you +tests. Here we named it `test_rotl`. These tests will be run when you run the Python program. Let's break down the first few lines of this function: @@ -271,12 +271,12 @@ Let's break down the first few lines of this function: Now that the environment is set up, let's actually verify our contract! This is done at the line -|`RCS_result =` | `llvm_verify(` | `mod,` | `'RCS',` | `RCS_Contract()`| `)`| +|`rotl_result =` | `llvm_verify(` | `mod,` | `'rotl',` | `rotl_Contract()`| `)`| |----------|-----------|----|------|---------------|----| |Assign this variable| to the result of trying to verify| the bitcode| function with this name| using this contract|.| Now that we have the result, we want to assert this result succeeded -using `self.assertIs(RCS_result.is_success(), True)`. +using `self.assertIs(rotl_result.is_success(), True)`. ## Debugging C with SAW @@ -292,29 +292,29 @@ from saw_client.llvm import * from saw_client.proofscript import * from saw_client.llvm_type import * -class RCS_Contract(Contract): +class rotl_Contract(Contract): def specification(self): xs = self.fresh_var(i32, "xs") shift = self.fresh_var(i32, "shift") self.execute_func(xs, shift) - self.returns_f("RCS {xs} {shift}") + self.returns_f("rotl {xs} {shift}") -class RCSTest(unittest.TestCase): - def test_RCS(self): +class rotlTest(unittest.TestCase): + def test_rotl(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) pwd = os.getcwd() - bcname = pwd + "/../src/rcs.bc" - cryname = pwd + "/spec/rcs.cry" + bcname = pwd + "/../src/rotl.bc" + cryname = pwd + "/spec/rotl.cry" cryptol_load_file(cryname) mod = llvm_load_module(bcname) - RCS_result = llvm_verify(mod, 'RCS', RCS_Contract()) - self.assertIs(RCS_result.is_success(), True) + rotl_result = llvm_verify(mod, 'rotl', rotl_Contract()) + self.assertIs(rotl_result.is_success(), True) if __name__ == "__main__": unittest.main() @@ -335,12 +335,12 @@ We can now run the proof script. ``` $ cd labs/SAW/proof -$ python3 rcs.py -[03:08:29.986] Verifying RCS ... -[03:08:29.987] Simulating RCS ... -[03:08:29.988] Checking proof obligations RCS ... -[03:08:30.007] Subgoal failed: RCS safety assertion: -internal: error: in RCS +$ python3 rotl.py +[03:08:29.986] Verifying rotl ... +[03:08:29.987] Simulating rotl ... +[03:08:29.988] Checking proof obligations rotl ... +[03:08:30.007] Subgoal failed: rotl safety assertion: +internal: error: in rotl Undefined behavior encountered Details: Poison value created @@ -348,16 +348,16 @@ Details: [03:08:30.007] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 382} [03:08:30.007] ----------Counterexample---------- -[03:08:30.007] shift0: 134217728 +[03:08:30.007] shift0: 2147483648 [03:08:30.007] ---------------------------------- F ====================================================================== -FAIL: test_RCS (__main__.RCSTest) +FAIL: test_rotl (__main__.rotlTest) ---------------------------------------------------------------------- Traceback (most recent call last): - File "cryptol-course/labs/SAW/proof/rcs.py", line 31, in test_RCS - self.assertIs(RCS_result.is_success(), True) + File "cryptol-course/labs/SAW/proof/rotl.py", line 31, in test_rotl + self.assertIs(rotl_result.is_success(), True) AssertionError: False is not True ---------------------------------------------------------------------- @@ -382,31 +382,31 @@ As expected, this alerts us to a bug: [03:08:30.007] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 382} [03:08:30.007] ----------Counterexample---------- -[03:08:30.007] shift0: 134217728 +[03:08:30.007] shift0: 2147483648 [03:08:30.007] ---------------------------------- ``` SAW also provides a handy counterexample, namely, when `shift = -134217728` (clearly larger than 31), we encounter undefined behavior. +2147483648` (clearly larger than 31), we encounter undefined behavior. One remedy to this is the following: ```C -uint32_t RCS(uint32_t bits, uint32_t shift) { +uint32_t rotl(uint32_t bits, uint32_t shift) { shift %= sizeof(bits) * 8; - return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); + return (bits << shift) | (bits >> (sizeof(bits) * 8 - shift)); } ``` Recompiling and running SAW gives: ```sh -$ clang ../src/rcs2.c -o ../src/rcs.bc -c -emit-llvm && python3 rcs.py -[03:11:54.334] Verifying RCS ... -[03:11:54.334] Simulating RCS ... -[03:11:54.335] Checking proof obligations RCS ... -[03:11:54.351] Subgoal failed: RCS safety assertion: -internal: error: in RCS +$ clang ../src/rotl2.c -o ../src/rotl.bc -c -emit-llvm && python3 rotl.py +[03:11:54.334] Verifying rotl ... +[03:11:54.334] Simulating rotl ... +[03:11:54.335] Checking proof obligations rotl ... +[03:11:54.351] Subgoal failed: rotl safety assertion: +internal: error: in rotl Undefined behavior encountered Details: Poison value created @@ -419,11 +419,11 @@ Details: F ====================================================================== -FAIL: test_RCS (__main__.RCSTest) +FAIL: test_rotl (__main__.rotlTest) ---------------------------------------------------------------------- Traceback (most recent call last): - File "cryptol-course/labs/SAW/proof/rcs.py", line 31, in test_RCS - self.assertIs(RCS_result.is_success(), True) + File "cryptol-course/labs/SAW/proof/rotl.py", line 31, in test_rotl + self.assertIs(rotl_result.is_success(), True) AssertionError: False is not True ---------------------------------------------------------------------- @@ -441,20 +441,20 @@ exhibit undefined behavior. Let's try again with ```C -uint32_t RCS(uint32_t bits, uint32_t shift) { +uint32_t rotl(uint32_t bits, uint32_t shift) { shift %= sizeof(bits)*8; if(shift == 0) return bits; - return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); + return (bits << shift) | (bits >> (sizeof(bits) * 8 - shift)); } ``` ```sh -$ clang ../src/rcs3.c -o ../src/rcs.bc -c -emit-llvm && python3 rcs.py -[03:14:09.561] Verifying RCS ... -[03:14:09.562] Simulating RCS ... -[03:14:09.563] Checking proof obligations RCS ... -[03:14:09.614] Proof succeeded! RCS -✅ Verified: lemma_RCS_Contract (defined at cryptol-course/labs/SAW/proof/rcs.py:30) +$ clang ../src/rotl3.c -o ../src/rotl.bc -c -emit-llvm && python3 rotl.py +[03:14:09.561] Verifying rotl ... +[03:14:09.562] Simulating rotl ... +[03:14:09.563] Checking proof obligations rotl ... +[03:14:09.614] Proof succeeded! rotl +✅ Verified: lemma_rotl_Contract (defined at cryptol-course/labs/SAW/proof/rotl.py:30) . ---------------------------------------------------------------------- Ran 1 test in 0.780s diff --git a/labs/SAW/proof/rcs.py b/labs/SAW/proof/rotl.py similarity index 65% rename from labs/SAW/proof/rcs.py rename to labs/SAW/proof/rotl.py index f6a5d71b..1af6d4df 100644 --- a/labs/SAW/proof/rcs.py +++ b/labs/SAW/proof/rotl.py @@ -6,29 +6,29 @@ from saw_client.proofscript import * from saw_client.llvm_type import * -class RCS_Contract(Contract): +class rotl_Contract(Contract): def specification(self): xs = self.fresh_var(i32, "xs") shift = self.fresh_var(i32, "shift") self.execute_func(xs, shift) - self.returns_f("RCS {xs} {shift}") + self.returns_f("rotl {xs} {shift}") -class RCSTest(unittest.TestCase): - def test_RCS(self): +class rotlTest(unittest.TestCase): + def test_rotl(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) pwd = os.getcwd() - bcname = pwd + "/../src/rcs.bc" - cryname = pwd + "/spec/rcs.cry" + bcname = pwd + "/../src/rotl.bc" + cryname = pwd + "/spec/rotl.cry" cryptol_load_file(cryname) mod = llvm_load_module(bcname) - RCS_result = llvm_verify(mod, 'RCS', RCS_Contract()) - self.assertIs(RCS_result.is_success(), True) + rotl_result = llvm_verify(mod, 'rotl', rotl_Contract()) + self.assertIs(rotl_result.is_success(), True) if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/proof/spec/rcs.cry b/labs/SAW/proof/spec/rcs.cry deleted file mode 100644 index a1f976d7..00000000 --- a/labs/SAW/proof/spec/rcs.cry +++ /dev/null @@ -1,4 +0,0 @@ -module rcs where - -RCS : [32] -> [32] -> [32] -RCS xs shift = xs >>> shift diff --git a/labs/SAW/proof/spec/rotl.cry b/labs/SAW/proof/spec/rotl.cry new file mode 100644 index 00000000..468664f7 --- /dev/null +++ b/labs/SAW/proof/spec/rotl.cry @@ -0,0 +1,4 @@ +module rotl where + +rotl : [32] -> [32] -> [32] +rotl xs shift = xs <<< shift diff --git a/labs/SAW/src/rcs.c b/labs/SAW/src/rcs.c deleted file mode 100644 index 43cd4cda..00000000 --- a/labs/SAW/src/rcs.c +++ /dev/null @@ -1,7 +0,0 @@ -#include -#include -#include - -uint32_t RCS(uint32_t bits, uint32_t shift) { - return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); -} diff --git a/labs/SAW/src/rcs2.c b/labs/SAW/src/rcs2.c deleted file mode 100644 index a69ee8c3..00000000 --- a/labs/SAW/src/rcs2.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include -#include - -uint32_t RCS(uint32_t bits, uint32_t shift) { - shift %= sizeof(bits) * 8; - return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); -} diff --git a/labs/SAW/src/rotl.c b/labs/SAW/src/rotl.c new file mode 100644 index 00000000..8c119eb4 --- /dev/null +++ b/labs/SAW/src/rotl.c @@ -0,0 +1,7 @@ +#include +#include +#include + +uint32_t rotl(uint32_t bits, uint32_t shift) { + return (bits << shift) | (bits >> (sizeof(bits) * 8 - shift)); +} diff --git a/labs/SAW/src/rotl2.c b/labs/SAW/src/rotl2.c new file mode 100644 index 00000000..ac7621cf --- /dev/null +++ b/labs/SAW/src/rotl2.c @@ -0,0 +1,8 @@ +#include +#include +#include + +uint32_t rotl(uint32_t bits, uint32_t shift) { + shift %= sizeof(bits) * 8; + return (bits << shift) | (bits >> (sizeof(bits) * 8 - shift)); +} diff --git a/labs/SAW/src/rcs3.c b/labs/SAW/src/rotl3.c similarity index 52% rename from labs/SAW/src/rcs3.c rename to labs/SAW/src/rotl3.c index cff451af..a6ee3f20 100644 --- a/labs/SAW/src/rcs3.c +++ b/labs/SAW/src/rotl3.c @@ -2,8 +2,8 @@ #include #include -uint32_t RCS(uint32_t bits, uint32_t shift) { +uint32_t rotl(uint32_t bits, uint32_t shift) { shift %= sizeof(bits) * 8; if(shift == 0) return bits; - return (bits << (sizeof(bits) * 8 - shift)) | (bits >> shift); + return (bits << shift) | (bits >> (sizeof(bits) * 8 - shift)); } From 2beb914cf86984fedaa49132e5f5d2dcac719f71 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 08:33:38 -0400 Subject: [PATCH 075/120] Updated course workflow diagram --- misc/CryptolCourse.gv | 4 ++++ misc/CryptolCourse.gv.png | Bin 47039 -> 71245 bytes 2 files changed, 4 insertions(+) diff --git a/misc/CryptolCourse.gv b/misc/CryptolCourse.gv index cd3d03ad..4baf51aa 100644 --- a/misc/CryptolCourse.gv +++ b/misc/CryptolCourse.gv @@ -31,6 +31,7 @@ digraph { Salsa20Properties [URL="../labs/Salsa20/Salsa20Props.html"]; TranspositionCiphers [URL="../labs/Transposition/Contents.html"]; ProjectEuler [URL="../labs/ProjectEuler/ProjectEuler.html"]; + ContinuousReasoning [URL="../labs/SAW/SAW.html"]; ParameterizedModules [URL="../labs/SimonSpeck/SimonSpeck.html"]; // newline/space labels @@ -44,6 +45,7 @@ digraph { TranspositionCiphers [label = "Transposition\nCiphers"] ParameterizedModules [label = "Parameterized\nModules"] Salsa20Properties [label = "Salsa20\nProperties"] + ContinuousReasoning [label = "SAW\nContinuous Reasoning"]; StyleGuide [label = "Style Guide"] ProjectEuler [label = "Project Euler"] @@ -69,6 +71,7 @@ digraph { CryptographicProperties -> Salsa20Properties; CryptographicProperties -> TranspositionCiphers; CryptographicProperties -> ProjectEuler; + CryptographicProperties -> ContinuousReasoning KeyWrapping -> ParameterizedModules; // ranks @@ -99,6 +102,7 @@ digraph { Salsa20Properties; TranspositionCiphers; ProjectEuler; + ContinuousReasoning; } { diff --git a/misc/CryptolCourse.gv.png b/misc/CryptolCourse.gv.png index 867231b6cc4c974b63d07a8754ad8a3a44def800..5f690db0ac349cead0fde688d9d9bea7a172a95e 100644 GIT binary patch literal 71245 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 NzQxKSbJM|J{|7%QYoP!D literal 47039 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⪻UCuv+_LoQ#mG$f`ul5K{}$;E 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 Date: Thu, 2 Jun 2022 08:34:00 -0400 Subject: [PATCH 076/120] Updated course workflow diagram --- misc/CryptolCourse.gv.svg | 262 ++++++++++++++++++++++---------------- 1 file changed, 155 insertions(+), 107 deletions(-) diff --git a/misc/CryptolCourse.gv.svg b/misc/CryptolCourse.gv.svg index 9256ee97..ff8c8253 100644 --- a/misc/CryptolCourse.gv.svg +++ b/misc/CryptolCourse.gv.svg @@ -1,233 +1,281 @@ - - - - -%3 + + + -Installation + +Installation -Installation +Installation -Overview + +Overview -Overview +Overview -Installation->Overview - - + +Installation->Overview + + -Interpreter + +Interpreter -Interpreter +Interpreter -Overview->Interpreter - - + +Overview->Interpreter + + -LanguageBasics + +LanguageBasics -Language -Basics +Language +Basics -Interpreter->LanguageBasics - - + +Interpreter->LanguageBasics + + -CRC + +CRC - -CRC + +CRC -LanguageBasics->CRC - - + +LanguageBasics->CRC + + -StyleGuide + +StyleGuide - -Style Guide + +Style Guide -LanguageBasics->StyleGuide - - + +LanguageBasics->StyleGuide + + -CryptolDemos + +CryptolDemos - -Cryptol Demos + +Cryptol Demos -LanguageBasics->CryptolDemos - - + +LanguageBasics->CryptolDemos + + -SAWDemos + +SAWDemos - -SAW Demos + +SAW Demos -LanguageBasics->SAWDemos - - + +LanguageBasics->SAWDemos + + -TypeHackery + +TypeHackery - -Type Hackery + +Type Hackery -LanguageBasics->TypeHackery - - + +LanguageBasics->TypeHackery + + -Salsa20 + +Salsa20 - -Salsa20 + +Salsa20 -CRC->Salsa20 - - + +CRC->Salsa20 + + -CryptographicProperties + +CryptographicProperties - -Cryptographic -Properties + +Cryptographic +Properties -Salsa20->CryptographicProperties - - + +Salsa20->CryptographicProperties + + -KeyWrapping + +KeyWrapping - -Key Wrapping + +Key Wrapping -CryptographicProperties->KeyWrapping - - + +CryptographicProperties->KeyWrapping + + -Salsa20Properties + +Salsa20Properties - -Salsa20 -Properties + +Salsa20 +Properties -CryptographicProperties->Salsa20Properties - - + +CryptographicProperties->Salsa20Properties + + -TranspositionCiphers + +TranspositionCiphers - -Transposition -Ciphers + +Transposition +Ciphers -CryptographicProperties->TranspositionCiphers - - + +CryptographicProperties->TranspositionCiphers + + -ProjectEuler + +ProjectEuler - -Project Euler + +Project Euler -CryptographicProperties->ProjectEuler - - + +CryptographicProperties->ProjectEuler + + + + + +ContinuousReasoning + + +SAW +Continuous Reasoning + + + + + +CryptographicProperties->ContinuousReasoning + + -Capstone + +Capstone - -Capstone + +Capstone -KeyWrapping->Capstone - - + +KeyWrapping->Capstone + + -ParameterizedModules - - -Parameterized -Modules + +ParameterizedModules + + +Parameterized +Modules -KeyWrapping->ParameterizedModules - - + +KeyWrapping->ParameterizedModules + + From 2ce1b06e37ce311a43ff119f2d7a42c27ad5e3b4 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 08:51:14 -0400 Subject: [PATCH 077/120] First take at breadcrumbs for SAW lab --- README.md | 3 +++ labs/CryptoProofs/CryptoProofs.md | 1 + labs/SAW/SAW.md | 11 +++++++++++ 3 files changed, 15 insertions(+) diff --git a/README.md b/README.md index c5e02c09..ed029fe5 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,9 @@ first lab walks you through [installing and running Cryptol](INSTALL.md). enjoyed the last lab, go ahead and try your hand at using Cryptol's connection to automated provers (SMT solvers) to solve some classic computational puzzles. + * [Continuous Reasoning with SAW](./labs/SAW/SAW.md): Learn how to + use Python to drive SAW and enforce formal invariants on + cryptographic implementations at every check-in to a repository. 8. [Methods for Key Wrapping](./labs/KeyWrapping/KeyWrapping.md): Create a Cryptol specification of NIST's [SP800-38F key wrap standard](https://csrc.nist.gov/publications/detail/sp/800-38f/final). diff --git a/labs/CryptoProofs/CryptoProofs.md b/labs/CryptoProofs/CryptoProofs.md index 699294a7..fd1d5665 100644 --- a/labs/CryptoProofs/CryptoProofs.md +++ b/labs/CryptoProofs/CryptoProofs.md @@ -383,3 +383,4 @@ https://github.com/weaversa/cryptol-course/issues || [+ Salsa20 Properties](../Salsa20/Salsa20Props.md) || || [+ Transposition Ciphers](../Transposition/Contents.md) || || [+ Project Euler](../ProjectEuler/ProjectEuler.md) || +|| [+ Continuous Reasoning with SAW](./labs/SAW/SAW.md) || diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 53f55b86..5198bf81 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1790,3 +1790,14 @@ Satisfiability Modulo Theories. ## Capstone +# Solicitation + +How was your experience with this lab? Suggestions are welcome in the +form of a ticket on the course GitHub page: +https://github.com/weaversa/cryptol-course/issues + +# From here, you can go somewhere! + +|||| +|-:|:-:|-| +|| [- Cryptographic Properties](../CryptoProofs/CryptoProofs.md) || From 9bb766c449a4d5cff105e11539e2665968575496 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 08:53:36 -0400 Subject: [PATCH 078/120] fixing breadcrumbs --- labs/CryptoProofs/CryptoProofs.md | 2 +- labs/CryptoProofs/CryptoProofsAnswers.md | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/labs/CryptoProofs/CryptoProofs.md b/labs/CryptoProofs/CryptoProofs.md index fd1d5665..ba7911e9 100644 --- a/labs/CryptoProofs/CryptoProofs.md +++ b/labs/CryptoProofs/CryptoProofs.md @@ -383,4 +383,4 @@ https://github.com/weaversa/cryptol-course/issues || [+ Salsa20 Properties](../Salsa20/Salsa20Props.md) || || [+ Transposition Ciphers](../Transposition/Contents.md) || || [+ Project Euler](../ProjectEuler/ProjectEuler.md) || -|| [+ Continuous Reasoning with SAW](./labs/SAW/SAW.md) || +|| [+ Continuous Reasoning with SAW](../SAW/SAW.md) || diff --git a/labs/CryptoProofs/CryptoProofsAnswers.md b/labs/CryptoProofs/CryptoProofsAnswers.md index cf5290e7..cb8a2959 100644 --- a/labs/CryptoProofs/CryptoProofsAnswers.md +++ b/labs/CryptoProofs/CryptoProofsAnswers.md @@ -477,3 +477,4 @@ https://github.com/weaversa/cryptol-course/issues || [+ Salsa20 Properties](../Salsa20/Salsa20Props.md) || || [+ Transposition Ciphers](../Transposition/Contents.md) || || [+ Project Euler](../ProjectEuler/ProjectEuler.md) || +|| [+ Continuous Reasoning with SAW](../SAW/SAW.md) || From 448eeb8e869e66d023810cd8f49c73b3a73233a5 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 09:00:47 -0400 Subject: [PATCH 079/120] reworking solution for more clarity --- labs/CryptoProofs/CryptoProofsAnswers.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/labs/CryptoProofs/CryptoProofsAnswers.md b/labs/CryptoProofs/CryptoProofsAnswers.md index cb8a2959..e32778a5 100644 --- a/labs/CryptoProofs/CryptoProofsAnswers.md +++ b/labs/CryptoProofs/CryptoProofsAnswers.md @@ -228,9 +228,6 @@ matched_pt = join "tootough" matched_ct = 0x95d07f8a72707733 ``` -To make this solvable, try it again with the first six bytes of key -provided: `0x1234567890ab`. - ```Xcryptol-session-ci-none labs::CryptoProofs::CryptoProofsAnswers> :sat \key -> DES.encrypt key matched_pt == matched_ct ``` @@ -242,6 +239,9 @@ DES keys have been broken using specialized algorithms and large amounts of compute power, but not by a single computer running a SAT solver. +To make this solvable, try it again with the first six bytes of key +provided: `0x1234567890ab`. + ```Xcryptol-session labs::CryptoProofs::CryptoProofsAnswers> :sat \key -> DES.encrypt key matched_pt == matched_ct /\ take key == 0x1234567890ab Satisfiable From c4005988eb695a345c4bb7206419be6f6d27ca37 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 09:04:26 -0400 Subject: [PATCH 080/120] in an a -> in a --- labs/CryptoProofs/CryptoProofs.md | 2 +- labs/CryptoProofs/CryptoProofsAnswers.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/labs/CryptoProofs/CryptoProofs.md b/labs/CryptoProofs/CryptoProofs.md index ba7911e9..4d34cd31 100644 --- a/labs/CryptoProofs/CryptoProofs.md +++ b/labs/CryptoProofs/CryptoProofs.md @@ -339,7 +339,7 @@ plaintext inputs. **EXERCISE**: 2.5.2 DES Parity Bits -Having equivalent keys is often considered a weakness in an a +Having equivalent keys is often considered a weakness in a cipher. However, in the case of DES, it turns out that this is a result of a design choice. The lowest bit of each byte of a DES key is actually a [parity bit](https://en.wikipedia.org/wiki/Parity_bit) that diff --git a/labs/CryptoProofs/CryptoProofsAnswers.md b/labs/CryptoProofs/CryptoProofsAnswers.md index e32778a5..2b8ed9bc 100644 --- a/labs/CryptoProofs/CryptoProofsAnswers.md +++ b/labs/CryptoProofs/CryptoProofsAnswers.md @@ -419,7 +419,7 @@ Q.E.D. **EXERCISE**: 2.5.2 DES Parity Bits -Having equivalent keys is often considered a weakness in an a +Having equivalent keys is often considered a weakness in a cipher. However, in the case of DES, it turns out that this is a result of a design choice. The lowest bit of each byte of a DES key is actually a [parity bit](https://en.wikipedia.org/wiki/Parity_bit) that From 4ed59b7ca00d628a53959ec968554f6d667b1946 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 13:33:24 +0000 Subject: [PATCH 081/120] Added explicit SAW struct example --- labs/SAW/Game/proof/Game.py | 47 ++++++++++++++++++++++++++++--------- labs/SAW/Game/src/Game.c | 22 +++++++++++++++++ labs/SAW/Game/src/Game.h | 14 ++++++++++- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 0f6bd115..0eb4eb7c 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -36,18 +36,20 @@ # Defines and Constants ####################################### -MAX_NAME_LENGTH = 12 -SUCCESS = 170 -FAILURE = 85 -MAX_STAT = 100 -SCREEN_ROWS = 15 -SCREEN_COLS = 10 -SCREEN_TILES = SCREEN_ROWS * SCREEN_COLS -NEUTRAL = 0 -DEFEAT_PLAYER = 1 -DEFEAT_OPPONENT = 2 +MAX_NAME_LENGTH = 12 +SUCCESS = 170 +FAILURE = 85 +MAX_STAT = 100 +SCREEN_ROWS = 15 +SCREEN_COLS = 10 +SCREEN_TILES = SCREEN_ROWS * SCREEN_COLS +NEUTRAL = 0 +DEFEAT_PLAYER = 1 +DEFEAT_OPPONENT = 2 ASSET_TABLE_SIZE = 16 - +GAITS = 2 +DIRECTIONS = 4 +ANIMATION_STEPS = 3 ####################################### # SAW Helper Functions @@ -133,6 +135,27 @@ def specification (self): self.returns(cry_f("`({SUCCESS}) : [32]")) +# initDefaultSprite Contract +# uint32_t initDefaultSprite(sprite_t* sprite) +class initDefaultSprite_Contract(Contract): + def specification (self): + # Declare variables + ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) + frames = self.fresh_var(ty, "sprite.frames") + xPos = self.fresh_var(i32, "sprite.xPos") + yPos = self.fresh_var(i32, "sprite.yPos") + sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(frames, xPos, yPos)) + + # Symbolically execute the function + self.execute_func(sprite_p) + + # Assert postconditions + self.points_to(sprite_p, struct(cry_f("""(zero, 1 , 2) + : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32])"""))) + + self.returns_f("`({SUCCESS}) : [32]") + + # checkStats Contract # uint32_t checkStats(character_t* character) class checkStats_Contract(Contract): @@ -519,6 +542,7 @@ def test_Game(self): # Override(s) associated with basic struct initialization initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) + initDefaultSprite_result = llvm_verify(module, 'initDefaultSprite', initDefaultSprite_Contract()) # Overrides(s) associated with preconditions and postconditions that must # be considered in SAW contracts & unit test overrides @@ -544,6 +568,7 @@ def test_Game(self): # Assert the overrides are successful self.assertIs(levelUp_result.is_success(), True) self.assertIs(initDefaultPlayer_result.is_success(), True) + self.assertIs(initDefaultSprite_result.is_success(), True) self.assertIs(checkStats_pass_result.is_success(), True) self.assertIs(checkStats_fail_result.is_success(), True) self.assertIs(resolveAttack_case1_result.is_success(), True) diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index 1ebe140f..cc3f94ca 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -42,6 +42,28 @@ uint32_t initDefaultPlayer(player_t* player) } +uint32_t initDefaultSprite(sprite_t* sprite) +{ + // Initialize the sprite frames to the default asset + for (uint8_t i = 0; i < GAITS; i++) + { + for (uint8_t j = 0; j < DIRECTIONS; j++) + { + for (uint8_t k = 0; k < ANIMATION_STEPS; k++) + { + sprite->frames[i][j][k] = 0x00; + } + } + } + + // Initialize sprite's default position + sprite->xPos = 1; + sprite->yPos = 2; + + return SUCCESS; +} + + /////////////////////////////////////// // Function(s) with preconditions and postconditions that must // be considered in SAW contracts & unit test overrides diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index 50fe595e..fb9e801f 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -11,6 +11,10 @@ #define SCREEN_COLS 10 #define SCREEN_TILES SCREEN_ROWS*SCREEN_COLS #define ASSET_TABLE_SIZE 16 +#define GAITS 2 // Possible gaits to animate: walk and run +#define DIRECTIONS 4 // 2D game, 4 directions (up, down, left, right) +#define ANIMATION_STEPS 3 // 3 frames per direction (stand, left leg forward, right leg forward) + /////////////////////////////////////// // Globals @@ -56,7 +60,7 @@ typedef struct { // Struct containing inventory information typedef struct { - //item_t* item; + //item_t* item; // Assume this points to an item_t array of unknown length item_t item[5]; uint32_t numItems; } inventory_t; @@ -66,6 +70,13 @@ typedef struct { uint8_t tiles[SCREEN_TILES]; // Holds asset ID for each screen tile } screen_t; +// Struct containing information on a character sprite +typedef struct { + uint8_t frames[GAITS][DIRECTIONS][ANIMATION_STEPS]; + uint32_t xPos; // x position relative to the screen + uint32_t yPos; // y position relative to the screen +} sprite_t; + /////////////////////////////////////// // Function prototypes @@ -76,6 +87,7 @@ uint32_t levelUp(uint32_t level); // Function(s) with basic struct initialization uint32_t initDefaultPlayer(player_t* player); +uint32_t initDefaultSprite(sprite_t* sprite); // Function(s) with preconditions and postconditions that must // be considered in SAW contracts & unit test overrides From dd3503f80357c83844b00dd791b81627b967044b Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 13:43:28 +0000 Subject: [PATCH 082/120] Simplified initDefaultPlayer_Contract --- labs/SAW/Game/proof/Game.py | 45 +++++++++++-------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 0eb4eb7c..68b658bd 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -89,48 +89,29 @@ def specification (self): # player_t is an alternative typedef name for character_t. player = self.alloc(alias_ty("struct.character_t")) + # Symbolically execute the function self.execute_func(player) - # Assert the post-condition behaviors - # Note: The following explicit points_to method is currently the only way - # to assert struct field contents. - # Index 0 = "name" field - # Recall that 0x41 is ASCII for 'A' - self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player[0], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) + # Assert the postcondition behaviors - # Index 1 is the "level" field + # Option 1: Assert one field at a time via points_to + self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) self.points_to(player['level'], cry_f("1 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player[1], cry_f("1 : [32]")) - - # Index 2 is the "hp" field self.points_to(player['hp'], cry_f("10 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player[2], cry_f("10 : [32]")) - - # Index 3 is the "atk" field self.points_to(player['atk'], cry_f("5 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player[3], cry_f("5 : [32]")) - - # Index 4 is the "def" field self.points_to(player['def'], cry_f("4 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player[4], cry_f("4 : [32]")) - - # Index 5 is the "spd" field self.points_to(player['spd'], cry_f("3 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # Note: If bitcode isn't compiled with debug symbols enabled (no "-g" flag) + # then use the field indices instead. + # self.points_to(player[0], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) + # self.points_to(player[1], cry_f("1 : [32]")) + # self.points_to(player[2], cry_f("10 : [32]")) + # self.points_to(player[3], cry_f("5 : [32]")) + # self.points_to(player[4], cry_f("4 : [32]")) # self.points_to(player[5], cry_f("3 : [32]")) - # Incorrect Alternative 1: Invalid label in record update. - # self.points_to(player, cry_f("{{ repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) - - # Incorrect Alternative 2: SAW doesn't yet support translating Cryptol's - # record type(s) into crucible-llvm's type system. - # self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) + # Option 2: Assert all of the fields to a tuple + # self.points_to(player, cry_f("( repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] )")) self.returns(cry_f("`({SUCCESS}) : [32]")) From 65ab3287b40d75763d04414458babf577b5c2702 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 14:10:10 +0000 Subject: [PATCH 083/120] Added initial explicit struct example discussion --- labs/SAW/SAW.md | 45 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 5198bf81..00d28c4f 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1211,10 +1211,51 @@ above with postcoditions ## Structs in Cryptol -We can also explicitly define a structure: +We can also explicitly define a structure in SAW. Let's consider another struct: +```c +#define GAITS 2 +#define DIRECTIONS 4 +#define ANIMATION_STEPS 3 + +typedef struct { + uint8_t frames[GAITS][DIRECTIONS][ANIMATION_STEPS]; + uint32_t xPos; + uint32_t yPos; +} sprite_t; ``` -example + +The idea behind the `sprite_t` struct is to hold all of the sprites associated with a character that we will present in our game. `frames` is a 3D uint8_t array where each element represents an asset identifier from our art collection. Why a 3D array? Well, we want to provide animations for our characters that way it looks like they are moving on the screen (and it's a great excuse to discuss multi-dimensional arrays in SAW). The first dimension refers to the number of gaits we want to represent, that being walking and running. The second dimension refers to the number of directions a character can face. Imagine that we are working with a 2D top down game, so we have 4 directions: up, down, left, and right. The third dimension refers to the number of frames per movement. + +Let's think about how we walk forward (feel free to try this at home). If you are walking forward, you first stand up, move one foot in front of the other, place that foot down, lift up your back foot, and move that back foot ahead of your front foot. Rinse and repeat, and you'll be walking 'cross the floor. + +Now that's a lot of steps! We can simplify this process by only considering 3 frames: standing up, left foot in front, and right foot in front. We can then reuse the standing up frame as a transition between the left foot forward and the right foot forward frames. + +Alright, that's enough of a crash course in animation. Let's get back to our struct. We have two more fields: xPos and yPos. These are simply positional references for where a character is located relative to the screen. + +With that understanding, let's consider a function that uses the `sprite_t` struct: + +```c +uint32_t initDefaultSprite(sprite_t* sprite) +{ + // Initialize the sprite frames to the default asset + for (uint8_t i = 0; i < GAITS; i++) + { + for (uint8_t j = 0; j < DIRECTIONS; j++) + { + for (uint8_t k = 0; k < ANIMATION_STEPS; k++) + { + sprite->frames[i][j][k] = 0x00; + } + } + } + + // Initialize sprite's default position + sprite->xPos = 1; + sprite->yPos = 2; + + return SUCCESS; +} ``` //Cryptol interprets structs as tuples From 0ad4d081a2c2514d36f2ea3f47b9682415a34967 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 14:27:30 +0000 Subject: [PATCH 084/120] Updated explicit structs example --- labs/SAW/SAW.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 00d28c4f..f487ae6d 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1258,6 +1258,32 @@ uint32_t initDefaultSprite(sprite_t* sprite) } ``` +Now, let's go ahead and make a contract to represent this function: + +```python +class initDefaultSprite_Contract(Contract): + def specification (self): + # Declare variables + ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) + frames = self.fresh_var(ty, "sprite.frames") + xPos = self.fresh_var(i32, "sprite.xPos") + yPos = self.fresh_var(i32, "sprite.yPos") + sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(frames, xPos, yPos)) + + # Symbolically execute the function + self.execute_func(sprite_p) + + # Assert postconditions + self.points_to(sprite_p, struct(cry_f("""(zero, 1 , 2) + : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32])"""))) + + self.returns_f("`({SUCCESS}) : [32]") +``` + +Notice that for explicit structs, we declare variables that represent the types for each struct field. We then connect them to our allocated `sprite_t` pointer, `sprite_p`, using `points_to = struct(...)`. This input to `alloc` asserts the precondition that `sprite_p` points to these SAW-defined variables. If we wanted to, we could then assert other preconditions on `frames`, `xPos`, and `yPos` using `precondition_f` if desired. We don't do so in this example contract, but it's still a feature to consider! + +Also notice how we assert the `points_to` postcondition for `sprite_p`. We use `struct` again, but this time to assert that `sprite_p` points to a Cryptol-defined tuple. Relating this back to our `initDefaultPlayer_Contract` example, we can see that this one assertion simplifies our postcondition compared to checking each field at a time. As a side note, we use 3 double quotes (""") for our `cry_f` call. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines so to improve code readability. Python considers the 3 double quotes as a multiline string. While multiline strings may be used as block comments in Python, it is perfect for us to use here given that `cry_f ` accepts an input string. + //Cryptol interprets structs as tuples //But what if a struct has a pointer as a field...? From e80e1c336bc63650a5ce3aba816960bcff4e966c Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 14:35:24 +0000 Subject: [PATCH 085/120] Updated SAW struct initialization & explicit structs section --- labs/SAW/SAW.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index f487ae6d..58533e64 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1124,8 +1124,6 @@ where `SUCCESS` and `MAX_NAME_LENGTH` are C constants #define FAILURE 85 ``` -## Struct Initialization and Explicit Structs - The following constract will verify our initialization: ```python @@ -1209,9 +1207,9 @@ Alternatively, we could use post conditions above with postcoditions ``` -## Structs in Cryptol +### Explicit Structs -We can also explicitly define a structure in SAW. Let's consider another struct: +We can also explicitly define a struct in SAW. Let's consider another struct: ```c #define GAITS 2 @@ -1284,6 +1282,8 @@ Notice that for explicit structs, we declare variables that represent the types Also notice how we assert the `points_to` postcondition for `sprite_p`. We use `struct` again, but this time to assert that `sprite_p` points to a Cryptol-defined tuple. Relating this back to our `initDefaultPlayer_Contract` example, we can see that this one assertion simplifies our postcondition compared to checking each field at a time. As a side note, we use 3 double quotes (""") for our `cry_f` call. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines so to improve code readability. Python considers the 3 double quotes as a multiline string. While multiline strings may be used as block comments in Python, it is perfect for us to use here given that `cry_f ` accepts an input string. +### Structs in Cryptol + //Cryptol interprets structs as tuples //But what if a struct has a pointer as a field...? From 076c079f1b3f8e08e9545bbdb5b6e6f87a53ccdf Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Thu, 2 Jun 2022 10:41:05 -0400 Subject: [PATCH 086/120] Update SAW.md --- labs/SAW/SAW.md | 37 +------------------------------------ 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 58533e64..c6864765 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1734,7 +1734,7 @@ class resolveAttack_Contract(Contract): Would this contract pass verification? Absolutely. Given that the `MAX_STAT` preconditions limits our input values, we would never see SAW's counterexample of an integer overflow/underflow from case 2. - +## Using Structs in Specifications # Using Gained Knowledge @@ -1822,41 +1822,6 @@ See [SMT: Equality Logic With Uninterpreted Functions](https://www21.in.tum.de/t how uninterpreted functions and constraints are applied to Satisfiability Modulo Theories. -//add in use when function complicated -//add uninterpreting may cause failure (logic needed sometimes). This can be avoided sometimes by making sure for every function you are uninterpreting you are also passing in a corresponding lemma - - -# Advanced Topics and Exercises - -//Dj add stuff - -## Global Variables - -//DJ add stuff - -//maybe something with `extern` - -### Nested DEFINE Statements - -// SAWs limitations -// Worse case: edit the source code - -## Aliasing - -// Talk about in general what aliasing is -// DJ add stuff (specifically with aliases) -// Talk about SAW limitations and lemmas - -### Wrapper Functions - -// Worse case scenario: editing the source code - -## Large Data - -// Dj add the stuff about inputting in a big array - -## Capstone - # Solicitation How was your experience with this lab? Suggestions are welcome in the From 1aacca28033092495be8c94dd52075f6478031cc Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 10:55:18 -0400 Subject: [PATCH 087/120] Testing a new workflow for the SAW python tutorial --- .github/workflows/python-saw.yml | 19 ++++++++++++++- labs/SAW/SAW.md | 42 +++++++++++++++++--------------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/.github/workflows/python-saw.yml b/.github/workflows/python-saw.yml index 059f3765..c0b96cbf 100644 --- a/.github/workflows/python-saw.yml +++ b/.github/workflows/python-saw.yml @@ -36,4 +36,21 @@ jobs: # Start saw-remote-api server - run: start-saw-remote-api-read-only # Run Salsa20 Python SAW Script - - run: python3 labs/Demos/SAW/Salsa20/proof/salsa20.py \ No newline at end of file + - run: python3 labs/Demos/SAW/Salsa20/proof/salsa20.py + + saw-tutorial: + runs-on: ubuntu-latest + container: + image: ghcr.io/weaversa/cryptol-course:2.12 + options: --user root + steps: + - name: Checkout + uses: actions/checkout@v2 + # Start saw-remote-api server + - run: start-saw-remote-api-read-only + # Run final rotl SAW Script + - run: cd labs/SAW/proof && clang ../src/rotl3.c -o ../src/rotl.bc -c -emit-llvm && python3 rotl.py + # Run addRow SAW Script + - run: cd labs/SAW/proof && clang ../src/addRow.c -o ../src/addRow.bc -c -emit-llvm && python3 addRow.py + # Run null SAW Script + - run: cd labs/SAW/proof && clang ../src/null.c -o ../src/null.bc -c -emit-llvm && python3 null.py diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 5198bf81..f7fdd596 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -333,7 +333,7 @@ or else the python script won't do anything! We can now run the proof script. -``` +```sh $ cd labs/SAW/proof $ python3 rotl.py [03:08:29.986] Verifying rotl ... @@ -826,8 +826,8 @@ What do you think will happen if we run this code? Click here to find out! Running the code, SAW verifies the first two contracts - ``` - $ python3 addRow.py + ```sh + $ clang ../src/addRow.c -o ../src/addRow.bc -c -emit-llvm && python3 addRow.py [15:40:51.330] Verifying addRow5Mutate ... [15:40:51.330] Simulating addRow5Mutate ... [15:40:51.335] Checking proof obligations addRow5Mutate ... @@ -916,7 +916,7 @@ What do you think will happen if we run this code? And now SAW happily verifies the third contract! - ``` + ```sh $ python3 addRow.py ✅ Verified: lemma_addRow5Mutate_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/addRow.py:64) ✅ Verified: lemma_addRow5NewVar_Contract (defined at /home/cryptol/cryptol-course/labs/SAW/proof/addRow.py:67) @@ -1018,8 +1018,8 @@ if __name__ == "__main__": It turns out the contract above will fail! - ``` - $ python3 null.py + ```sh + $ clang ../src/null.c -o ../src/null.bc -c -emit-llvm && python3 null.py [17:21:44.701] Verifying isNull ... [17:21:44.701] Simulating isNull ... [17:21:44.703] Checking proof obligations isNull ... @@ -1040,7 +1040,7 @@ if __name__ == "__main__": However, if **every** setting is a counterexample, then this is telling us the pointer must have been the null pointer! An initialized symbolic pointer that hasn't been assigned a symbolic variable to point to is **NOT** equivalent to a null pointer in SAW. We can use `null()` in situations where we want a null pointer. For example, if we change the contract above to - ``` + ```python class FContract(Contract): def specification(self): self.execute_func(null()) @@ -1050,7 +1050,7 @@ if __name__ == "__main__": then SAW is a happy. - ``` + ```sh $ python3 null.py [17:33:50.802] Verifying isNull ... [17:33:50.802] Simulating isNull ... @@ -1157,9 +1157,11 @@ Here, we assert that the `name` field of the player pointer points to a value sp If we didn't have the debug symbols in the bitcode, SAW would throw us an error like so: -``` -clang -c -emit-llvm -o artifacts/Game.bc src/Game.c -python3 proof/Game.py +```sh +$ cd labs/SAW/Game +$ mkdir artifacts +$ clang -c -emit-llvm -o artifacts/Game.bc src/Game.c +$ python3 proof/Game.py ⚠️ Failed to verify: lemma_initDefaultPlayer_Contract (defined at proof/Game.py:513): error: Unable to resolve struct field name: '"name"' Could not resolve setup value debug information into a struct type. @@ -1362,9 +1364,9 @@ if __name__ == "__main__": Excellent, now for the moment of truth! -``` -clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c -python3 proof/Game.py +```sh +$ clang -c -emit-llvm -o artifacts/Game.bc src/Game.c +$ python3 proof/Game.py [01:59:53.576] Verifying resolveAttack ... [01:59:53.577] Simulating resolveAttack ... [01:59:53.580] Checking proof obligations resolveAttack ... @@ -1477,9 +1479,9 @@ Fine, let's toss out our fancy math skills and write our contact's precondition self.precondition_f("{target}.2 <= ({atk} - {target}.4)") ``` -``` -clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c -python3 proof/Game.py +```sh +$ clang -c -emit-llvm -o artifacts/Game.bc src/Game.c +$ python3 proof/Game.py [02:34:39.387] Verifying resolveAttack ... [02:34:39.388] Simulating resolveAttack ... [02:34:39.391] Checking proof obligations resolveAttack ... @@ -1569,9 +1571,9 @@ This means our precondition for case 2 is lacking something. Let's adjust it in self.precondition_f("{target}.2 <= ({atk} - {target}.4)") ``` -``` -clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c -python3 proof/Game.py +```sh +$ clang -c -emit-llvm -o artifacts/Game.bc src/Game.c +$ python3 proof/Game.py [02:55:30.437] Verifying resolveAttack ... [02:55:30.437] Simulating resolveAttack ... [02:55:30.440] Checking proof obligations resolveAttack ... From 82b7e4294034310b277b9b3f63c39025ae14ac47 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 14:55:29 +0000 Subject: [PATCH 088/120] Updated Cryptol struct discussion --- labs/SAW/Game/proof/Game.py | 8 ++++-- labs/SAW/SAW.md | 50 +++++++++++++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 68b658bd..466eb7ac 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -131,8 +131,13 @@ def specification (self): self.execute_func(sprite_p) # Assert postconditions - self.points_to(sprite_p, struct(cry_f("""(zero, 1 , 2) + self.points_to(sprite_p, struct(cry_f("""(zero, 1, 2) : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32])"""))) + # Pop Quiz: Why does this assertion fail? + # self.points_to(sprite_p, struct(cry_f(""" + # {{ frames = zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], + # xPos = 1 : [32], + # yPos = 2 : [32]}}"""))) self.returns_f("`({SUCCESS}) : [32]") @@ -565,7 +570,6 @@ def test_Game(self): self.assertIs(setScreenTile_fail_result.is_success(), True) self.assertIs(resetInventoryItems_result.is_success(), True) - if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 58533e64..929eba5a 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1272,7 +1272,7 @@ class initDefaultSprite_Contract(Contract): self.execute_func(sprite_p) # Assert postconditions - self.points_to(sprite_p, struct(cry_f("""(zero, 1 , 2) + self.points_to(sprite_p, struct(cry_f("""(zero, 1, 2) : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32])"""))) self.returns_f("`({SUCCESS}) : [32]") @@ -1284,7 +1284,53 @@ Also notice how we assert the `points_to` postcondition for `sprite_p`. We use ` ### Structs in Cryptol -//Cryptol interprets structs as tuples +Cryptol interprets structs as tuples. In our `initDefaultSprite_Contract` example, we asserted the following Cryptol tuple: + +``` +(zero, 1, 2) : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32]) +``` + +which simplifies to: + +``` +(zero, 1, 2) : ([2][4][3][8], [32], [32]) +``` + +While Cryptol's record types could also represent structs, SAW does not currently support translating Cryptol's record types into crucible-llvm's type system. So if we tried to use the following postcondition with a Cryptol record: + +```python +self.points_to(sprite_p, struct(cry_f(""" + {{ framzero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], + xPos = 1 : [32], + yPos = 2 : [32]}}"""))) +``` + +SAW would return this error: + +``` +clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c +python3 proof/Game.py +⚠️ Failed to verify: lemma_initDefaultSprite_Contract (defined at proof/Game.py:530): +error: SAW doesn't yet support translating Cryptol's record type(s) into crucible-llvm's type system. + stdout: + +F +====================================================================== +FAIL: test_Game (__main__.GameTests) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "proof/Game.py", line 556, in test_Game + self.assertIs(initDefaultSprite_result.is_success(), True) +AssertionError: False is not True + +---------------------------------------------------------------------- +Ran 1 test in 0.878s + +FAILED (failures=1) +🛑 The goal failed to verify. +make: *** [Makefile:14: all] Error 1 +``` + //But what if a struct has a pointer as a field...? ## Exercise: Resolving an Attack! From a0b9aa58112ac5f0f0927c2603d788a1f4598e82 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 12:04:21 -0400 Subject: [PATCH 089/120] fixed ci --- labs/SAW/proof/addRow.py | 6 ++++-- labs/SAW/proof/null.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/labs/SAW/proof/addRow.py b/labs/SAW/proof/addRow.py index a2fe0164..5c9a54d1 100644 --- a/labs/SAW/proof/addRow.py +++ b/labs/SAW/proof/addRow.py @@ -1,3 +1,4 @@ +import os import unittest from saw_client import * from saw_client.crucible import * @@ -55,8 +56,9 @@ def test_rowAdds(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) - bcname = "../src/addRow.bc" - cryname = "spec/addRow.cry" + pwd = os.getcwd() + bcname = pwd + "/../src/addRow.bc" + cryname = pwd + "/spec/addRow.cry" cryptol_load_file(cryname) mod = llvm_load_module(bcname) diff --git a/labs/SAW/proof/null.py b/labs/SAW/proof/null.py index 749602e0..95968502 100644 --- a/labs/SAW/proof/null.py +++ b/labs/SAW/proof/null.py @@ -1,9 +1,9 @@ +import os import unittest from saw_client import * from saw_client.crucible import * from saw_client.llvm import * - class isNull_Contract(Contract): def specification(self): # p = self.alloc(i32) @@ -16,7 +16,9 @@ class LLVMAssertNullTest(unittest.TestCase): def test_llvm_assert_null(self): connect(reset_server=True) if __name__ == "__main__": view(LogResults(verbose_failure=True)) - bcname = '../src/null.bc' + + pwd = os.getcwd() + bcname = pwd + "/../src/null.bc" mod = llvm_load_module(bcname) result = llvm_verify(mod, 'isNull', isNull_Contract()) From 79d6eaff5e082ade6a78c260b3b04f7845763e02 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Thu, 2 Jun 2022 12:39:22 -0400 Subject: [PATCH 090/120] Update SAW.md --- labs/SAW/SAW.md | 116 +++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index bdf5757c..9e81d3a3 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1074,9 +1074,9 @@ This example is from [here]() In this section we learn how to verify code involving structs and global variables by analyzing a game. The code for the game can be found [here](./Game/src/). -### Struct Initialization +## Struct Initialization -The game defines the following player type: +The game defines the following player type. ```C typedef struct { @@ -1091,7 +1091,7 @@ typedef struct { typedef character_t player_t; ``` -The following function will initialize a player. +A player is initialized with some default values ```C uint32_t initDefaultPlayer(player_t* player) @@ -1116,7 +1116,7 @@ uint32_t initDefaultPlayer(player_t* player) } ``` -where `SUCCESS` and `MAX_NAME_LENGTH` are C constants +where `SUCCESS` and `MAX_NAME_LENGTH` are C constants: ```C #define MAX_NAME_LENGTH 12 @@ -1124,12 +1124,16 @@ where `SUCCESS` and `MAX_NAME_LENGTH` are C constants #define FAILURE 85 ``` -The following constract will verify our initialization: +We use the following contract to verify this initialization function: ```python +MAX_NAME_LENGTH = 12 +SUCCESS = 170 +FAILURE = 85 + class initDefaultPlayer_Contract(Contract): def specification (self): - player = self.alloc(alias_ty("struct.character_t")) + player = self.alloc(alias_ty("struct.player_t")) self.execute_func(player) @@ -1143,17 +1147,32 @@ class initDefaultPlayer_Contract(Contract): self.returns(cry_f("`({SUCCESS}) : [32]")) ``` -The command `alias_ty("struct.")` creates a type corresponding to the structure, so `player = self.alloc(alias_ty("struct.character_t"))` just creates a symbolic pointer variable `player` pointing to a structure of type `character_t`. Even though the function's input parameter is `player_t`, we need to pass `character_t` to `alias_ty` given that `player_t` is just a typedef for `character_t`. +For every C symbol defined using `#define` we make a corresponding Python global variable. -Let's breakdown a `points_to` command seen above: +``` +MAX_NAME_LENGTH = 12 +SUCCESS = 170 +FAILURE = 85 +``` -```python -self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) +The command `alias_ty("struct.")` creates a type corresponding to the structure, e.g., + +```python3 +player = self.alloc(alias_ty("struct.player_t")) ``` +creates a symbolic pointer variable `player` pointing to a structure of type `player_t`. + +Let's breakdown a `points_to` command seen above: + -Here, we assert that the `name` field of the player pointer points to a value specified a Cryptol expression. Notice that we use the string associated with the struct field, `name`, to access our target struct index. We are able to use strings instead of remembering the index of every field when debug symbols are included in the generated bitcode. For the full compilation details, check out the [Makefile](./Game/Makefile) associated with the `Game` directory. However, the `-g` clang flag is what tells the compiler to include the field names of the structs in the bitcode. +| `self.points_to(` | `player` | `['name']` | `,` | `cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")` | `)` | +|-------------------|-----------|----------|------|-------------------------------------------------|-----| +| Assert in the current contract that the following pointer | with this name | points to a struct with this named field | and the value of that field is | this expression | . | -If we didn't have the debug symbols in the bitcode, SAW would throw us an error like so: + +Above we use strings to reference fields of structures. However, we can only do this when strings are present in the bitcode, e.g., when debug symbols are included in the generated bitcode. The `-g` clang flag tells the compiler to include the field names of the structs in the bitcode. For the full compilation details, check out the [Makefile](./Game/Makefile) associated with the `Game` directory. + +If we didn't have the debug symbols in the bitcode, SAW would produce an error: ```sh $ cd labs/SAW/Game @@ -1164,27 +1183,9 @@ $ python3 proof/Game.py error: Unable to resolve struct field name: '"name"' Could not resolve setup value debug information into a struct type. Perhaps you need to compile with debug symbols enabled. - - stdout: - -F -====================================================================== -FAIL: test_Game (__main__.GameTests) ----------------------------------------------------------------------- -Traceback (most recent call last): - File "proof/Game.py", line 514, in test_Game - self.assertIs(initDefaultPlayer_result.is_success(), True) -AssertionError: False is not True - ----------------------------------------------------------------------- -Ran 1 test in 0.865s - -FAILED (failures=1) -🛑 The goal failed to verify. -make: *** [Makefile:14: all] Error 1 ``` -As we already mentioned, adding the `-g` flag will resolve the error. However, what if we didn't want to include debug symbols in the bitcode, but still wanted to verify our contract? Well, we can reference the struct fields by using their corresponding indices like so: +If we didn't want to include debug symbols in the bitcode, then we can reference the struct fields by using their corresponding indices: ```python class initDefaultPlayer_Contract(Contract): @@ -1203,13 +1204,7 @@ class initDefaultPlayer_Contract(Contract): self.returns(cry_f("`({SUCCESS}) : [32]")) ``` -Alternatively, we could use post conditions - -``` -above with postcoditions -``` - -### Explicit Structs +## Explicit Structs We can also explicitly define a struct in SAW. Let's consider another struct: @@ -1264,39 +1259,42 @@ Now, let's go ahead and make a contract to represent this function: class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables - ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) - frames = self.fresh_var(ty, "sprite.frames") - xPos = self.fresh_var(i32, "sprite.xPos") - yPos = self.fresh_var(i32, "sprite.yPos") - sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(frames, xPos, yPos)) + ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) + frames = self.fresh_var(ty, "sprite.frames") + xPos = self.fresh_var(i32, "sprite.xPos") + yPos = self.fresh_var(i32, "sprite.yPos") + sprite = struct(frames, xPos, yPos) + sprite_p = self.alloc(alias_ty("struct.sprite_t")) + self.points_to(sprite_p, sprite) # Symbolically execute the function self.execute_func(sprite_p) # Assert postconditions - self.points_to(sprite_p, struct(cry_f("""(zero, 1, 2) - : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32])"""))) + self.points_to(sprite_p, cry_f("""(zero, 1, 2) + : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32])""")) self.returns_f("`({SUCCESS}) : [32]") ``` -Notice that for explicit structs, we declare variables that represent the types for each struct field. We then connect them to our allocated `sprite_t` pointer, `sprite_p`, using `points_to = struct(...)`. This input to `alloc` asserts the precondition that `sprite_p` points to these SAW-defined variables. If we wanted to, we could then assert other preconditions on `frames`, `xPos`, and `yPos` using `precondition_f` if desired. We don't do so in this example contract, but it's still a feature to consider! - -Also notice how we assert the `points_to` postcondition for `sprite_p`. We use `struct` again, but this time to assert that `sprite_p` points to a Cryptol-defined tuple. Relating this back to our `initDefaultPlayer_Contract` example, we can see that this one assertion simplifies our postcondition compared to checking each field at a time. As a side note, we use 3 double quotes (""") for our `cry_f` call. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines so to improve code readability. Python considers the 3 double quotes as a multiline string. While multiline strings may be used as block comments in Python, it is perfect for us to use here given that `cry_f ` accepts an input string. - -### Structs in Cryptol - -Cryptol interprets structs as tuples. In our `initDefaultSprite_Contract` example, we asserted the following Cryptol tuple: +Like `array`, the `struct` keyword declares a symbolic struct given a variable for each struct field. We assert the precondition that our pointer points to this symbolic struct. Alternatively, we could replace +```python + sprite = struct(frames, xPos, yPos) + sprite_p = self.alloc(alias_ty("struct.sprite_t")) + self.points_to(sprite_p, sprite) ``` -(zero, 1, 2) : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32]) + with +```python +sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(frames, xPos, yPos)) ``` +since we don't use `sprite` later in the code. If we wanted, we could assert other preconditions on `frames`, `xPos`, and `yPos` using `precondition_f`. We don't in this example, but it's still a feature to consider! -which simplifies to: +In the post condition, we assert `sprite_p` points to some concrete structure. Cryptol interprets symbolic structs as tuples. Contrast this simplistic postcondition to our previous `initDefaultPlayer_Contract` example, where we had to check each field one at a time. -``` -(zero, 1, 2) : ([2][4][3][8], [32], [32]) -``` +We use 3 double quotes `"""` for our `cry_f` call. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines to improve code readability. Python considers the 3 double quotes as a multiline string. Multiline strings may also be used as block comments in Python. + +### Cryptol Records and Structs While Cryptol's record types could also represent structs, SAW does not currently support translating Cryptol's record types into crucible-llvm's type system. So if we tried to use the following postcondition with a Cryptol record: @@ -1333,7 +1331,7 @@ FAILED (failures=1) make: *** [Makefile:14: all] Error 1 ``` -//But what if a struct has a pointer as a field...? +If a Cryptol specification uses a record type to represent structs, then we can define a Python helper function for wrapping. ## Exercise: Resolving an Attack! @@ -1782,8 +1780,6 @@ class resolveAttack_Contract(Contract): Would this contract pass verification? Absolutely. Given that the `MAX_STAT` preconditions limits our input values, we would never see SAW's counterexample of an integer overflow/underflow from case 2. -## Using Structs in Specifications - # Using Gained Knowledge ## Assumptions and Lemmas From 2e2ecf4547d0d41cf5aceddd8d3145952c782248 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 17:11:28 +0000 Subject: [PATCH 091/120] Modified sprite example to contain a pointer field --- labs/SAW/Game/proof/Game.py | 17 ++++++++--------- labs/SAW/Game/src/Game.c | 5 ++++- labs/SAW/Game/src/Game.h | 3 ++- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 466eb7ac..2f4d0bf5 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -121,23 +121,22 @@ def specification (self): class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables + character_p = self.alloc(alias_ty("struct.character_t")) + tempCharacter_p = self.alloc(alias_ty("struct.character_t")) ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) frames = self.fresh_var(ty, "sprite.frames") xPos = self.fresh_var(i32, "sprite.xPos") yPos = self.fresh_var(i32, "sprite.yPos") - sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(frames, xPos, yPos)) + sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) # Symbolically execute the function - self.execute_func(sprite_p) + self.execute_func(character_p, sprite_p) # Assert postconditions - self.points_to(sprite_p, struct(cry_f("""(zero, 1, 2) - : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32])"""))) - # Pop Quiz: Why does this assertion fail? - # self.points_to(sprite_p, struct(cry_f(""" - # {{ frames = zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], - # xPos = 1 : [32], - # yPos = 2 : [32]}}"""))) + self.points_to(sprite_p, struct( character_p, + cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), + cry_f("1 : [32]"), + cry_f("2 : [32]") )) self.returns_f("`({SUCCESS}) : [32]") diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index cc3f94ca..8dcc9fda 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -42,8 +42,11 @@ uint32_t initDefaultPlayer(player_t* player) } -uint32_t initDefaultSprite(sprite_t* sprite) +uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) { + // Initialize the character to the passed pointer + sprite->character = character; + // Initialize the sprite frames to the default asset for (uint8_t i = 0; i < GAITS; i++) { diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index fb9e801f..e646b22c 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -72,6 +72,7 @@ typedef struct { // Struct containing information on a character sprite typedef struct { + character_t* character; uint8_t frames[GAITS][DIRECTIONS][ANIMATION_STEPS]; uint32_t xPos; // x position relative to the screen uint32_t yPos; // y position relative to the screen @@ -87,7 +88,7 @@ uint32_t levelUp(uint32_t level); // Function(s) with basic struct initialization uint32_t initDefaultPlayer(player_t* player); -uint32_t initDefaultSprite(sprite_t* sprite); +uint32_t initDefaultSprite(character_t* character, sprite_t* sprite); // Function(s) with preconditions and postconditions that must // be considered in SAW contracts & unit test overrides From e1a7da77f4ae2bf9d958a80e938f0c531c746030 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 17:40:26 +0000 Subject: [PATCH 092/120] Updated initDefaultSprite example based on added pointer field --- labs/SAW/SAW.md | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 9e81d3a3..63e86a08 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1214,13 +1214,14 @@ We can also explicitly define a struct in SAW. Let's consider another struct: #define ANIMATION_STEPS 3 typedef struct { + character_t* character; uint8_t frames[GAITS][DIRECTIONS][ANIMATION_STEPS]; uint32_t xPos; uint32_t yPos; } sprite_t; ``` -The idea behind the `sprite_t` struct is to hold all of the sprites associated with a character that we will present in our game. `frames` is a 3D uint8_t array where each element represents an asset identifier from our art collection. Why a 3D array? Well, we want to provide animations for our characters that way it looks like they are moving on the screen (and it's a great excuse to discuss multi-dimensional arrays in SAW). The first dimension refers to the number of gaits we want to represent, that being walking and running. The second dimension refers to the number of directions a character can face. Imagine that we are working with a 2D top down game, so we have 4 directions: up, down, left, and right. The third dimension refers to the number of frames per movement. +The idea behind the `sprite_t` struct is to hold all of the sprites associated with a character that we will present in our game. `character` is a pointer to a `character_t` type that the sprite is tied to. `frames` is a 3D uint8_t array where each element represents an asset identifier from our art collection. Why a 3D array? Well, we want to provide animations for our characters that way it looks like they are moving on the screen (and it's a great excuse to discuss multi-dimensional arrays in SAW). The first dimension refers to the number of gaits we want to represent, that being walking and running. The second dimension refers to the number of directions a character can face. Imagine that we are working with a 2D top down game, so we have 4 directions: up, down, left, and right. The third dimension refers to the number of frames per movement. Let's think about how we walk forward (feel free to try this at home). If you are walking forward, you first stand up, move one foot in front of the other, place that foot down, lift up your back foot, and move that back foot ahead of your front foot. Rinse and repeat, and you'll be walking 'cross the floor. @@ -1231,8 +1232,11 @@ Alright, that's enough of a crash course in animation. Let's get back to our str With that understanding, let's consider a function that uses the `sprite_t` struct: ```c -uint32_t initDefaultSprite(sprite_t* sprite) +uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) { + // Initialize the character to the passed pointer + sprite->character = character; + // Initialize the sprite frames to the default asset for (uint8_t i = 0; i < GAITS; i++) { @@ -1259,20 +1263,25 @@ Now, let's go ahead and make a contract to represent this function: class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables - ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) - frames = self.fresh_var(ty, "sprite.frames") - xPos = self.fresh_var(i32, "sprite.xPos") - yPos = self.fresh_var(i32, "sprite.yPos") - sprite = struct(frames, xPos, yPos) + character_p = self.alloc(alias_ty("struct.character_t")) + tempCharacter_p = self.alloc(alias_ty("struct.character_t")) + ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) + frames = self.fresh_var(ty, "sprite.frames") + xPos = self.fresh_var(i32, "sprite.xPos") + yPos = self.fresh_var(i32, "sprite.yPos") + sprite = struct(tempCharacter_p, frames, xPos, yPos) sprite_p = self.alloc(alias_ty("struct.sprite_t")) self.points_to(sprite_p, sprite) # Symbolically execute the function - self.execute_func(sprite_p) + self.execute_func(character_p, sprite_p) # Assert postconditions - self.points_to(sprite_p, cry_f("""(zero, 1, 2) - : ([{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], [32], [32])""")) + self.points_to(sprite_p, struct( character_p, + cry_f("""zero + : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"""), + cry_f("1 : [32]"), + cry_f("2 : [32]") )) self.returns_f("`({SUCCESS}) : [32]") ``` @@ -1280,19 +1289,19 @@ class initDefaultSprite_Contract(Contract): Like `array`, the `struct` keyword declares a symbolic struct given a variable for each struct field. We assert the precondition that our pointer points to this symbolic struct. Alternatively, we could replace ```python - sprite = struct(frames, xPos, yPos) + sprite = struct(tempCharacter_p, frames, xPos, yPos) sprite_p = self.alloc(alias_ty("struct.sprite_t")) self.points_to(sprite_p, sprite) ``` with ```python -sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(frames, xPos, yPos)) +sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) ``` -since we don't use `sprite` later in the code. If we wanted, we could assert other preconditions on `frames`, `xPos`, and `yPos` using `precondition_f`. We don't in this example, but it's still a feature to consider! +since we don't use `sprite` later in the code. If we wanted, we could assert other preconditions on `tempCharacter_p`, `frames`, `xPos`, and `yPos` using `precondition_f`. We don't in this example, but it's still a feature to consider! -In the post condition, we assert `sprite_p` points to some concrete structure. Cryptol interprets symbolic structs as tuples. Contrast this simplistic postcondition to our previous `initDefaultPlayer_Contract` example, where we had to check each field one at a time. +In the postcondition, we assert `sprite_p` points to some concrete structure. Cryptol interprets symbolic structs as tuples. Contrast this simplistic postcondition to our previous `initDefaultPlayer_Contract` example, where we had to check each field one at a time. -We use 3 double quotes `"""` for our `cry_f` call. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines to improve code readability. Python considers the 3 double quotes as a multiline string. Multiline strings may also be used as block comments in Python. +We use 3 double quotes `"""` in one of our `cry_f` calls. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines to improve code readability. Python considers the 3 double quotes as a multiline string. Multiline strings may also be used as block comments in Python. ### Cryptol Records and Structs @@ -1300,9 +1309,9 @@ While Cryptol's record types could also represent structs, SAW does not currentl ```python self.points_to(sprite_p, struct(cry_f(""" - {{ framzero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], - xPos = 1 : [32], - yPos = 2 : [32]}}"""))) + {{ frame = zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], + xPos = 1 : [32], + yPos = 2 : [32]}}"""))) ``` SAW would return this error: From 1e908cd947cc0a9926986fc08f7a512d8374a380 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 18:05:39 +0000 Subject: [PATCH 093/120] Reordered the Cryptol tuple and record discussion --- labs/SAW/Game/proof/Game.py | 9 ++++ labs/SAW/SAW.md | 98 +++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 41 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 2f4d0bf5..d1eab19a 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -113,6 +113,14 @@ def specification (self): # Option 2: Assert all of the fields to a tuple # self.points_to(player, cry_f("( repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] )")) + # Pop Quiz: Why doesn't this work? + # self.points_to(player, cry_f("""{{ name = repeat 0x41 : [{MAX_NAME_LENGTH}][8], + # level = 1 : [32], + # hp = 10 : [32], + # atk = 5 : [32], + # def = 4 : [32], + # spd = 3 : [32] }}""")) + self.returns(cry_f("`({SUCCESS}) : [32]")) @@ -569,6 +577,7 @@ def test_Game(self): self.assertIs(setScreenTile_fail_result.is_success(), True) self.assertIs(resetInventoryItems_result.is_success(), True) + if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 63e86a08..cbd8ea56 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1204,6 +1204,62 @@ class initDefaultPlayer_Contract(Contract): self.returns(cry_f("`({SUCCESS}) : [32]")) ``` + +### Structs as Cryptol Tuples and Records + +Let's go back and tidy up `initDefaultPlayer_Contract`'s postconditions. Instead of using one postcondition per field, we can rewrite the `points_to` postcondition to account for the entire struct. We do this by taking advantage of the fact that Cryptol interprets symbolic structs as tuples. + +```python +self.points_to(player, cry_f("""( repeat 0x41 : [{MAX_NAME_LENGTH}][8], + 1 : [32], + 10 : [32], + 5 : [32], + 4 : [32], + 3 : [32] )""")) +``` + +We use 3 double quotes `"""` in our `cry_f` call. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines to improve code readability. Python considers the 3 double quotes as a multiline string. Multiline strings may also be used as block comments in Python. + +While Cryptol's record types could also represent structs, SAW does not currently support translating Cryptol's record types into crucible-llvm's type system. If we tried to represent the struct as a Cryptol record like so: + +```python + self.points_to(player, cry_f("""{{ name = repeat 0x41 : [{MAX_NAME_LENGTH}][8], + level = 1 : [32], + hp = 10 : [32], + atk = 5 : [32], + def = 4 : [32], + spd = 3 : [32] }}""")) +``` + +SAW would return this error: + +``` +clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c +python3 proof/Game.py +⚠️ Failed to verify: lemma_initDefaultPlayer_Contract (defined at proof/Game.py:537): +error: SAW doesn't yet support translating Cryptol's record type(s) into crucible-llvm's type system. + stdout: + +F +====================================================================== +FAIL: test_Game (__main__.GameTests) +---------------------------------------------------------------------- +Traceback (most recent call last): + File "proof/Game.py", line 563, in test_Game + self.assertIs(initDefaultPlayer_result.is_success(), True) +AssertionError: False is not True + +---------------------------------------------------------------------- +Ran 1 test in 0.752s + +FAILED (failures=1) +🛑 The goal failed to verify. +make: *** [Makefile:14: all] Error 1 +``` + +If a Cryptol specification uses a record type to represent structs, then we can define a Python helper function for wrapping. + + ## Explicit Structs We can also explicitly define a struct in SAW. Let's consider another struct: @@ -1299,48 +1355,8 @@ sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharac ``` since we don't use `sprite` later in the code. If we wanted, we could assert other preconditions on `tempCharacter_p`, `frames`, `xPos`, and `yPos` using `precondition_f`. We don't in this example, but it's still a feature to consider! -In the postcondition, we assert `sprite_p` points to some concrete structure. Cryptol interprets symbolic structs as tuples. Contrast this simplistic postcondition to our previous `initDefaultPlayer_Contract` example, where we had to check each field one at a time. - -We use 3 double quotes `"""` in one of our `cry_f` calls. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines to improve code readability. Python considers the 3 double quotes as a multiline string. Multiline strings may also be used as block comments in Python. - -### Cryptol Records and Structs - -While Cryptol's record types could also represent structs, SAW does not currently support translating Cryptol's record types into crucible-llvm's type system. So if we tried to use the following postcondition with a Cryptol record: - -```python -self.points_to(sprite_p, struct(cry_f(""" - {{ frame = zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], - xPos = 1 : [32], - yPos = 2 : [32]}}"""))) -``` - -SAW would return this error: - -``` -clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c -python3 proof/Game.py -⚠️ Failed to verify: lemma_initDefaultSprite_Contract (defined at proof/Game.py:530): -error: SAW doesn't yet support translating Cryptol's record type(s) into crucible-llvm's type system. - stdout: - -F -====================================================================== -FAIL: test_Game (__main__.GameTests) ----------------------------------------------------------------------- -Traceback (most recent call last): - File "proof/Game.py", line 556, in test_Game - self.assertIs(initDefaultSprite_result.is_success(), True) -AssertionError: False is not True - ----------------------------------------------------------------------- -Ran 1 test in 0.878s - -FAILED (failures=1) -🛑 The goal failed to verify. -make: *** [Makefile:14: all] Error 1 -``` +In the postcondition, we assert `sprite_p` points to some concrete structure. The benefit of using explicit structs is that it allows us to represent pointer fields that may be present in a struct. -If a Cryptol specification uses a record type to represent structs, then we can define a Python helper function for wrapping. ## Exercise: Resolving an Attack! From af31bcc23874671fca193c291db22b7154953bc6 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 18:10:09 +0000 Subject: [PATCH 094/120] Updated SAW.md --- labs/SAW/SAW.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index cbd8ea56..0570e360 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1334,8 +1334,7 @@ class initDefaultSprite_Contract(Contract): # Assert postconditions self.points_to(sprite_p, struct( character_p, - cry_f("""zero - : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"""), + cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), cry_f("1 : [32]"), cry_f("2 : [32]") )) @@ -1353,7 +1352,7 @@ Like `array`, the `struct` keyword declares a symbolic struct given a variable f ```python sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) ``` -since we don't use `sprite` later in the code. If we wanted, we could assert other preconditions on `tempCharacter_p`, `frames`, `xPos`, and `yPos` using `precondition_f`. We don't in this example, but it's still a feature to consider! +since we don't use `sprite` later in the code. If we wanted, we could assert other preconditions on `tempCharacter_p`, `frames`, `xPos`, and `yPos``. We don't in this example, but it's still a feature to consider! In the postcondition, we assert `sprite_p` points to some concrete structure. The benefit of using explicit structs is that it allows us to represent pointer fields that may be present in a struct. From e205aa16c33adc78cff089edc03fbfeaa5873e05 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 18:18:07 +0000 Subject: [PATCH 095/120] Updated SAW.md --- labs/SAW/SAW.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 0570e360..50612411 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1223,12 +1223,12 @@ We use 3 double quotes `"""` in our `cry_f` call. This technique is handy when w While Cryptol's record types could also represent structs, SAW does not currently support translating Cryptol's record types into crucible-llvm's type system. If we tried to represent the struct as a Cryptol record like so: ```python - self.points_to(player, cry_f("""{{ name = repeat 0x41 : [{MAX_NAME_LENGTH}][8], - level = 1 : [32], - hp = 10 : [32], - atk = 5 : [32], - def = 4 : [32], - spd = 3 : [32] }}""")) +self.points_to(player, cry_f("""{{ name = repeat 0x41 : [{MAX_NAME_LENGTH}][8], + level = 1 : [32], + hp = 10 : [32], + atk = 5 : [32], + def = 4 : [32], + spd = 3 : [32] }}""")) ``` SAW would return this error: From 0f2a20bec0abe7b27988dd15c493e9e0103db35e Mon Sep 17 00:00:00 2001 From: djmorel <35698075+djmorel@users.noreply.github.com> Date: Thu, 2 Jun 2022 14:54:23 -0400 Subject: [PATCH 096/120] Update SAW.md --- labs/SAW/SAW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 50612411..3dc7e135 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -191,7 +191,7 @@ string). To use Python variables in scope within the string use Sometimes we don't want to return a Cryptol term. In those cases we can just use `returns(someSetupValue)`. The specification function of a Contract must **always** have a `self.returns(someSetupValue)` or -`self.returns_f(string)` statement. If the function returns `void` one +`self.returns_f(string)` statement. If the function returns `void`, one can use `self.returns(void)`. ### Terms from Cryptol From 4c44e4a0217b9651709c8915944868f75069dfe2 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 19:29:26 +0000 Subject: [PATCH 097/120] Added a full tuple method to represent struct pointer fields --- labs/SAW/Game/proof/Game.py | 11 +++++++++-- labs/SAW/SAW.md | 26 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index d1eab19a..12d91ae4 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -129,7 +129,8 @@ def specification (self): class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables - character_p = self.alloc(alias_ty("struct.character_t")) + character_p = self.alloc(alias_ty("struct.character_t")) # For Option 1 + #character_p = self.fresh_var(ptr_ty(alias_ty("struct.character_t"))) # For Option 2 tempCharacter_p = self.alloc(alias_ty("struct.character_t")) ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) frames = self.fresh_var(ty, "sprite.frames") @@ -141,11 +142,17 @@ def specification (self): self.execute_func(character_p, sprite_p) # Assert postconditions + # Option 1: Struct Strategy self.points_to(sprite_p, struct( character_p, cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), cry_f("1 : [32]"), cry_f("2 : [32]") )) - + # Option 2: Full Tuple Strategy + #self.points_to(sprite_p, cry_f("""( {character_p}, + # zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], + # 1 : [32], + # 2 : [32]) """)) + self.returns_f("`({SUCCESS}) : [32]") diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 50612411..10621195 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1356,6 +1356,32 @@ since we don't use `sprite` later in the code. If we wanted, we could assert oth In the postcondition, we assert `sprite_p` points to some concrete structure. The benefit of using explicit structs is that it allows us to represent pointer fields that may be present in a struct. +However, explicit structs isn't the only way to represent pointer fields. We could also use a tuple to assert our postcondition. However, we will need to change our definition for `character_p`. + +```python +class initDefaultSprite_Contract(Contract): + def specification (self): + # Declare variables + character_p = self.fresh_var(ptr_ty(alias_ty("struct.character_t"))) + tempCharacter_p = self.alloc(alias_ty("struct.character_t")) + ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) + frames = self.fresh_var(ty, "sprite.frames") + xPos = self.fresh_var(i32, "sprite.xPos") + yPos = self.fresh_var(i32, "sprite.yPos") + sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) + + # Symbolically execute the function + self.execute_func(character_p, sprite_p) + + # Assert postconditions + self.points_to(sprite_p, cry_f("""( {character_p}, + zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], + 1 : [32], + 2 : [32]) """)) + + self.returns_f("`({SUCCESS}) : [32]") +``` + ## Exercise: Resolving an Attack! From 2fa641beb81e5510d014e299d70dc3f7b398ecfb Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 20:10:45 +0000 Subject: [PATCH 098/120] Updated SAW.md --- labs/SAW/SAW.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 323ef65d..03ed8b04 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -60,6 +60,7 @@ class contractName(Contract): def specification(self): # Initialization and Preconditions # Execute Function + # Initialization for output variables (optional) # Postconditions and Return class testName(unittest.TestCase): @@ -1495,7 +1496,11 @@ class resolveAttack_Contract(Contract): self.returns(void) ``` -Our contract is looking pretty good now! Let's set up our unit test: +Our contract is looking pretty good now! Notice that we use `{target}.2` and `{target}.4`for our postconditions. This notation lets us access fields within our target type. Specifically, `{target}.2` refers to the `hp` field, and `{target}.4` refers to the `def` field. + +It should be noted that using these cases in a parameterized contract is not the only way to set up our contract. We could have designed some fancy Cryptol that considers all of the different stat ranges for our preconditions and postconditions. However, we wanted to show this method of using cases since we can leverage Python's if statements in our proofs. + +Let's set up our unit test: ```python class GameTests(unittest.TestCase): From ecb01e2b88ea9c1cbaf9a32d10947986aec133c2 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Thu, 2 Jun 2022 16:34:06 -0400 Subject: [PATCH 099/120] Update SAW.md --- labs/SAW/SAW.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 03ed8b04..43787f9f 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1208,7 +1208,7 @@ class initDefaultPlayer_Contract(Contract): ### Structs as Cryptol Tuples and Records -Let's go back and tidy up `initDefaultPlayer_Contract`'s postconditions. Instead of using one postcondition per field, we can rewrite the `points_to` postcondition to account for the entire struct. We do this by taking advantage of the fact that Cryptol interprets symbolic structs as tuples. +We can replace all of the `points_to` postconditions in the previous contract since Cryptol interprets symbolic structs as tuples. ```python self.points_to(player, cry_f("""( repeat 0x41 : [{MAX_NAME_LENGTH}][8], @@ -1219,7 +1219,7 @@ self.points_to(player, cry_f("""( repeat 0x41 : [{MAX_NAME_LENGTH}][8], 3 : [32] )""")) ``` -We use 3 double quotes `"""` in our `cry_f` call. This technique is handy when we want to separate our expected Cryptol-defined behaviors over multiple lines to improve code readability. Python considers the 3 double quotes as a multiline string. Multiline strings may also be used as block comments in Python. +We use 3 double quotes `"""` in our `cry_f` call. This technique is useful when we want to separate our expected Cryptol-defined behaviors over multiple lines to improve code readability. Python considers the 3 double quotes as a multiline string. Multiline strings may also be used as block comments in Python. While Cryptol's record types could also represent structs, SAW does not currently support translating Cryptol's record types into crucible-llvm's type system. If we tried to represent the struct as a Cryptol record like so: @@ -1320,12 +1320,11 @@ Now, let's go ahead and make a contract to represent this function: class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables - character_p = self.alloc(alias_ty("struct.character_t")) + character = self.alloc(alias_ty("struct.character_t")) tempCharacter_p = self.alloc(alias_ty("struct.character_t")) - ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) - frames = self.fresh_var(ty, "sprite.frames") - xPos = self.fresh_var(i32, "sprite.xPos") - yPos = self.fresh_var(i32, "sprite.yPos") + frames = self.fresh_var(array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))), "sprite.frames") + xPos = self.fresh_var(i32, "sprite.xPos") + yPos = self.fresh_var(i32, "sprite.yPos") sprite = struct(tempCharacter_p, frames, xPos, yPos) sprite_p = self.alloc(alias_ty("struct.sprite_t")) self.points_to(sprite_p, sprite) From 0c18cf19158152e95a6a9ec693b844f65ce1fe24 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 20:55:12 +0000 Subject: [PATCH 100/120] Copied original Game artifacts into a subfolder, DLC --- labs/SAW/Game/{ => DLC}/GameOutline.md | 0 labs/SAW/Game/DLC/Makefile | 20 + labs/SAW/Game/DLC/README.md | 5 + labs/SAW/Game/DLC/proof/Game.py | 570 +++++++++++++++++++++++++ labs/SAW/Game/DLC/specs/Game.cry | 15 + labs/SAW/Game/DLC/src/Assets.c | 6 + labs/SAW/Game/DLC/src/Game.c | 208 +++++++++ labs/SAW/Game/DLC/src/Game.h | 109 +++++ labs/SAW/Game/proof/Game.py | 21 - 9 files changed, 933 insertions(+), 21 deletions(-) rename labs/SAW/Game/{ => DLC}/GameOutline.md (100%) create mode 100644 labs/SAW/Game/DLC/Makefile create mode 100644 labs/SAW/Game/DLC/README.md create mode 100644 labs/SAW/Game/DLC/proof/Game.py create mode 100644 labs/SAW/Game/DLC/specs/Game.cry create mode 100644 labs/SAW/Game/DLC/src/Assets.c create mode 100644 labs/SAW/Game/DLC/src/Game.c create mode 100644 labs/SAW/Game/DLC/src/Game.h diff --git a/labs/SAW/Game/GameOutline.md b/labs/SAW/Game/DLC/GameOutline.md similarity index 100% rename from labs/SAW/Game/GameOutline.md rename to labs/SAW/Game/DLC/GameOutline.md diff --git a/labs/SAW/Game/DLC/Makefile b/labs/SAW/Game/DLC/Makefile new file mode 100644 index 00000000..de95f0c7 --- /dev/null +++ b/labs/SAW/Game/DLC/Makefile @@ -0,0 +1,20 @@ +# Note: Makefile assumes user already started the SAW remote API +# $ start-saw-remote-api + +# Variables to hold relative file paths +SRC=src +PROOF=proof +SPECS=specs +ARTIFACTS=artifacts + +# Note: -g flag compiles the code with debug symbols enabled. +# The flag lets us pass field names in addition to indices for structs. +all: artifacts + clang -c -g -emit-llvm -o $(ARTIFACTS)/Game.bc $(SRC)/Game.c + python3 $(PROOF)/Game.py + +artifacts: + mkdir $(ARTIFACTS) + +clean: + rm -r $(ARTIFACTS) \ No newline at end of file diff --git a/labs/SAW/Game/DLC/README.md b/labs/SAW/Game/DLC/README.md new file mode 100644 index 00000000..7a368788 --- /dev/null +++ b/labs/SAW/Game/DLC/README.md @@ -0,0 +1,5 @@ +# Game DLC + +Contents in this directory provide an expanded example of what exists in the parent [Game](../) directory. + +[src/](./src) contains an expanded Game library with more functions for you to try out and make SAW contracts for. \ No newline at end of file diff --git a/labs/SAW/Game/DLC/proof/Game.py b/labs/SAW/Game/DLC/proof/Game.py new file mode 100644 index 00000000..d79d1afb --- /dev/null +++ b/labs/SAW/Game/DLC/proof/Game.py @@ -0,0 +1,570 @@ +####################################### +# Imporant Imports +####################################### + +import os +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * +from saw_client.proofscript import * +from saw_client.llvm_type import * + + +####################################### +# Defines and Constants +####################################### + +MAX_NAME_LENGTH = 12 +SUCCESS = 170 +FAILURE = 85 +MAX_STAT = 100 +SCREEN_ROWS = 15 +SCREEN_COLS = 10 +SCREEN_TILES = SCREEN_ROWS * SCREEN_COLS +NEUTRAL = 0 +DEFEAT_PLAYER = 1 +DEFEAT_OPPONENT = 2 +ASSET_TABLE_SIZE = 16 +GAITS = 2 +DIRECTIONS = 4 +ANIMATION_STEPS = 3 + +####################################### +# SAW Helper Functions +####################################### + +def ptr_to_fresh(spec: Contract, ty: LLVMType, + name: str) -> Tuple[FreshVar, SetupVal]: + var = spec.fresh_var(ty, name) + ptr = spec.alloc(ty, points_to = var) + return (var, ptr) + + +####################################### +# SAW Contracts +####################################### + +# levelUp Contract +# uint32_t levelUp(uint32_t level) +class levelUp_Contract(Contract): + def specification (self): + # Declare level variable + level = self.fresh_var(i32, "level") + + # Symbolically execute the function + self.execute_func(level) + + # Assert the function's return behavior + self.returns_f("levelUp {level}") + + +# initDefaultPlayer Contract +# uint32_t initDefaultPlayer(player_t* player) +class initDefaultPlayer_Contract(Contract): + def specification (self): + # Pull the struct from the bitcode + # Although the function uses player_t, pass character_t to SAW since + # player_t is an alternative typedef name for character_t. + player = self.alloc(alias_ty("struct.character_t")) + + # Symbolically execute the function + self.execute_func(player) + + # Assert the postcondition behaviors + + # Option 1: Assert one field at a time via points_to + self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) + self.points_to(player['level'], cry_f("1 : [32]")) + self.points_to(player['hp'], cry_f("10 : [32]")) + self.points_to(player['atk'], cry_f("5 : [32]")) + self.points_to(player['def'], cry_f("4 : [32]")) + self.points_to(player['spd'], cry_f("3 : [32]")) + # Note: If bitcode isn't compiled with debug symbols enabled (no "-g" flag) + # then use the field indices instead. + # self.points_to(player[0], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) + # self.points_to(player[1], cry_f("1 : [32]")) + # self.points_to(player[2], cry_f("10 : [32]")) + # self.points_to(player[3], cry_f("5 : [32]")) + # self.points_to(player[4], cry_f("4 : [32]")) + # self.points_to(player[5], cry_f("3 : [32]")) + + # Option 2: Assert all of the fields to a tuple + # self.points_to(player, cry_f("( repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] )")) + + # Pop Quiz: Why doesn't this work? + # self.points_to(player, cry_f("""{{ name = repeat 0x41 : [{MAX_NAME_LENGTH}][8], + # level = 1 : [32], + # hp = 10 : [32], + # atk = 5 : [32], + # def = 4 : [32], + # spd = 3 : [32] }}""")) + + self.returns(cry_f("`({SUCCESS}) : [32]")) + + +# initDefaultSprite Contract +# uint32_t initDefaultSprite(sprite_t* sprite) +class initDefaultSprite_Contract(Contract): + def specification (self): + # Declare variables + character_p = self.alloc(alias_ty("struct.character_t")) # For Option 1 + #character_p = self.fresh_var(ptr_ty(alias_ty("struct.character_t"))) # For Option 2 + tempCharacter_p = self.alloc(alias_ty("struct.character_t")) + ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) + frames = self.fresh_var(ty, "sprite.frames") + xPos = self.fresh_var(i32, "sprite.xPos") + yPos = self.fresh_var(i32, "sprite.yPos") + sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) + + # Symbolically execute the function + self.execute_func(character_p, sprite_p) + + # Assert postconditions + # Option 1: Struct Strategy + self.points_to(sprite_p, struct( character_p, + cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), + cry_f("1 : [32]"), + cry_f("2 : [32]") )) + # Option 2: Full Tuple Strategy + #self.points_to(sprite_p, cry_f("""( {character_p}, + # zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], + # 1 : [32], + # 2 : [32]) """)) + + self.returns_f("`({SUCCESS}) : [32]") + + +# checkStats Contract +# uint32_t checkStats(character_t* character) +class checkStats_Contract(Contract): + def __init__(self, shouldPass : bool): + super().__init__() + self.shouldPass = shouldPass + # There are 2 possible cases for checkStats + # Case 1 (shouldPass == True): All of the stats are below the MAX_STAT cap + # Case 2 (shouldPass == False): At least one stat exceeds the MAX_STAT cap + + def specification (self): + # Declare variables + (character, character_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="character") + + # Assert preconditions + if (self.shouldPass): + # Set up the preconditions to assure all stats are below the MAX_STAT cap + self.precondition_f("{character}.2 <= `{MAX_STAT}") + self.precondition_f("{character}.3 <= `{MAX_STAT}") + self.precondition_f("{character}.4 <= `{MAX_STAT}") + self.precondition_f("{character}.5 <= `{MAX_STAT}") + else: + # Note: Must meet at least one of the following preconditions to result + # in a failure (feel free to comment out as many as you want) + self.precondition_f("{character}.2 > `{MAX_STAT}") + self.precondition_f("{character}.3 > `{MAX_STAT}") + self.precondition_f("{character}.4 > `{MAX_STAT}") + self.precondition_f("{character}.5 > `{MAX_STAT}") + + # Symbolically execute the function + self.execute_func(character_p) + + # Assert the postcondition behavior + if (self.shouldPass): + self.returns(cry_f("`({SUCCESS}) : [32]")) + else: + self.returns(cry_f("`({FAILURE}) : [32]")) + + +# resolveAttack Contract +# void resolveAttack(character_t* target, uint32_t atk) +class resolveAttack_Contract(Contract): + def __init__(self, case : int): + super().__init__() + self.case = case + # There are 3 possible cases for resolveAttack + # Case 1: Attack mitigated + # Case 2: Immediate KO + # Case 3: Regular attack + # Each case results in a different function behavior for calculating the + # target's remaining HP. While we could make 3 separate contracts to handle + # all of the possible cases, we can pass a parameter to the contract, which + # identifies what preconditions and postconditions to set. + + def specification (self): + # Declare variables + (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") + atk = self.fresh_var(i32, "atk") + + # Assert the precondition that the stats are below the max stat cap + # Pop Quiz: Why do we need these preconditions? + self.precondition_f("{atk} <= `{MAX_STAT}") + self.precondition_f("{target}.2 <= `{MAX_STAT}") + self.precondition_f("{target}.4 <= `{MAX_STAT}") + + # Determine the preconditions based on the case parameter + if (self.case == 1): + # target->def >= atk + self.precondition_f("{target}.4 >= {atk}") + elif (self.case == 2): + # target->hp <= (atk - target->def) + self.precondition_f("({target}.2 + {target}.4) <= {atk}") + # Pop Quiz: Are the following preconditions better for case 2? + # self.precondition_f("{target}.4 < {atk}") + # self.precondition_f("{target}.2 <= ({atk} - {target}.4)") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("{target}.4 < {atk}") + self.precondition_f("({target}.2 + {target}.4) > {atk}") + + # Symbolically execute the function + self.execute_func(target_p, atk) + + # Determine the postcondition based on the case parameter + if (self.case == 1): + self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(target_p[2], cry_f("{target}.2 : [32]")) + elif (self.case == 2): + self.points_to(target_p['hp'], cry_f("0 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(target_p[2], cry_f("0 : [32]")) + else: + self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(target_p[2], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + + self.returns(void) + + +# selfDamage Contract +# uint32_t selfDamage(player_t* player) +class selfDamage_Contract(Contract): + def __init__(self, case : int): + super().__init__() + self.case = case + # There are 3 possible cases for resolveAttack + # Case 1: Attack mitigated + # Case 2: Immediate KO + # Case 3: Regular attack + # Each case results in a different function behavior for calculating the + # player's remaining HP. While we could make 3 separate contracts to handle + # all of the possible cases, we can pass a parameter to the contract, which + # identifies what preconditions and postconditions to set. + + def specification (self): + # Declare variables + (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") + + # Assert the precondition that the player's HP is positive + # Why? Game logic assumes you can't damage yourself if you're already KO'd! + self.precondition_f("{player}.2 > 0") + + # Assert the precondition that character stats are below the max stat cap + # Pop Quiz: Explain why the proof fails when the following preconditions + # are commented out. + self.precondition_f("{player}.2 <= `{MAX_STAT}") + self.precondition_f("{player}.3 <= `{MAX_STAT}") + self.precondition_f("{player}.4 <= `{MAX_STAT}") + self.precondition_f("{player}.5 <= `{MAX_STAT}") + + # Determine the preconditions based on the case parameter + if (self.case == 1): + # player->def >= player->atk + self.precondition_f("{player}.4 >= {player}.3") + elif (self.case == 2): + # player->hp <= (player->atk - player->def) + self.precondition_f("({player}.2 + {player}.4) <= {player}.3") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("{player}.4 < {player}.3") + self.precondition_f("({player}.2 + {player}.4) > {player}.3") + + # Symbolically execute the function + self.execute_func(player_p, player_p) + + # Assert the postcondition + if (self.case == 1): + self.points_to(player_p['hp'], cry_f("{player}.2 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("{player}.2 : [32]")) + self.returns(cry_f("`({NEUTRAL}) : [32]")) + elif (self.case == 2): + self.points_to(player_p['hp'], cry_f("0 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("0 : [32]")) + self.returns(cry_f("`({DEFEAT_PLAYER}) : [32]")) + else: + self.points_to(player_p['hp'], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(player_p[2], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) + self.returns(cry_f("`({NEUTRAL}) : [32]")) + + +# quickBattle Contract +# void quickBattle(player_t* player, character_t* opponent) +class quickBattle_Contract(Contract): + def specification (self): + # Declare variables + (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") + (opponent, opponent_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="opponent") + # Pop Quiz: Why does allocating the pointers in the following way yield an + # error? + #player = self.alloc(alias_ty("struct.character_t")) + #opponent = self.alloc(alias_ty("struct.character_t")) + + # Assert the precondition that both HPs are greater than 0 + # Why? Game logic assumes you can't attack if you're already KO'd! + self.precondition_f("{player}.2 > 0") + self.precondition_f("{opponent}.2 > 0") + + # Assert the precondition that character stats are below the max stat cap + # Pop Quiz: Explain why the proof fails when the following preconditions + # are commented out. + self.precondition_f("{player}.2 <= `{MAX_STAT}") + self.precondition_f("{player}.3 <= `{MAX_STAT}") + self.precondition_f("{player}.4 <= `{MAX_STAT}") + self.precondition_f("{player}.5 <= `{MAX_STAT}") + self.precondition_f("{opponent}.2 <= `{MAX_STAT}") + self.precondition_f("{opponent}.3 <= `{MAX_STAT}") + self.precondition_f("{opponent}.4 <= `{MAX_STAT}") + self.precondition_f("{opponent}.5 <= `{MAX_STAT}") + + # Symbolically execute the function + self.execute_func(player_p, opponent_p) + + # Assert the postcondition + self.returns(void) + + +# getDefaultLevel Contract +# uint32_t getDefaultLevel() +class getDefaultLevel_Contract(Contract): + def specification (self): + # Initialize the defaultLevel global variable + defaultLevel_init = global_initializer("defaultLevel") + self.points_to(global_var("defaultLevel"), defaultLevel_init) + + # Symbolically execute the function + self.execute_func() + + # Assert the function's return behavior + self.returns(defaultLevel_init) + + +# initScreen Contract +# uint32_t initScreen(screen_t* screen, uint8_t assetID) +class initScreen_Contract(Contract): + def specification (self): + # Declare variables + screen = self.alloc(alias_ty("struct.screen_t")) + assetID = self.fresh_var(i8, "assetID") + + # Symbolically execute the function + self.execute_func(screen, assetID) + + # Assert the postcondition + self.points_to(screen['tiles'], cry_f("repeat {assetID} : [{SCREEN_TILES}][8]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(screen[0], cry_f("repeat {assetID} : [{SCREEN_TILES}][8]")) + + self.returns(cry_f("`({SUCCESS}) : [32]")) + + +# setScreenTile +# uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx) +class setScreenTile_Contract(Contract): + def __init__(self, shouldPass : bool): + super().__init__() + self.shouldPass = shouldPass + # There are 2 possible cases for setScreenTile + # Case 1 (shouldPass == True): Both screenIdx and tableIdx are below their + # limits, SCREEN_TILES and ASSET_TABLE_SIZE, + # respectively. + # Case 2 (shouldPass == False): At least one index exceeds its limits. + + def specification (self): + # Declare variables + (screen, screen_p) = ptr_to_fresh(self, alias_ty("struct.screen_t"), name="screen") + screenIdx = self.fresh_var(i32, "screenIdx") + tableIdx = self.fresh_var(i32, "tableIdx") + # Pop Quiz: Why can't we just declare and pass the screen pointer? + # screen_p = self.alloc(alias_ty("struct.screen_t")) + + # Initialize Game.h's assetTable according to Assets.c + # Note: The contents of assetTable from Assets.c was copied into Game.cry + # Required because the global variable in Game.h is declared as an extern, + # and the current way Game.bc is compiled does not include Assets.c in the + # Makefile. + self.points_to(global_var("assetTable"), cry_f("assetTable")) + + # Assert preconditions depending on the Contract parameter + if (self.shouldPass): + self.precondition_f("{screenIdx} < {SCREEN_TILES}") + self.precondition_f("{tableIdx} < {ASSET_TABLE_SIZE}") + else: + # Note: Only one of the following preconditions is needed + self.precondition_f("{screenIdx} >= {SCREEN_TILES}") + self.precondition_f("{tableIdx} >= {ASSET_TABLE_SIZE}") + + # Symbolically execute the function + self.execute_func(screen_p, screenIdx, tableIdx) + + # Since we just want to check one index, let's have our screen pointer + # point to a new screen_t variable. + screen_post = self.fresh_var(alias_ty("struct.screen_t"), "screen_post") + + # Assert that the original screen pointer now points to the new screen_t + # variable. This will allow us to reference screen_post in Cryptol for our + # later postconditions. + self.points_to(screen_p, screen_post) + + # Assert postconditions depending on the Contract parameter + if (self.shouldPass): + self.postcondition_f("({screen_post}@{screenIdx}) == assetTable@{tableIdx}") + #self.points_to(screen['tiles'][cry_f("{screenIdx}")], cry_f("assetTable@{tableIdx}")) + self.returns_f("`({SUCCESS}) : [32]") + else: + self.returns_f("`({FAILURE}) : [32]") + + +# resetInventoryItems Contract +# void resetInventoryItems(inventory_t* inventory) +class resetInventoryItems_Contract(Contract): + def __init__(self, numItems : int): + super().__init__() + self.numItems = numItems + # Note: The inventory_t struct defines its item field as a item_t pointer. + # Instead of declaring a fixed array size, the pointer enables for a + # variable size depending on the memory allocation configured by its + # caller. + # While this is valid C syntax, SAW and Cryptol require the known + # size ahead of time. Here, our contract takes the numItems parameter + # to declare a fixed array size. + + def specification (self): + # Declare variables + # Note: The setup here does not use item_p for the proof. However, item_p + # is included to show errors that can be encountered with the + # inventory_t struct. + (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) + """ + If inventory_t is defined as: + typedef struct { + item_t* item; + uint32_t numItems; + } inventory_t; + + Attempt 1: + ========== + Passing item as so: + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) + + yields the following error: + ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): + error: types not memory-compatible: + { %struct.item_t*, i32 } + { [5 x { i32, i32 }], i32 } + + + stdout: + + Attempt 2: + ========== + Passing item_p as so: + inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item_p, cry_f("{self.numItems} : [32]"))) + + yields the following error: + ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): + error: typeOfSetupValue: llvm_elem requires pointer to struct or array, found %struct.item_t** + stdout: + + Considering both of these verification setup attempts, we can see that + defining inventory_t with an item_t pointer is tough for SAW to setup and. + prove. Consequently, it is better to use fixed array lengths for structs! + """ + + # Symbolically execute the function + self.execute_func(inventory_p) + + # Assert the postconditions + for i in range(self.numItems): + self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) + # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: + # self.points_to(inventory_p[0][i][1], cry_f("0 : [32]")) + # Note: Even though debug symbols is enabled, SAW cannot resolve the + # "quantity" field, which is why we still use a 1 above. + + self.returns(void) + + +####################################### + + +####################################### +# Unit Tests +####################################### + +class GameTests(unittest.TestCase): + def test_Game(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + pwd = os.getcwd() + bitcode_name = pwd + "/artifacts/Game.bc" + cryptol_name = pwd + "/specs/Game.cry" + + cryptol_load_file(cryptol_name) + module = llvm_load_module(bitcode_name) + + # Override(s) associated with basic SAW setup + levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) + + # Override(s) associated with basic struct initialization + initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) + initDefaultSprite_result = llvm_verify(module, 'initDefaultSprite', initDefaultSprite_Contract()) + + # Overrides(s) associated with preconditions and postconditions that must + # be considered in SAW contracts & unit test overrides + checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) + checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) + resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) + resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) + resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) + selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) + + # Override(s) associated with global variable handling + getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) + initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) + setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) + setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) + + # Override(s) showing limitations with struct pointer fields + resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) + + # Assert the overrides are successful + self.assertIs(levelUp_result.is_success(), True) + self.assertIs(initDefaultPlayer_result.is_success(), True) + self.assertIs(initDefaultSprite_result.is_success(), True) + self.assertIs(checkStats_pass_result.is_success(), True) + self.assertIs(checkStats_fail_result.is_success(), True) + self.assertIs(resolveAttack_case1_result.is_success(), True) + self.assertIs(resolveAttack_case2_result.is_success(), True) + self.assertIs(resolveAttack_case3_result.is_success(), True) + self.assertIs(selfDamage_case1_result.is_success(), True) + self.assertIs(selfDamage_case2_result.is_success(), True) + self.assertIs(selfDamage_case3_result.is_success(), True) + self.assertIs(quickBattle_result.is_success(), True) + self.assertIs(getDefaultLevel_result.is_success(), True) + self.assertIs(initScreen_result.is_success(), True) + self.assertIs(setScreenTile_pass_result.is_success(), True) + self.assertIs(setScreenTile_fail_result.is_success(), True) + self.assertIs(resetInventoryItems_result.is_success(), True) + + +if __name__ == "__main__": + unittest.main() + +####################################### diff --git a/labs/SAW/Game/DLC/specs/Game.cry b/labs/SAW/Game/DLC/specs/Game.cry new file mode 100644 index 00000000..559a14d4 --- /dev/null +++ b/labs/SAW/Game/DLC/specs/Game.cry @@ -0,0 +1,15 @@ +module Game where + + +assetTable = [0x01, 0x12, 0x23, 0x34, + 0x45, 0x56, 0x67, 0x78, + 0x89, 0x9A, 0xAB, 0xBC, + 0xCD, 0xDE, 0xEF, 0xF0] : [16][8] + +levelUp : [32] -> [32] +levelUp level = level + 1 + +resolveAttack : [32] -> [32] -> [32] -> [32] +resolveAttack hp def atk = hp' + where + hp' = hp - (atk - def) diff --git a/labs/SAW/Game/DLC/src/Assets.c b/labs/SAW/Game/DLC/src/Assets.c new file mode 100644 index 00000000..def3e4de --- /dev/null +++ b/labs/SAW/Game/DLC/src/Assets.c @@ -0,0 +1,6 @@ +#include "Game.h" + +const uint8_t assetTable[ASSET_TABLE_SIZE] = {0x01, 0x12, 0x23, 0x34, + 0x45, 0x56, 0x67, 0x78, + 0x89, 0x9A, 0xAB, 0xBC, + 0xCD, 0xDE, 0xEF, 0xF0}; \ No newline at end of file diff --git a/labs/SAW/Game/DLC/src/Game.c b/labs/SAW/Game/DLC/src/Game.c new file mode 100644 index 00000000..8dcc9fda --- /dev/null +++ b/labs/SAW/Game/DLC/src/Game.c @@ -0,0 +1,208 @@ +#include "Game.h" + + +/////////////////////////////////////// +// Function(s) with basic SAW setup +/////////////////////////////////////// + +uint32_t levelUp(uint32_t level) +{ + return (level + 1); +} + + +/////////////////////////////////////// +// Function(s) with basic struct initialization +/////////////////////////////////////// + +uint32_t initDefaultPlayer(player_t* player) +{ + // Variables + uint8_t i = 0; + uint32_t hp_default = 10; + uint32_t atk_default = 5; + uint32_t def_default = 4; + uint32_t spd_default = 3; + + // Initialize player according to some defaults + for (i = 0; i < MAX_NAME_LENGTH; i++) + { + // For simplicity, let's use A very cool name + // Assume ignoring the null terminator is fine too + // Remember, this is a dummy example ;) + player->name[i] = 'A'; + } + player->level = 1; + player->hp = hp_default; + player->atk = atk_default; + player->def = def_default; + player->spd = spd_default; + + return SUCCESS; +} + + +uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) +{ + // Initialize the character to the passed pointer + sprite->character = character; + + // Initialize the sprite frames to the default asset + for (uint8_t i = 0; i < GAITS; i++) + { + for (uint8_t j = 0; j < DIRECTIONS; j++) + { + for (uint8_t k = 0; k < ANIMATION_STEPS; k++) + { + sprite->frames[i][j][k] = 0x00; + } + } + } + + // Initialize sprite's default position + sprite->xPos = 1; + sprite->yPos = 2; + + return SUCCESS; +} + + +/////////////////////////////////////// +// Function(s) with preconditions and postconditions that must +// be considered in SAW contracts & unit test overrides +/////////////////////////////////////// + +// Checks that the character stats are below MAX_STAT +// Note: MUST be called before running any function that uses character stats! +uint32_t checkStats(character_t* character) +{ + // Assume failure by default + uint32_t result = FAILURE; + + // Check the stats + if (character->hp <= MAX_STAT && + character->atk <= MAX_STAT && + character->def <= MAX_STAT && + character->spd <= MAX_STAT ) + { + result = SUCCESS; + } + + return result; +} + + +void resolveAttack(character_t* target, uint32_t atk) +{ + if ( target->def >= atk) + { + // The target's defense mitigates the attack + target->hp = target->hp; + } + else if ( target->hp <= (atk - target->def) ) + { + // The attack will knock out the target + target->hp = 0; + } + else + { + // Calculate damage as normal + target->hp = target->hp - (atk - target->def); + } +} + + +uint32_t selfDamage(player_t* player) +{ + enum battleResult result = NEUTRAL; + + // Player attacks itself (perhaps due to status condition) + resolveAttack(player, player->atk); + // Note: If SAW ever gives a "Memory not disjoint" error, then what SAW + // actually means is that inputs to an overridden function cannot + // overlap in memory. Given that player->atk is a field within player, + // the inputs overlap. + // Also consider the following way to have the player damage itself: + // quickBattle(player, player); + // This also contains inputs that overlap in memory (i.e. not disjoint). + // Bear in mind that both function calls in this example will not result in + // the Memory Disjoint error for the Python API. However, they will likely + // yield the same error if not using the Python API (i.e. llvm.saw). + + if (player->hp <= 0) + { + // Player's self damage results in a knockout + result = DEFEAT_PLAYER; + } + + return result; +} + + +// Simulates a simple battle where only the faster character attacks. +void quickBattle(player_t* player, character_t* opponent) +{ + if (player->spd >= opponent->spd) + { + resolveAttack(opponent, player->atk); + } + else + { + resolveAttack(player, opponent->atk); + } +} + + +/////////////////////////////////////// +// Function(s) with global variable handling +/////////////////////////////////////// + +uint32_t getDefaultLevel() +{ + return defaultLevel; +} + + +uint32_t initScreen(screen_t* screen, uint8_t assetID) +{ + // Iterate through the screen tiles and set their asset ID + for (int i = 0; i < SCREEN_TILES; i++) + { + screen->tiles[i] = assetID; + } + + return SUCCESS; +} + + +// Sets a tile displayed on the screen to an particular assetID held in the +// global assetTable +uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx) +{ + // Initialize return status to FAILURE + uint32_t result = FAILURE; + + // Check for valid bounds + if (screenIdx < SCREEN_TILES && tableIdx < ASSET_TABLE_SIZE) + { + screen->tiles[screenIdx] = assetTable[tableIdx]; + result = SUCCESS; + } + + return result; +} + + + +/////////////////////////////////////// +// Function(s) showing limitations with struct pointer fields +/////////////////////////////////////// + +void resetInventoryItems(inventory_t* inventory) +{ + // Iterate through the inventory and set the quantity field to 0 + for (int i = 0; i < inventory->numItems; i++) + { + inventory->item[i].quantity = 0; + } +} diff --git a/labs/SAW/Game/DLC/src/Game.h b/labs/SAW/Game/DLC/src/Game.h new file mode 100644 index 00000000..e646b22c --- /dev/null +++ b/labs/SAW/Game/DLC/src/Game.h @@ -0,0 +1,109 @@ +#ifndef GAME_H +#define GAME_H + +#include + +#define MAX_NAME_LENGTH 12 +#define SUCCESS 170 // 170 = 0xAA = 10101010 +#define FAILURE 85 // 85 = 0x55 = 01010101 +#define MAX_STAT 100 +#define SCREEN_ROWS 15 +#define SCREEN_COLS 10 +#define SCREEN_TILES SCREEN_ROWS*SCREEN_COLS +#define ASSET_TABLE_SIZE 16 +#define GAITS 2 // Possible gaits to animate: walk and run +#define DIRECTIONS 4 // 2D game, 4 directions (up, down, left, right) +#define ANIMATION_STEPS 3 // 3 frames per direction (stand, left leg forward, right leg forward) + + +/////////////////////////////////////// +// Globals +/////////////////////////////////////// + +const uint32_t defaultLevel = 1; +extern const uint8_t assetTable[ASSET_TABLE_SIZE]; + + +/////////////////////////////////////// +// Enums +/////////////////////////////////////// + +// Enum containing possible battle outcomes +enum battleResult{ + NEUTRAL = 0, + DEFEAT_PLAYER = 1, + DEFEAT_OPPONENT = 2 +}; + + +/////////////////////////////////////// +// Structs +/////////////////////////////////////// + +// Struct containing character information +typedef struct { + uint8_t name[MAX_NAME_LENGTH]; + uint32_t level; + uint32_t hp; + uint32_t atk; + uint32_t def; + uint32_t spd; +} character_t; + +typedef character_t player_t; + +// Struct containing item information +typedef struct { + uint32_t id; + uint32_t quantity; +} item_t; + +// Struct containing inventory information +typedef struct { + //item_t* item; // Assume this points to an item_t array of unknown length + item_t item[5]; + uint32_t numItems; +} inventory_t; + +// Struct containing screen information +typedef struct { + uint8_t tiles[SCREEN_TILES]; // Holds asset ID for each screen tile +} screen_t; + +// Struct containing information on a character sprite +typedef struct { + character_t* character; + uint8_t frames[GAITS][DIRECTIONS][ANIMATION_STEPS]; + uint32_t xPos; // x position relative to the screen + uint32_t yPos; // y position relative to the screen +} sprite_t; + + +/////////////////////////////////////// +// Function prototypes +/////////////////////////////////////// + +// Function(s) with basic SAW setup +uint32_t levelUp(uint32_t level); + +// Function(s) with basic struct initialization +uint32_t initDefaultPlayer(player_t* player); +uint32_t initDefaultSprite(character_t* character, sprite_t* sprite); + +// Function(s) with preconditions and postconditions that must +// be considered in SAW contracts & unit test overrides +uint32_t checkStats(character_t* character); +void resolveAttack(character_t* target, uint32_t atk); +uint32_t selfDamage(player_t* player); +void quickBattle(player_t* player, character_t* opponent); + +// Function(s) with global variable handling +uint32_t getDefaultLevel(); +uint32_t initScreen(screen_t* screen, uint8_t assetID); +uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx); + +// Function(s) showing limitations with struct pointer fields +void resetInventoryItems(inventory_t* inventory); + + +#endif \ No newline at end of file diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 12d91ae4..d79d1afb 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -1,24 +1,3 @@ -# TODO -# - Try the alloc_pointsto_buffer example & see its limitations -# --> See if possible to keep pointer field -# - Aligned -# - Make a worksheet & solution for the SAW examples -# - Update SAW.md to discuss the topics covered in this SAW script -# --> Link to the GaloicInc/saw-script and GaloisInc/saw-demos examples for more Python references -# https://github.com/GaloisInc/saw-script/tree/master/saw-remote-api/python/tests/saw -# https://github.com/GaloisInc/saw-demos/tree/master/demos/signal-protocol -# --> Mention importance of "-g" flag for debug symbols -# --> Discuss reasons between self.alloc (only want pointer) and -# ptr_to_fresh (want to set initial contents or reference in postcondition) -# --> Mention reason for preconditions (i.e. checkStats caller vs callee) -# Removing the preconditons in resolveAttack leads to integer overflow -# --> Mention in some SAW configs (i.e. llvm), nested defines may be an issue (i.e. initScreen) -# --> Mention in some SAW configs (i.e. llvm), aliasing/non-disjoint memory regions may be an issue (i.e. selfDamage) -# --> Discuss the issues associated with inventory_t's item_t pointer -# --> Include a "Troubleshooting/What SAW Says & Means" section -# - Reorder the functions to match the topic discussion - - ####################################### # Imporant Imports ####################################### From 34df9791fcd765105cdb4a1cd51858a580565e77 Mon Sep 17 00:00:00 2001 From: smithnormform <97316220+smithnormform@users.noreply.github.com> Date: Thu, 2 Jun 2022 16:55:33 -0400 Subject: [PATCH 101/120] Update SAW.md --- labs/SAW/SAW.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 43787f9f..85093c9d 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1843,13 +1843,12 @@ Only functions with implementations in the bitcode can be verified. If one impor For example, the height of [height-balanced binary trees](https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree) -on n nodes is given by the ceiling of `log(n)`. In C we might use the -`math` library to write the helper function. +on n nodes is given by the ceiling of `log(n)`. In C we might try to implement this using ```C #include -uint64_t log2i(uint64_t i) { +uint64_t ceilLog2(uint64_t i) { return ceil(log2(i)); } @@ -1864,7 +1863,7 @@ To accomplish these goals we make a contract outlining what the behavior of this function: ```python -class log2iContract(Contract): +class ceilLog2Contract(Contract): def specification(self): i = self.fresh_var(i64, "i") @@ -1874,18 +1873,18 @@ class log2iContract(Contract): ``` -In the unit test we would assume the `log2Contract`: +In the unit test we would assume the `ceilLog2Contract`: ``` -log2i_assume = llvm_assume(mod, 'log2i', log2iContract()) +log2i_assume = llvm_assume(mod, 'ceilLog2', ceilLog2Contract()) ``` -If a C function ever used `log2i`, then we could pass in the +If a C function ever used `ceilLog2`, then we could pass in the assumption as an optional argument to verification: ``` -getHeight_result = llvm_verify(mod, 'getHeight', getHeightContract(), lemmas=[log2i_assume]) +getHeight_result = llvm_verify(mod, 'getHeight', getHeightContract(), lemmas=[ceilLog2_assume]) ``` The optional argument is a list because we can supply multiple @@ -1898,8 +1897,10 @@ addOneRemoveTwo_result = llvm_verify(mod, 'addOneRemoveTwo', addOneRemoveTwo(), ``` One can think of lemmas as rewrite rules under the hood. Whenever SAW -encounters log2 function in the C it will interpret its behavior as -what is specified in the `log2Contract()`. +encounters ceilLog2 function in the C it will interpret its behavior as +what is specified in the `ceilLog2Contract()`. + +**Exercise**: Does the `C` function `ceilLog2` as defined above always yield the behavior outlined in `ceilLog2Contract`? That is, was our assumption a fair one to make? If not, change the `C` code (possibly using different library functions) to match `lg2` in Cryptol. ## Uninterpreting Functions From 1f551e5a181ea90d309c658fda52560912597129 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 17:04:01 -0400 Subject: [PATCH 102/120] fix? --- .github/workflows/python-saw.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/python-saw.yml b/.github/workflows/python-saw.yml index 52a66aab..6141fe7d 100644 --- a/.github/workflows/python-saw.yml +++ b/.github/workflows/python-saw.yml @@ -37,11 +37,10 @@ jobs: - name: Download bc-files uses: actions/download-artifact@v3 with: - name: salsa20-bc - path: labs/Demos/SAW/Salsa20/ - # Start saw-remote-api server - - run: start-saw-remote-api-read-only - # Run Salsa20 Python SAW Script + name: bc-files + path: labs/Demos/SAW/ + - name: Start saw-remote-api server + run: start-saw-remote-api-read-only - name: Run Python SAW Script run: | python3 labs/Demos/SAW/xxHash/xxhash32-ref.py From 5e90e51820767767c98f37e42305e011abd37a66 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 21:46:32 +0000 Subject: [PATCH 103/120] Simplified base Game example proof, specs, and src files --- labs/SAW/Game/DLC/proof/Game.py | 2 +- labs/SAW/Game/proof/Game.py | 363 +------------------------------- labs/SAW/Game/specs/Game.cry | 8 - labs/SAW/Game/src/Assets.c | 6 - labs/SAW/Game/src/Game.c | 115 ---------- labs/SAW/Game/src/Game.h | 66 +----- 6 files changed, 10 insertions(+), 550 deletions(-) delete mode 100644 labs/SAW/Game/src/Assets.c diff --git a/labs/SAW/Game/DLC/proof/Game.py b/labs/SAW/Game/DLC/proof/Game.py index d79d1afb..8bcc073c 100644 --- a/labs/SAW/Game/DLC/proof/Game.py +++ b/labs/SAW/Game/DLC/proof/Game.py @@ -104,7 +104,7 @@ def specification (self): # initDefaultSprite Contract -# uint32_t initDefaultSprite(sprite_t* sprite) +# uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index d79d1afb..7b5feaac 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -19,17 +19,11 @@ SUCCESS = 170 FAILURE = 85 MAX_STAT = 100 -SCREEN_ROWS = 15 -SCREEN_COLS = 10 -SCREEN_TILES = SCREEN_ROWS * SCREEN_COLS -NEUTRAL = 0 -DEFEAT_PLAYER = 1 -DEFEAT_OPPONENT = 2 -ASSET_TABLE_SIZE = 16 GAITS = 2 DIRECTIONS = 4 ANIMATION_STEPS = 3 + ####################################### # SAW Helper Functions ####################################### @@ -45,20 +39,6 @@ def ptr_to_fresh(spec: Contract, ty: LLVMType, # SAW Contracts ####################################### -# levelUp Contract -# uint32_t levelUp(uint32_t level) -class levelUp_Contract(Contract): - def specification (self): - # Declare level variable - level = self.fresh_var(i32, "level") - - # Symbolically execute the function - self.execute_func(level) - - # Assert the function's return behavior - self.returns_f("levelUp {level}") - - # initDefaultPlayer Contract # uint32_t initDefaultPlayer(player_t* player) class initDefaultPlayer_Contract(Contract): @@ -72,44 +52,22 @@ def specification (self): self.execute_func(player) # Assert the postcondition behaviors - - # Option 1: Assert one field at a time via points_to self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) self.points_to(player['level'], cry_f("1 : [32]")) self.points_to(player['hp'], cry_f("10 : [32]")) self.points_to(player['atk'], cry_f("5 : [32]")) self.points_to(player['def'], cry_f("4 : [32]")) self.points_to(player['spd'], cry_f("3 : [32]")) - # Note: If bitcode isn't compiled with debug symbols enabled (no "-g" flag) - # then use the field indices instead. - # self.points_to(player[0], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) - # self.points_to(player[1], cry_f("1 : [32]")) - # self.points_to(player[2], cry_f("10 : [32]")) - # self.points_to(player[3], cry_f("5 : [32]")) - # self.points_to(player[4], cry_f("4 : [32]")) - # self.points_to(player[5], cry_f("3 : [32]")) - - # Option 2: Assert all of the fields to a tuple - # self.points_to(player, cry_f("( repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] )")) - - # Pop Quiz: Why doesn't this work? - # self.points_to(player, cry_f("""{{ name = repeat 0x41 : [{MAX_NAME_LENGTH}][8], - # level = 1 : [32], - # hp = 10 : [32], - # atk = 5 : [32], - # def = 4 : [32], - # spd = 3 : [32] }}""")) self.returns(cry_f("`({SUCCESS}) : [32]")) # initDefaultSprite Contract -# uint32_t initDefaultSprite(sprite_t* sprite) +# uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables - character_p = self.alloc(alias_ty("struct.character_t")) # For Option 1 - #character_p = self.fresh_var(ptr_ty(alias_ty("struct.character_t"))) # For Option 2 + character_p = self.alloc(alias_ty("struct.character_t")) tempCharacter_p = self.alloc(alias_ty("struct.character_t")) ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) frames = self.fresh_var(ty, "sprite.frames") @@ -121,16 +79,10 @@ def specification (self): self.execute_func(character_p, sprite_p) # Assert postconditions - # Option 1: Struct Strategy self.points_to(sprite_p, struct( character_p, cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), cry_f("1 : [32]"), cry_f("2 : [32]") )) - # Option 2: Full Tuple Strategy - #self.points_to(sprite_p, cry_f("""( {character_p}, - # zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], - # 1 : [32], - # 2 : [32]) """)) self.returns_f("`({SUCCESS}) : [32]") @@ -195,7 +147,6 @@ def specification (self): atk = self.fresh_var(i32, "atk") # Assert the precondition that the stats are below the max stat cap - # Pop Quiz: Why do we need these preconditions? self.precondition_f("{atk} <= `{MAX_STAT}") self.precondition_f("{target}.2 <= `{MAX_STAT}") self.precondition_f("{target}.4 <= `{MAX_STAT}") @@ -207,9 +158,6 @@ def specification (self): elif (self.case == 2): # target->hp <= (atk - target->def) self.precondition_f("({target}.2 + {target}.4) <= {atk}") - # Pop Quiz: Are the following preconditions better for case 2? - # self.precondition_f("{target}.4 < {atk}") - # self.precondition_f("{target}.2 <= ({atk} - {target}.4)") else: # Assume any other case follows the formal attack calculation self.precondition_f("{target}.4 < {atk}") @@ -221,285 +169,14 @@ def specification (self): # Determine the postcondition based on the case parameter if (self.case == 1): self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(target_p[2], cry_f("{target}.2 : [32]")) elif (self.case == 2): self.points_to(target_p['hp'], cry_f("0 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(target_p[2], cry_f("0 : [32]")) else: self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(target_p[2], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) - - self.returns(void) - - -# selfDamage Contract -# uint32_t selfDamage(player_t* player) -class selfDamage_Contract(Contract): - def __init__(self, case : int): - super().__init__() - self.case = case - # There are 3 possible cases for resolveAttack - # Case 1: Attack mitigated - # Case 2: Immediate KO - # Case 3: Regular attack - # Each case results in a different function behavior for calculating the - # player's remaining HP. While we could make 3 separate contracts to handle - # all of the possible cases, we can pass a parameter to the contract, which - # identifies what preconditions and postconditions to set. - - def specification (self): - # Declare variables - (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") - - # Assert the precondition that the player's HP is positive - # Why? Game logic assumes you can't damage yourself if you're already KO'd! - self.precondition_f("{player}.2 > 0") - - # Assert the precondition that character stats are below the max stat cap - # Pop Quiz: Explain why the proof fails when the following preconditions - # are commented out. - self.precondition_f("{player}.2 <= `{MAX_STAT}") - self.precondition_f("{player}.3 <= `{MAX_STAT}") - self.precondition_f("{player}.4 <= `{MAX_STAT}") - self.precondition_f("{player}.5 <= `{MAX_STAT}") - - # Determine the preconditions based on the case parameter - if (self.case == 1): - # player->def >= player->atk - self.precondition_f("{player}.4 >= {player}.3") - elif (self.case == 2): - # player->hp <= (player->atk - player->def) - self.precondition_f("({player}.2 + {player}.4) <= {player}.3") - else: - # Assume any other case follows the formal attack calculation - self.precondition_f("{player}.4 < {player}.3") - self.precondition_f("({player}.2 + {player}.4) > {player}.3") - - # Symbolically execute the function - self.execute_func(player_p, player_p) - - # Assert the postcondition - if (self.case == 1): - self.points_to(player_p['hp'], cry_f("{player}.2 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player_p[2], cry_f("{player}.2 : [32]")) - self.returns(cry_f("`({NEUTRAL}) : [32]")) - elif (self.case == 2): - self.points_to(player_p['hp'], cry_f("0 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player_p[2], cry_f("0 : [32]")) - self.returns(cry_f("`({DEFEAT_PLAYER}) : [32]")) - else: - self.points_to(player_p['hp'], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(player_p[2], cry_f("resolveAttack ({player}.2) ({player}.4) {player}.3")) - self.returns(cry_f("`({NEUTRAL}) : [32]")) - - -# quickBattle Contract -# void quickBattle(player_t* player, character_t* opponent) -class quickBattle_Contract(Contract): - def specification (self): - # Declare variables - (player, player_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="player") - (opponent, opponent_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="opponent") - # Pop Quiz: Why does allocating the pointers in the following way yield an - # error? - #player = self.alloc(alias_ty("struct.character_t")) - #opponent = self.alloc(alias_ty("struct.character_t")) - - # Assert the precondition that both HPs are greater than 0 - # Why? Game logic assumes you can't attack if you're already KO'd! - self.precondition_f("{player}.2 > 0") - self.precondition_f("{opponent}.2 > 0") - - # Assert the precondition that character stats are below the max stat cap - # Pop Quiz: Explain why the proof fails when the following preconditions - # are commented out. - self.precondition_f("{player}.2 <= `{MAX_STAT}") - self.precondition_f("{player}.3 <= `{MAX_STAT}") - self.precondition_f("{player}.4 <= `{MAX_STAT}") - self.precondition_f("{player}.5 <= `{MAX_STAT}") - self.precondition_f("{opponent}.2 <= `{MAX_STAT}") - self.precondition_f("{opponent}.3 <= `{MAX_STAT}") - self.precondition_f("{opponent}.4 <= `{MAX_STAT}") - self.precondition_f("{opponent}.5 <= `{MAX_STAT}") - - # Symbolically execute the function - self.execute_func(player_p, opponent_p) - - # Assert the postcondition - self.returns(void) - - -# getDefaultLevel Contract -# uint32_t getDefaultLevel() -class getDefaultLevel_Contract(Contract): - def specification (self): - # Initialize the defaultLevel global variable - defaultLevel_init = global_initializer("defaultLevel") - self.points_to(global_var("defaultLevel"), defaultLevel_init) - - # Symbolically execute the function - self.execute_func() - - # Assert the function's return behavior - self.returns(defaultLevel_init) - - -# initScreen Contract -# uint32_t initScreen(screen_t* screen, uint8_t assetID) -class initScreen_Contract(Contract): - def specification (self): - # Declare variables - screen = self.alloc(alias_ty("struct.screen_t")) - assetID = self.fresh_var(i8, "assetID") - - # Symbolically execute the function - self.execute_func(screen, assetID) - - # Assert the postcondition - self.points_to(screen['tiles'], cry_f("repeat {assetID} : [{SCREEN_TILES}][8]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(screen[0], cry_f("repeat {assetID} : [{SCREEN_TILES}][8]")) - - self.returns(cry_f("`({SUCCESS}) : [32]")) - - -# setScreenTile -# uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx) -class setScreenTile_Contract(Contract): - def __init__(self, shouldPass : bool): - super().__init__() - self.shouldPass = shouldPass - # There are 2 possible cases for setScreenTile - # Case 1 (shouldPass == True): Both screenIdx and tableIdx are below their - # limits, SCREEN_TILES and ASSET_TABLE_SIZE, - # respectively. - # Case 2 (shouldPass == False): At least one index exceeds its limits. - - def specification (self): - # Declare variables - (screen, screen_p) = ptr_to_fresh(self, alias_ty("struct.screen_t"), name="screen") - screenIdx = self.fresh_var(i32, "screenIdx") - tableIdx = self.fresh_var(i32, "tableIdx") - # Pop Quiz: Why can't we just declare and pass the screen pointer? - # screen_p = self.alloc(alias_ty("struct.screen_t")) - - # Initialize Game.h's assetTable according to Assets.c - # Note: The contents of assetTable from Assets.c was copied into Game.cry - # Required because the global variable in Game.h is declared as an extern, - # and the current way Game.bc is compiled does not include Assets.c in the - # Makefile. - self.points_to(global_var("assetTable"), cry_f("assetTable")) - - # Assert preconditions depending on the Contract parameter - if (self.shouldPass): - self.precondition_f("{screenIdx} < {SCREEN_TILES}") - self.precondition_f("{tableIdx} < {ASSET_TABLE_SIZE}") - else: - # Note: Only one of the following preconditions is needed - self.precondition_f("{screenIdx} >= {SCREEN_TILES}") - self.precondition_f("{tableIdx} >= {ASSET_TABLE_SIZE}") - - # Symbolically execute the function - self.execute_func(screen_p, screenIdx, tableIdx) - - # Since we just want to check one index, let's have our screen pointer - # point to a new screen_t variable. - screen_post = self.fresh_var(alias_ty("struct.screen_t"), "screen_post") - - # Assert that the original screen pointer now points to the new screen_t - # variable. This will allow us to reference screen_post in Cryptol for our - # later postconditions. - self.points_to(screen_p, screen_post) - - # Assert postconditions depending on the Contract parameter - if (self.shouldPass): - self.postcondition_f("({screen_post}@{screenIdx}) == assetTable@{tableIdx}") - #self.points_to(screen['tiles'][cry_f("{screenIdx}")], cry_f("assetTable@{tableIdx}")) - self.returns_f("`({SUCCESS}) : [32]") - else: - self.returns_f("`({FAILURE}) : [32]") - - -# resetInventoryItems Contract -# void resetInventoryItems(inventory_t* inventory) -class resetInventoryItems_Contract(Contract): - def __init__(self, numItems : int): - super().__init__() - self.numItems = numItems - # Note: The inventory_t struct defines its item field as a item_t pointer. - # Instead of declaring a fixed array size, the pointer enables for a - # variable size depending on the memory allocation configured by its - # caller. - # While this is valid C syntax, SAW and Cryptol require the known - # size ahead of time. Here, our contract takes the numItems parameter - # to declare a fixed array size. - - def specification (self): - # Declare variables - # Note: The setup here does not use item_p for the proof. However, item_p - # is included to show errors that can be encountered with the - # inventory_t struct. - (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") - inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) - """ - If inventory_t is defined as: - typedef struct { - item_t* item; - uint32_t numItems; - } inventory_t; - - Attempt 1: - ========== - Passing item as so: - inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) - - yields the following error: - ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): - error: types not memory-compatible: - { %struct.item_t*, i32 } - { [5 x { i32, i32 }], i32 } - - - stdout: - - Attempt 2: - ========== - Passing item_p as so: - inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item_p, cry_f("{self.numItems} : [32]"))) - - yields the following error: - ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): - error: typeOfSetupValue: llvm_elem requires pointer to struct or array, found %struct.item_t** - stdout: - - Considering both of these verification setup attempts, we can see that - defining inventory_t with an item_t pointer is tough for SAW to setup and. - prove. Consequently, it is better to use fixed array lengths for structs! - """ - - # Symbolically execute the function - self.execute_func(inventory_p) - - # Assert the postconditions - for i in range(self.numItems): - self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) - # If bitcode is compiled without debug symbols enabled (no "-g" flag), use: - # self.points_to(inventory_p[0][i][1], cry_f("0 : [32]")) - # Note: Even though debug symbols is enabled, SAW cannot resolve the - # "quantity" field, which is why we still use a 1 above. self.returns(void) -####################################### - - ####################################### # Unit Tests ####################################### @@ -516,36 +193,16 @@ def test_Game(self): cryptol_load_file(cryptol_name) module = llvm_load_module(bitcode_name) - # Override(s) associated with basic SAW setup - levelUp_result = llvm_verify(module, 'levelUp', levelUp_Contract()) - - # Override(s) associated with basic struct initialization + # Set up verification overrides initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) initDefaultSprite_result = llvm_verify(module, 'initDefaultSprite', initDefaultSprite_Contract()) - - # Overrides(s) associated with preconditions and postconditions that must - # be considered in SAW contracts & unit test overrides checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) - selfDamage_case1_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(1), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - selfDamage_case2_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(2), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - selfDamage_case3_result = llvm_verify(module, 'selfDamage', selfDamage_Contract(3), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - quickBattle_result = llvm_verify(module, 'quickBattle', quickBattle_Contract(), lemmas=[resolveAttack_case1_result, resolveAttack_case2_result, resolveAttack_case3_result]) - - # Override(s) associated with global variable handling - getDefaultLevel_result = llvm_verify(module, 'getDefaultLevel', getDefaultLevel_Contract()) - initScreen_result = llvm_verify(module, 'initScreen', initScreen_Contract()) - setScreenTile_pass_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(True)) - setScreenTile_fail_result = llvm_verify(module, 'setScreenTile', setScreenTile_Contract(False)) - - # Override(s) showing limitations with struct pointer fields - resetInventoryItems_result = llvm_verify(module, 'resetInventoryItems', resetInventoryItems_Contract(5)) # Assert the overrides are successful - self.assertIs(levelUp_result.is_success(), True) self.assertIs(initDefaultPlayer_result.is_success(), True) self.assertIs(initDefaultSprite_result.is_success(), True) self.assertIs(checkStats_pass_result.is_success(), True) @@ -553,18 +210,6 @@ def test_Game(self): self.assertIs(resolveAttack_case1_result.is_success(), True) self.assertIs(resolveAttack_case2_result.is_success(), True) self.assertIs(resolveAttack_case3_result.is_success(), True) - self.assertIs(selfDamage_case1_result.is_success(), True) - self.assertIs(selfDamage_case2_result.is_success(), True) - self.assertIs(selfDamage_case3_result.is_success(), True) - self.assertIs(quickBattle_result.is_success(), True) - self.assertIs(getDefaultLevel_result.is_success(), True) - self.assertIs(initScreen_result.is_success(), True) - self.assertIs(setScreenTile_pass_result.is_success(), True) - self.assertIs(setScreenTile_fail_result.is_success(), True) - self.assertIs(resetInventoryItems_result.is_success(), True) - if __name__ == "__main__": unittest.main() - -####################################### diff --git a/labs/SAW/Game/specs/Game.cry b/labs/SAW/Game/specs/Game.cry index 559a14d4..951162c5 100644 --- a/labs/SAW/Game/specs/Game.cry +++ b/labs/SAW/Game/specs/Game.cry @@ -1,14 +1,6 @@ module Game where -assetTable = [0x01, 0x12, 0x23, 0x34, - 0x45, 0x56, 0x67, 0x78, - 0x89, 0x9A, 0xAB, 0xBC, - 0xCD, 0xDE, 0xEF, 0xF0] : [16][8] - -levelUp : [32] -> [32] -levelUp level = level + 1 - resolveAttack : [32] -> [32] -> [32] -> [32] resolveAttack hp def atk = hp' where diff --git a/labs/SAW/Game/src/Assets.c b/labs/SAW/Game/src/Assets.c deleted file mode 100644 index def3e4de..00000000 --- a/labs/SAW/Game/src/Assets.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "Game.h" - -const uint8_t assetTable[ASSET_TABLE_SIZE] = {0x01, 0x12, 0x23, 0x34, - 0x45, 0x56, 0x67, 0x78, - 0x89, 0x9A, 0xAB, 0xBC, - 0xCD, 0xDE, 0xEF, 0xF0}; \ No newline at end of file diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index 8dcc9fda..e9607fd6 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -1,20 +1,6 @@ #include "Game.h" -/////////////////////////////////////// -// Function(s) with basic SAW setup -/////////////////////////////////////// - -uint32_t levelUp(uint32_t level) -{ - return (level + 1); -} - - -/////////////////////////////////////// -// Function(s) with basic struct initialization -/////////////////////////////////////// - uint32_t initDefaultPlayer(player_t* player) { // Variables @@ -67,11 +53,6 @@ uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) } -/////////////////////////////////////// -// Function(s) with preconditions and postconditions that must -// be considered in SAW contracts & unit test overrides -/////////////////////////////////////// - // Checks that the character stats are below MAX_STAT // Note: MUST be called before running any function that uses character stats! uint32_t checkStats(character_t* character) @@ -110,99 +91,3 @@ void resolveAttack(character_t* target, uint32_t atk) target->hp = target->hp - (atk - target->def); } } - - -uint32_t selfDamage(player_t* player) -{ - enum battleResult result = NEUTRAL; - - // Player attacks itself (perhaps due to status condition) - resolveAttack(player, player->atk); - // Note: If SAW ever gives a "Memory not disjoint" error, then what SAW - // actually means is that inputs to an overridden function cannot - // overlap in memory. Given that player->atk is a field within player, - // the inputs overlap. - // Also consider the following way to have the player damage itself: - // quickBattle(player, player); - // This also contains inputs that overlap in memory (i.e. not disjoint). - // Bear in mind that both function calls in this example will not result in - // the Memory Disjoint error for the Python API. However, they will likely - // yield the same error if not using the Python API (i.e. llvm.saw). - - if (player->hp <= 0) - { - // Player's self damage results in a knockout - result = DEFEAT_PLAYER; - } - - return result; -} - - -// Simulates a simple battle where only the faster character attacks. -void quickBattle(player_t* player, character_t* opponent) -{ - if (player->spd >= opponent->spd) - { - resolveAttack(opponent, player->atk); - } - else - { - resolveAttack(player, opponent->atk); - } -} - - -/////////////////////////////////////// -// Function(s) with global variable handling -/////////////////////////////////////// - -uint32_t getDefaultLevel() -{ - return defaultLevel; -} - - -uint32_t initScreen(screen_t* screen, uint8_t assetID) -{ - // Iterate through the screen tiles and set their asset ID - for (int i = 0; i < SCREEN_TILES; i++) - { - screen->tiles[i] = assetID; - } - - return SUCCESS; -} - - -// Sets a tile displayed on the screen to an particular assetID held in the -// global assetTable -uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx) -{ - // Initialize return status to FAILURE - uint32_t result = FAILURE; - - // Check for valid bounds - if (screenIdx < SCREEN_TILES && tableIdx < ASSET_TABLE_SIZE) - { - screen->tiles[screenIdx] = assetTable[tableIdx]; - result = SUCCESS; - } - - return result; -} - - - -/////////////////////////////////////// -// Function(s) showing limitations with struct pointer fields -/////////////////////////////////////// - -void resetInventoryItems(inventory_t* inventory) -{ - // Iterate through the inventory and set the quantity field to 0 - for (int i = 0; i < inventory->numItems; i++) - { - inventory->item[i].quantity = 0; - } -} diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index e646b22c..e04ebac5 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -3,39 +3,18 @@ #include -#define MAX_NAME_LENGTH 12 + +// Define status values that provide a hamming distance of 5 #define SUCCESS 170 // 170 = 0xAA = 10101010 #define FAILURE 85 // 85 = 0x55 = 01010101 + +#define MAX_NAME_LENGTH 12 #define MAX_STAT 100 -#define SCREEN_ROWS 15 -#define SCREEN_COLS 10 -#define SCREEN_TILES SCREEN_ROWS*SCREEN_COLS -#define ASSET_TABLE_SIZE 16 #define GAITS 2 // Possible gaits to animate: walk and run #define DIRECTIONS 4 // 2D game, 4 directions (up, down, left, right) #define ANIMATION_STEPS 3 // 3 frames per direction (stand, left leg forward, right leg forward) -/////////////////////////////////////// -// Globals -/////////////////////////////////////// - -const uint32_t defaultLevel = 1; -extern const uint8_t assetTable[ASSET_TABLE_SIZE]; - - -/////////////////////////////////////// -// Enums -/////////////////////////////////////// - -// Enum containing possible battle outcomes -enum battleResult{ - NEUTRAL = 0, - DEFEAT_PLAYER = 1, - DEFEAT_OPPONENT = 2 -}; - - /////////////////////////////////////// // Structs /////////////////////////////////////// @@ -52,24 +31,6 @@ typedef struct { typedef character_t player_t; -// Struct containing item information -typedef struct { - uint32_t id; - uint32_t quantity; -} item_t; - -// Struct containing inventory information -typedef struct { - //item_t* item; // Assume this points to an item_t array of unknown length - item_t item[5]; - uint32_t numItems; -} inventory_t; - -// Struct containing screen information -typedef struct { - uint8_t tiles[SCREEN_TILES]; // Holds asset ID for each screen tile -} screen_t; - // Struct containing information on a character sprite typedef struct { character_t* character; @@ -83,27 +44,10 @@ typedef struct { // Function prototypes /////////////////////////////////////// -// Function(s) with basic SAW setup -uint32_t levelUp(uint32_t level); - -// Function(s) with basic struct initialization uint32_t initDefaultPlayer(player_t* player); uint32_t initDefaultSprite(character_t* character, sprite_t* sprite); - -// Function(s) with preconditions and postconditions that must -// be considered in SAW contracts & unit test overrides uint32_t checkStats(character_t* character); void resolveAttack(character_t* target, uint32_t atk); -uint32_t selfDamage(player_t* player); -void quickBattle(player_t* player, character_t* opponent); - -// Function(s) with global variable handling -uint32_t getDefaultLevel(); -uint32_t initScreen(screen_t* screen, uint8_t assetID); -uint32_t setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx); - -// Function(s) showing limitations with struct pointer fields -void resetInventoryItems(inventory_t* inventory); -#endif \ No newline at end of file +#endif From 773cdaab1806b7263375ce3b36e3eec2d0be6172 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 22:57:50 +0000 Subject: [PATCH 104/120] Added basic documentation for Game.h --- labs/SAW/Game/src/Game.h | 48 +++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index e04ebac5..0328ed50 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -4,22 +4,23 @@ #include -// Define status values that provide a hamming distance of 5 -#define SUCCESS 170 // 170 = 0xAA = 10101010 -#define FAILURE 85 // 85 = 0x55 = 01010101 +// Status values that provide a hamming distance of 5 +#define SUCCESS 170 // 170 = 0xAA = 10101010 +#define FAILURE 85 // 85 = 0x55 = 01010101 -#define MAX_NAME_LENGTH 12 -#define MAX_STAT 100 -#define GAITS 2 // Possible gaits to animate: walk and run -#define DIRECTIONS 4 // 2D game, 4 directions (up, down, left, right) -#define ANIMATION_STEPS 3 // 3 frames per direction (stand, left leg forward, right leg forward) +#define MAX_NAME_LENGTH 12 // Maximum number of bytes in a character name +#define MAX_STAT 100 // Inclusive upper bound limit on character stats + +#define GAITS 2 // Possible gaits to animate: walk and run +#define DIRECTIONS 4 // 2D game, 4 directions (up, down, left, right) +#define ANIMATION_STEPS 3 // 3 frames per direction (stand, left leg forward, right leg forward) /////////////////////////////////////// // Structs /////////////////////////////////////// -// Struct containing character information +// Contains information about in-game characters typedef struct { uint8_t name[MAX_NAME_LENGTH]; uint32_t level; @@ -31,7 +32,7 @@ typedef struct { typedef character_t player_t; -// Struct containing information on a character sprite +// Contains information related to character sprites typedef struct { character_t* character; uint8_t frames[GAITS][DIRECTIONS][ANIMATION_STEPS]; @@ -44,9 +45,36 @@ typedef struct { // Function prototypes /////////////////////////////////////// +/** + Initializes a player variable based on default parameters. + \param player_t* player - Pointer to an allocated player variable. + \return SUCCESS. +**/ uint32_t initDefaultPlayer(player_t* player); + +/** + Initializes a sprite variable based on default parameters and ties the sprite + to the passed character reference. + \param character_t* character - Pointer to a character variable that the + the sprite should be tied to. + \param sprite_t* sprite - Pointer to an allocated sprite variable. + \return SUCCESS. +**/ uint32_t initDefaultSprite(character_t* character, sprite_t* sprite); + +/** + Checks whether the referenced character's stats are at or below the MAX_STAT. + \param character_t* character - Pointer to the character in question. + \return SUCCESS when all stats are <= MAX_STAT, or FAILURE otherwise. +**/ uint32_t checkStats(character_t* character); + +/** + Resolves a target's hp stat after an attack. + \param character_t* target - Defender during the attack. + \param uint32_t atk - Attacker's atk stat. + \return None. +**/ void resolveAttack(character_t* target, uint32_t atk); From a51e796ef6dd2b785cf6b3710484216654960d3f Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 23:24:33 +0000 Subject: [PATCH 105/120] Split Game.py into a worksheet and answers sheet --- labs/SAW/Game/proof/Game.py | 184 ++++++++---------------- labs/SAW/Game/proof/Game_answers.py | 215 ++++++++++++++++++++++++++++ labs/SAW/Game/src/Game.c | 38 +++-- labs/SAW/Game/src/Game.h | 14 +- 4 files changed, 297 insertions(+), 154 deletions(-) create mode 100644 labs/SAW/Game/proof/Game_answers.py diff --git a/labs/SAW/Game/proof/Game.py b/labs/SAW/Game/proof/Game.py index 7b5feaac..8c56ebc8 100644 --- a/labs/SAW/Game/proof/Game.py +++ b/labs/SAW/Game/proof/Game.py @@ -43,138 +43,80 @@ def ptr_to_fresh(spec: Contract, ty: LLVMType, # uint32_t initDefaultPlayer(player_t* player) class initDefaultPlayer_Contract(Contract): def specification (self): - # Pull the struct from the bitcode - # Although the function uses player_t, pass character_t to SAW since - # player_t is an alternative typedef name for character_t. - player = self.alloc(alias_ty("struct.character_t")) + # TODO: Declare variables - # Symbolically execute the function - self.execute_func(player) - # Assert the postcondition behaviors - self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) - self.points_to(player['level'], cry_f("1 : [32]")) - self.points_to(player['hp'], cry_f("10 : [32]")) - self.points_to(player['atk'], cry_f("5 : [32]")) - self.points_to(player['def'], cry_f("4 : [32]")) - self.points_to(player['spd'], cry_f("3 : [32]")) + # TODO: Assert preconditions - self.returns(cry_f("`({SUCCESS}) : [32]")) + + # TODO: Symbolically execute the function + + + # TODO: Assert postconditions + + + # TODO: Remove the following line when you have something! + pass # initDefaultSprite Contract # uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) class initDefaultSprite_Contract(Contract): def specification (self): - # Declare variables - character_p = self.alloc(alias_ty("struct.character_t")) - tempCharacter_p = self.alloc(alias_ty("struct.character_t")) - ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) - frames = self.fresh_var(ty, "sprite.frames") - xPos = self.fresh_var(i32, "sprite.xPos") - yPos = self.fresh_var(i32, "sprite.yPos") - sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) - - # Symbolically execute the function - self.execute_func(character_p, sprite_p) - - # Assert postconditions - self.points_to(sprite_p, struct( character_p, - cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), - cry_f("1 : [32]"), - cry_f("2 : [32]") )) - - self.returns_f("`({SUCCESS}) : [32]") + # TODO: Declare variables -# checkStats Contract -# uint32_t checkStats(character_t* character) -class checkStats_Contract(Contract): - def __init__(self, shouldPass : bool): - super().__init__() - self.shouldPass = shouldPass - # There are 2 possible cases for checkStats - # Case 1 (shouldPass == True): All of the stats are below the MAX_STAT cap - # Case 2 (shouldPass == False): At least one stat exceeds the MAX_STAT cap + # TODO: Assert preconditions - def specification (self): - # Declare variables - (character, character_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="character") - - # Assert preconditions - if (self.shouldPass): - # Set up the preconditions to assure all stats are below the MAX_STAT cap - self.precondition_f("{character}.2 <= `{MAX_STAT}") - self.precondition_f("{character}.3 <= `{MAX_STAT}") - self.precondition_f("{character}.4 <= `{MAX_STAT}") - self.precondition_f("{character}.5 <= `{MAX_STAT}") - else: - # Note: Must meet at least one of the following preconditions to result - # in a failure (feel free to comment out as many as you want) - self.precondition_f("{character}.2 > `{MAX_STAT}") - self.precondition_f("{character}.3 > `{MAX_STAT}") - self.precondition_f("{character}.4 > `{MAX_STAT}") - self.precondition_f("{character}.5 > `{MAX_STAT}") - - # Symbolically execute the function - self.execute_func(character_p) - - # Assert the postcondition behavior - if (self.shouldPass): - self.returns(cry_f("`({SUCCESS}) : [32]")) - else: - self.returns(cry_f("`({FAILURE}) : [32]")) + + # TODO: Symbolically execute the function + + + # TODO: Assert postconditions + + + # TODO: Remove the following line when you have something! + pass # resolveAttack Contract # void resolveAttack(character_t* target, uint32_t atk) class resolveAttack_Contract(Contract): - def __init__(self, case : int): - super().__init__() - self.case = case - # There are 3 possible cases for resolveAttack - # Case 1: Attack mitigated - # Case 2: Immediate KO - # Case 3: Regular attack - # Each case results in a different function behavior for calculating the - # target's remaining HP. While we could make 3 separate contracts to handle - # all of the possible cases, we can pass a parameter to the contract, which - # identifies what preconditions and postconditions to set. + def specification (self): + # TODO: Declare variables + + # TODO: Assert preconditions + + + # TODO: Symbolically execute the function + + + # TODO: Assert postconditions + + + # TODO: Remove the following line when you have something! + pass + + +# checkStats Contract +# uint32_t checkStats(character_t* character) +class checkStats_Contract(Contract): def specification (self): - # Declare variables - (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") - atk = self.fresh_var(i32, "atk") - - # Assert the precondition that the stats are below the max stat cap - self.precondition_f("{atk} <= `{MAX_STAT}") - self.precondition_f("{target}.2 <= `{MAX_STAT}") - self.precondition_f("{target}.4 <= `{MAX_STAT}") - - # Determine the preconditions based on the case parameter - if (self.case == 1): - # target->def >= atk - self.precondition_f("{target}.4 >= {atk}") - elif (self.case == 2): - # target->hp <= (atk - target->def) - self.precondition_f("({target}.2 + {target}.4) <= {atk}") - else: - # Assume any other case follows the formal attack calculation - self.precondition_f("{target}.4 < {atk}") - self.precondition_f("({target}.2 + {target}.4) > {atk}") + # TODO: Declare variables + + + # TODO: Assert preconditions + - # Symbolically execute the function - self.execute_func(target_p, atk) + # TODO: Symbolically execute the function + - # Determine the postcondition based on the case parameter - if (self.case == 1): - self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) - elif (self.case == 2): - self.points_to(target_p['hp'], cry_f("0 : [32]")) - else: - self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + # TODO: Assert postconditions - self.returns(void) + + # TODO: Remove the following line when you have something! + pass ####################################### @@ -193,23 +135,11 @@ def test_Game(self): cryptol_load_file(cryptol_name) module = llvm_load_module(bitcode_name) - # Set up verification overrides - initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) - initDefaultSprite_result = llvm_verify(module, 'initDefaultSprite', initDefaultSprite_Contract()) - checkStats_pass_result = llvm_verify(module, 'checkStats', checkStats_Contract(True)) - checkStats_fail_result = llvm_verify(module, 'checkStats', checkStats_Contract(False)) - resolveAttack_case1_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(1)) - resolveAttack_case2_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(2)) - resolveAttack_case3_result = llvm_verify(module, 'resolveAttack', resolveAttack_Contract(3)) - - # Assert the overrides are successful - self.assertIs(initDefaultPlayer_result.is_success(), True) - self.assertIs(initDefaultSprite_result.is_success(), True) - self.assertIs(checkStats_pass_result.is_success(), True) - self.assertIs(checkStats_fail_result.is_success(), True) - self.assertIs(resolveAttack_case1_result.is_success(), True) - self.assertIs(resolveAttack_case2_result.is_success(), True) - self.assertIs(resolveAttack_case3_result.is_success(), True) + # TODO: Set up verification overrides + + + # TODO: Assert the overrides are successful + if __name__ == "__main__": unittest.main() diff --git a/labs/SAW/Game/proof/Game_answers.py b/labs/SAW/Game/proof/Game_answers.py new file mode 100644 index 00000000..e6b52f5b --- /dev/null +++ b/labs/SAW/Game/proof/Game_answers.py @@ -0,0 +1,215 @@ +####################################### +# Imporant Imports +####################################### + +import os +import unittest +from saw_client import * +from saw_client.crucible import * +from saw_client.llvm import * +from saw_client.proofscript import * +from saw_client.llvm_type import * + + +####################################### +# Defines and Constants +####################################### + +MAX_NAME_LENGTH = 12 +SUCCESS = 170 +FAILURE = 85 +MAX_STAT = 100 +GAITS = 2 +DIRECTIONS = 4 +ANIMATION_STEPS = 3 + + +####################################### +# SAW Helper Functions +####################################### + +def ptr_to_fresh(spec: Contract, ty: LLVMType, + name: str) -> Tuple[FreshVar, SetupVal]: + var = spec.fresh_var(ty, name) + ptr = spec.alloc(ty, points_to = var) + return (var, ptr) + + +####################################### +# SAW Contracts +####################################### + +# initDefaultPlayer Contract +# uint32_t initDefaultPlayer(player_t* player) +class initDefaultPlayer_Contract(Contract): + def specification (self): + # Pull the struct from the bitcode + # Although the function uses player_t, pass character_t to SAW since + # player_t is an alternative typedef name for character_t. + player = self.alloc(alias_ty("struct.character_t")) + + # Symbolically execute the function + self.execute_func(player) + + # Assert the postcondition behaviors + self.points_to(player['name'], cry_f("repeat 0x41 : [{MAX_NAME_LENGTH}][8]")) + self.points_to(player['level'], cry_f("1 : [32]")) + self.points_to(player['hp'], cry_f("10 : [32]")) + self.points_to(player['atk'], cry_f("5 : [32]")) + self.points_to(player['def'], cry_f("4 : [32]")) + self.points_to(player['spd'], cry_f("3 : [32]")) + + self.returns(cry_f("`({SUCCESS}) : [32]")) + + +# initDefaultSprite Contract +# uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) +class initDefaultSprite_Contract(Contract): + def specification (self): + # Declare variables + character_p = self.alloc(alias_ty("struct.character_t")) + tempCharacter_p = self.alloc(alias_ty("struct.character_t")) + ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) + frames = self.fresh_var(ty, "sprite.frames") + xPos = self.fresh_var(i32, "sprite.xPos") + yPos = self.fresh_var(i32, "sprite.yPos") + sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) + + # Symbolically execute the function + self.execute_func(character_p, sprite_p) + + # Assert postconditions + self.points_to(sprite_p, struct( character_p, + cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), + cry_f("1 : [32]"), + cry_f("2 : [32]") )) + + self.returns_f("`({SUCCESS}) : [32]") + + +# resolveAttack Contract +# void resolveAttack(character_t* target, uint32_t atk) +class resolveAttack_Contract(Contract): + def __init__(self, case : int): + super().__init__() + self.case = case + # There are 3 possible cases for resolveAttack + # Case 1: Attack mitigated + # Case 2: Immediate KO + # Case 3: Regular attack + # Each case results in a different function behavior for calculating the + # target's remaining HP. While we could make 3 separate contracts to handle + # all of the possible cases, we can pass a parameter to the contract, which + # identifies what preconditions and postconditions to set. + + def specification (self): + # Declare variables + (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") + atk = self.fresh_var(i32, "atk") + + # Assert the precondition that the stats are below the max stat cap + self.precondition_f("{atk} <= `{MAX_STAT}") + self.precondition_f("{target}.2 <= `{MAX_STAT}") + self.precondition_f("{target}.4 <= `{MAX_STAT}") + + # Determine the preconditions based on the case parameter + if (self.case == 1): + # target->def >= atk + self.precondition_f("{target}.4 >= {atk}") + elif (self.case == 2): + # target->hp <= (atk - target->def) + self.precondition_f("({target}.2 + {target}.4) <= {atk}") + else: + # Assume any other case follows the formal attack calculation + self.precondition_f("{target}.4 < {atk}") + self.precondition_f("({target}.2 + {target}.4) > {atk}") + + # Symbolically execute the function + self.execute_func(target_p, atk) + + # Determine the postcondition based on the case parameter + if (self.case == 1): + self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) + elif (self.case == 2): + self.points_to(target_p['hp'], cry_f("0 : [32]")) + else: + self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) + + self.returns(void) + + +# checkStats Contract +# uint32_t checkStats(character_t* character) +class checkStats_Contract(Contract): + def __init__(self, shouldPass : bool): + super().__init__() + self.shouldPass = shouldPass + # There are 2 possible cases for checkStats + # Case 1 (shouldPass == True): All of the stats are below the MAX_STAT cap + # Case 2 (shouldPass == False): At least one stat exceeds the MAX_STAT cap + + def specification (self): + # Declare variables + (character, character_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="character") + + # Assert preconditions + if (self.shouldPass): + # Set up the preconditions to assure all stats are below the MAX_STAT cap + self.precondition_f("{character}.2 <= `{MAX_STAT}") + self.precondition_f("{character}.3 <= `{MAX_STAT}") + self.precondition_f("{character}.4 <= `{MAX_STAT}") + self.precondition_f("{character}.5 <= `{MAX_STAT}") + else: + # Note: Must meet at least one of the following preconditions to result + # in a failure (feel free to comment out as many as you want) + self.precondition_f("{character}.2 > `{MAX_STAT}") + self.precondition_f("{character}.3 > `{MAX_STAT}") + self.precondition_f("{character}.4 > `{MAX_STAT}") + self.precondition_f("{character}.5 > `{MAX_STAT}") + + # Symbolically execute the function + self.execute_func(character_p) + + # Assert the postcondition behavior + if (self.shouldPass): + self.returns(cry_f("`({SUCCESS}) : [32]")) + else: + self.returns(cry_f("`({FAILURE}) : [32]")) + + +####################################### +# Unit Tests +####################################### + +class GameTests(unittest.TestCase): + def test_Game(self): + connect(reset_server=True) + if __name__ == "__main__": view(LogResults(verbose_failure=True)) + + pwd = os.getcwd() + bitcode_name = pwd + "/artifacts/Game.bc" + cryptol_name = pwd + "/specs/Game.cry" + + cryptol_load_file(cryptol_name) + module = llvm_load_module(bitcode_name) + + # Set up verification overrides + initDefaultPlayer_result = llvm_verify(module, 'initDefaultPlayer', initDefaultPlayer_Contract()) + initDefaultSprite_result = llvm_verify(module, 'initDefaultSprite', initDefaultSprite_Contract()) + resolveAttack_case1_result = llvm_verify(module, 'resolveAttack' , resolveAttack_Contract(1) ) + resolveAttack_case2_result = llvm_verify(module, 'resolveAttack' , resolveAttack_Contract(2) ) + resolveAttack_case3_result = llvm_verify(module, 'resolveAttack' , resolveAttack_Contract(3) ) + checkStats_pass_result = llvm_verify(module, 'checkStats' , checkStats_Contract(True) ) + checkStats_fail_result = llvm_verify(module, 'checkStats' , checkStats_Contract(False) ) + + # Assert the overrides are successful + self.assertIs(initDefaultPlayer_result.is_success() , True) + self.assertIs(initDefaultSprite_result.is_success() , True) + self.assertIs(resolveAttack_case1_result.is_success(), True) + self.assertIs(resolveAttack_case2_result.is_success(), True) + self.assertIs(resolveAttack_case3_result.is_success(), True) + self.assertIs(checkStats_pass_result.is_success() , True) + self.assertIs(checkStats_fail_result.is_success() , True) + +if __name__ == "__main__": + unittest.main() diff --git a/labs/SAW/Game/src/Game.c b/labs/SAW/Game/src/Game.c index e9607fd6..ea1f4d52 100644 --- a/labs/SAW/Game/src/Game.c +++ b/labs/SAW/Game/src/Game.c @@ -53,26 +53,6 @@ uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) } -// Checks that the character stats are below MAX_STAT -// Note: MUST be called before running any function that uses character stats! -uint32_t checkStats(character_t* character) -{ - // Assume failure by default - uint32_t result = FAILURE; - - // Check the stats - if (character->hp <= MAX_STAT && - character->atk <= MAX_STAT && - character->def <= MAX_STAT && - character->spd <= MAX_STAT ) - { - result = SUCCESS; - } - - return result; -} - - void resolveAttack(character_t* target, uint32_t atk) { if ( target->def >= atk) @@ -91,3 +71,21 @@ void resolveAttack(character_t* target, uint32_t atk) target->hp = target->hp - (atk - target->def); } } + + +uint32_t checkStats(character_t* character) +{ + // Assume failure by default + uint32_t result = FAILURE; + + // Check the stats + if (character->hp <= MAX_STAT && + character->atk <= MAX_STAT && + character->def <= MAX_STAT && + character->spd <= MAX_STAT ) + { + result = SUCCESS; + } + + return result; +} \ No newline at end of file diff --git a/labs/SAW/Game/src/Game.h b/labs/SAW/Game/src/Game.h index 0328ed50..7f118bd3 100644 --- a/labs/SAW/Game/src/Game.h +++ b/labs/SAW/Game/src/Game.h @@ -62,13 +62,6 @@ uint32_t initDefaultPlayer(player_t* player); **/ uint32_t initDefaultSprite(character_t* character, sprite_t* sprite); -/** - Checks whether the referenced character's stats are at or below the MAX_STAT. - \param character_t* character - Pointer to the character in question. - \return SUCCESS when all stats are <= MAX_STAT, or FAILURE otherwise. -**/ -uint32_t checkStats(character_t* character); - /** Resolves a target's hp stat after an attack. \param character_t* target - Defender during the attack. @@ -77,5 +70,12 @@ uint32_t checkStats(character_t* character); **/ void resolveAttack(character_t* target, uint32_t atk); +/** + Checks whether the referenced character's stats are at or below the MAX_STAT. + \param character_t* character - Pointer to the character in question. + \return SUCCESS when all stats are <= MAX_STAT, or FAILURE otherwise. +**/ +uint32_t checkStats(character_t* character); + #endif From 556924f6fae12887a954ae1670449576f2170d70 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 23:42:43 +0000 Subject: [PATCH 106/120] Added steps for running the Game lab --- labs/SAW/SAW.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 85093c9d..67ef7e4d 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1072,7 +1072,16 @@ This example is from [here]() # Structs -In this section we learn how to verify code involving structs and global variables by analyzing a game. The code for the game can be found [here](./Game/src/). +In this section we will learn how to verify code involving structs by analyzing a game. The code for the game can be found [here](./Game/src/). + +To complete this lab, navigate to the [Game directory](./Game). In there, you'll notice the following: +- `Makefile`: Provides the necessary steps to generate our bitcode and run our SAW Python scripts. +- `src/`: Contains the source code we'll be analyzing +- `proof/`: Contains our Python scripts to run our SAW contracts. Your job will be to complete the `TODO` sections marked throughout [Game.cry](./Game/proof/Game.py). If you get stuck, you can refer to [Game_answers.cry](./Game/proof/Game_answers.py) or look at the discussions mentioned later in this markdown file! +- `specs/`: Contains our Cryptol specs that our SAW contracts can call. Feel free to add your own Cryptol functions to help you complete this lab! +- `DLC/`: Contains an extended version of this lab (think Downloadable Content) with even more Game functions for you to play with! While there aren't any lab worksheets configured for you in there, you can reference the contents for how to tackle additional functions. For more information regarding to what each function intends to teach, refer to [GameOutline.md](./Game/DLC/GameOutline.md). + +With that knowledge, make sure you have `start-saw-remote-api` running, open up Game.cry, fill out your answers, and test out your work by running `make`. Game on! ## Struct Initialization From aa6dfd083260634ebfdd00ade0054c2780997a30 Mon Sep 17 00:00:00 2001 From: djmorel Date: Thu, 2 Jun 2022 23:54:28 +0000 Subject: [PATCH 107/120] Updated DLC's GameOutline.md --- labs/SAW/Game/DLC/GameOutline.md | 305 ++++--------------------------- labs/SAW/SAW.md | 2 +- 2 files changed, 37 insertions(+), 270 deletions(-) diff --git a/labs/SAW/Game/DLC/GameOutline.md b/labs/SAW/Game/DLC/GameOutline.md index 68239ad1..ee256fe3 100644 --- a/labs/SAW/Game/DLC/GameOutline.md +++ b/labs/SAW/Game/DLC/GameOutline.md @@ -1,8 +1,16 @@ -# Initial Commit +# DLC + +This directory contains additional Game functions and SAW contracts for you to reference. You will notice: +- `Makefile`: Provides the necessary steps to generate our bitcode and run our SAW Python scripts. +- `src/`: Contains the source code we'll be analyzing +- `proof/`: Contains our Python scripts to run our SAW contracts. +- `specs/`: Contains our Cryptol specs that our SAW contracts can call. + +# DLC Functions Below is a list of functions included in Game/. -## levelUp(uint32_t level); +## levelUp(uint32_t level) **Goal:** Determine how to set up and verify a very simple function in a SAW contract. **Lessons Learned** @@ -10,12 +18,8 @@ Below is a list of functions included in Game/. - How to call a Cryptol function - How to pass SAW types to Cryptol function (curly braces) -**DJ's Notes:** -- This function was more of a proof of concept for me to get familiar with SAW's Python API. -- Lessons Learned here are already covered in previous SAW.md sections - -## getDefaultLevel(); +## getDefaultLevel() **Goal:** Determine how to handle global variables in a SAW contract. **Lessons Learned:** @@ -26,7 +30,7 @@ Below is a list of functions included in Game/. - When to pass no inputs to `execute_func` -## initDefaultPlayer(player_t* player); +## initDefaultPlayer(player_t* player) **Goal:** Determine how to setup a struct variable in a SAW contract. **Lessons Learned:** @@ -40,23 +44,9 @@ Below is a list of functions included in Game/. - Examples: `MAX_NAME_LENGTH`, `SUCCESS` - How to assert postconditions using `points_to` - Compiling clang with the `-g` flag provides debug symbols, so you can reference fields names rather than just indices -- SAW has issues using the `points_to` postcondition for describing behavior associated for a struct with many fields - - See the **Errors to Explore** section below - - This contract requires explicitly checking each and every struct field - -**Errors to Explore:** -- "Invalid label in record update" - - Generated when using the following postcondition (not using struct field names) -```python -self.points_to(player, cry_f("{{ repeat 0x41 : [{MAX_NAME_LENGTH}][8], 1 : [32], 10 : [32], 5 : [32], 4 : [32], 3 : [32] }}")) -``` -- "SAW doesn't yet support translating Cryptol's record type(s) into crucible-llvm's type system" - - Generated when using the following postcondition (with struct field names) -```python -self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), level=1 : [32], hp=10 : [32], atk=5 : [32], def=4 : [32], spd=3 : [32] }}")) -``` - -## checkStats(character_t* character); + + +## checkStats(character_t* character) **Goal:** Determine how to use parameterized contracts to set preconditions and postconditions. **Lessons Learned:** @@ -70,7 +60,7 @@ self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), le - How to use contract parameters to assert two possible postconditions - How to represent multiple Unit Tests on the same contract with different input parameters -**DJ's Notes:** +**Additional Notes:** - Note that the `checkStats` function would be used in the Game library to check character stats every instance before such stats would be referenced/used for gameplay. - In terms of the example, `checkStats` provides gameplay balancing by limiting how well characters can perform. - Given this policy, all other functions included in the library assume that `checkStats` is called before them. @@ -78,185 +68,33 @@ self.points_to(player, cry_f("{{ name=(repeat 0x41 : [{MAX_NAME_LENGTH}][8]), le - We will show an example in `resolveAttack` about what happens when the preconditions aren't used - Discuss the coding & security tradeoffs between checks made in callers vs callees -## resolveAttack(character_t* target, uint32_t atk); + +## resolveAttack(character_t* target, uint32_t atk) **Goal:** Expand upon the lessons learned with `checkStats` to get a contract ready for overrides/lemmas. **Lessons Learned:** - All of the points referenced in `checkStats` **Lessons Learned** - Understand how to write cases for a function with more than 2 possible behaviors - Understand when it is appropriate to include preconditions for functions that lack input checks - - Goes back to the caller vs callee tradeoffs mentioned in `checkStats` **DJ's Notes** - -**Errors to Explore:** -- Explain why the verification fails when the preconditions for `resolveAttack` are commented out. - - Spoiler alert, integer overflow/underflow/wrap-around -- Explain why SAW produces a counterexample when... - - the contract looks like: -```python -# Contract -class resolveAttack_Contract(Contract): - def __init__(self, case : int): - super().__init__() - self.case = case - # There are 3 possible cases for resolveAttack - # Case 2: Immediate KO - # Case 1: Attack mitigated - # Case 3: Regular attack - - def specification (self): - # Declare variables - (target, target_p) = ptr_to_fresh(self, alias_ty("struct.character_t"), name="target") - atk = self.fresh_var(i32, "atk") - - # Assert the precondition that the stats are below the max stat cap - self.precondition_f("{atk} <= `{MAX_STAT}") - self.precondition_f("{target}.2 <= `{MAX_STAT}") - self.precondition_f("{target}.4 <= `{MAX_STAT}") - - # Determine the preconditions based on the case parameter - if (self.case == 1): - # target->hp <= (atk - target->def) - self.precondition_f("({target}.2 + {target}.4) <= {atk}") - elif (self.case == 2): - # target->def >= atk - self.precondition_f("{target}.4 >= {atk}") - else: - # Assume any other case follows the formal attack calculation - self.precondition_f("{target}.4 < {atk}") - self.precondition_f("({target}.2 + {target}.4) > {atk}") - - # Symbolically execute the function - self.execute_func(target_p, atk) - - # Determine the postcondition based on the case parameter - if (self.case == 1): - self.points_to(target_p['hp'], cry_f("0 : [32]")) - elif (self.case == 2): - self.points_to(target_p['hp'], cry_f("{target}.2 : [32]")) - else: - self.points_to(target_p['hp'], cry_f("resolveAttack ({target}.2) ({target}.4) {atk}")) - - self.returns(void) -``` - - and the source code looks like: -```c -// Source code -void resolveAttack(character_t* target, uint32_t atk) -{ - if ( target->hp <= (atk - target->def) ) - { - // The attack will knock out the target - target->hp = 0; - } - else if ( target->def >= atk) - { - // The target's defense mitigates the attack - target->hp = target->hp; - } - else - { - // Calculate damage as normal - target->hp = target->hp - (atk - target->def); - } -} -``` - - Spoiler alert: Logical error in the source code where the first condition already covers the second condition - - -## selfDamage(player_t* player); -**Goal:** To provide a case study where SAW should have complained about its memory disjoint assertion being violated. Note that SAW's Python API silently resolves this issue. **Consider dropping from the SAW.md discussion**. Can still keep as a bonus example though! - - -## resetInventoryItems(inventory_t* inventory); + - Goes back to the caller vs callee tradeoffs mentioned in `checkStats` **Additional Notes** + + +## selfDamage(player_t* player) +**Goal:** To provide a case study where SAW should have complained about its memory disjoint assertion being violated. Note that SAW's Python API silently resolves this issue. + + +## resetInventoryItems(inventory_t* inventory) **Goal:** To show the problems SAW faces when verifying structs with pointer fields. **Lessons Learned:** - Consider rewriting the source code to avoid unallocated pointers as it makes SAW happy and reduces assumptions code makes (improves security) -**Errors to Explore:** -- Assuming the inventory_t struct is defined as: -```c -typedef struct { - item_t* item; - uint32_t numItems; -} inventory_t; -``` -understand why the following contract setups fail: - -Setup Attempt #1 -```python -class resetInventoryItems_Contract(Contract): - def __init__(self, numItems : int): - super().__init__() - self.numItems = numItems - - def specification (self): - # Declare variables - # Note: The setup here does not use item_p for the proof. However, item_p - # is included to show errors that can be encountered with the - # inventory_t struct. - (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") - inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item, cry_f("{self.numItems} : [32]"))) - - # Symbolically execute the function - self.execute_func(inventory_p) - - # Assert the postconditions - for i in range(self.numItems): - self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) - - self.returns(void) -``` - -```bash -... - ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): - error: types not memory-compatible: - { %struct.item_t*, i32 } - { [5 x { i32, i32 }], i32 } - stdout: -``` - -Setup Attempt #2 -```python -class resetInventoryItems_Contract(Contract): - def __init__(self, numItems : int): - super().__init__() - self.numItems = numItems - - def specification (self): - # Declare variables - # Note: The setup here does not use item_p for the proof. However, item_p - # is included to show errors that can be encountered with the - # inventory_t struct. - (item, item_p) = ptr_to_fresh(self, array_ty(self.numItems, alias_ty("struct.item_t")), name="item") - inventory_p = self.alloc(alias_ty("struct.inventory_t"), points_to = struct(item_p, cry_f("{self.numItems} : [32]"))) - - # Symbolically execute the function - self.execute_func(inventory_p) - - # Assert the postconditions - for i in range(self.numItems): - self.points_to(inventory_p['item'][i][1], cry_f("0 : [32]")) - - self.returns(void) -``` - -```bash - ⚠️ Failed to verify: lemma_resetInventoryItems_Contract (defined at proof/Game.py:190): - error: typeOfSetupValue: llvm_elem requires pointer to struct or array, found %struct.item_t** - stdout: - Considering both of these verification setup attempts, we can see that - defining inventory_t with an item_t pointer is tough for SAW to setup and. - prove. Consequently, it is better to use fixed array lengths for structs! -``` - - -## initScreen(screen_t* screen, uint8_t assetID); -**Goal:** To provide a case study where SAW should have complained about not knowing what nested defines resolve to. Note that SAW's Python API silently resolves this issue. **Consider dropping from the SAW.md discussion**. Can still keep as a bonus example though! - - -## setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx); + +## initScreen(screen_t* screen, uint8_t assetID) +**Goal:** To provide a case study where SAW should have complained about not knowing what nested defines resolve to. Note that SAW's Python API silently resolves this issue. + + +## setScreenTile(screen_t* screen, uint32_t screenIdx, uint32_t tableIdx) **Goal:** Demonstrate how to initialize an extern global array. **Lessons Learned:** @@ -266,85 +104,14 @@ class resetInventoryItems_Contract(Contract): - Understand when `ptr_to_fresh` vs `self.alloc` is needed - Repointing a pointer to a SAW variable (`screen_post`) in order to use that variable for postconditions -**Errors to Explore:** -- Why can't we just declare and pass the screen pointer, `screen_p`? -```python -class setScreenTile_Contract(Contract): - def __init__(self, shouldPass : bool): - super().__init__() - self.shouldPass = shouldPass - - def specification (self): - # Declare variables - screen_p = self.alloc(alias_ty("struct.screen_t")) - screenIdx = self.fresh_var(i32, "screenIdx") - tableIdx = self.fresh_var(i32, "tableIdx") - - # Initialize Game.h's assetTable according to Assets.c - self.points_to(global_var("assetTable"), cry_f("assetTable")) - - # Assert preconditions depending on the Contract parameter - if (self.shouldPass): - self.precondition_f("{screenIdx} < {SCREEN_TILES}") - self.precondition_f("{tableIdx} < {ASSET_TABLE_SIZE}") - else: - # Note: Only one of the following preconditions is needed - self.precondition_f("{screenIdx} >= {SCREEN_TILES}") - self.precondition_f("{tableIdx} >= {ASSET_TABLE_SIZE}") - - # Symbolically execute the function - self.execute_func(screen_p, screenIdx, tableIdx) - - # Since we just want to check one index, let's have our screen pointer - # point to a new screen_t variable. - screen_post = self.fresh_var(alias_ty("struct.screen_t"), "screen_post") - - # Assert that the original screen pointer now points to the new screen_t - # variable. This will allow us to reference screen_post in Cryptol for our - # later postconditions. - self.points_to(screen_p, screen_post) - - # Assert postconditions depending on the Contract parameter - if (self.shouldPass): - self.postcondition_f("({screen_post}@{screenIdx}) == assetTable@{tableIdx}") - self.returns_f("`({SUCCESS}) : [32]") - else: - self.returns_f("`({FAILURE}) : [32]") -``` - - -## quickBattle(player_t* player, character_t* opponent); + +## quickBattle(player_t* player, character_t* opponent) **Goal:** To show how to pass overrides (lemmas) to a Unit test. **Lessons Learned:** -- Must pass preconditions that match the preconditions included in the passed overrides (tied to **Errors to Explore**) +- Must pass preconditions that match the preconditions included in the passed overrides -**Errors to Explore:** -- Explain why `ptr_to_fresh` must be used over `self.alloc` -- Explain why the proof fails when the `MAX_STAT` preconditions are commented out (refer to the first point in **Lessons Learned**) - -**DJ's Notes:** +**Additional Notes:** - The function assumes that both the player and opponent have non-zero HP values, otherwise there wouldn't be a battle! - Explains why the `> 0` preconditions exist - -## counterBattle(player_t* player, character_t* opponent); -Note: No Contract for this one. Considering dropping due to how complex the Contract needs to be (multiple possible outcomes depending on the inputs - requires specific preconditions and postconditions) - - -# TODO -- Polish comments in all Game/ files - - Remove unused code when appropriate -- Define the background of the library (structs, functions, etc) -- Cryptol record/tuples ('(') for initDefaultPlayer -- Confirm the error cases yield the expected error results - - Necessary because some of these errors were encountered and resolved already. - - Want to properly recreate them for learning purposes. -- Determine if the resolveAttack logical error should be kept -- Determine if some functions should be dropped... - - Entirely: `counterBattle` - - It has so many SAW behaviors to consider (so fairly complicated), and I have not written a SAW contract for it - - Just from SAW.md: `selfDamage` - - Intended to be an example of field aliasing and show SAW complaining about "Memory not disjoint", but the Python API resolves it - - Just from SAW.md: `initScreen` - - Intended to be an example of where SAW cannot resolve nested defines, but the Python API resolves it diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 67ef7e4d..dd493414 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1081,7 +1081,7 @@ To complete this lab, navigate to the [Game directory](./Game). In there, you'll - `specs/`: Contains our Cryptol specs that our SAW contracts can call. Feel free to add your own Cryptol functions to help you complete this lab! - `DLC/`: Contains an extended version of this lab (think Downloadable Content) with even more Game functions for you to play with! While there aren't any lab worksheets configured for you in there, you can reference the contents for how to tackle additional functions. For more information regarding to what each function intends to teach, refer to [GameOutline.md](./Game/DLC/GameOutline.md). -With that knowledge, make sure you have `start-saw-remote-api` running, open up Game.cry, fill out your answers, and test out your work by running `make`. Game on! +With that knowledge, make sure you have `start-saw-remote-api` running, open up Game.cry, fill out your answers, and test your work by running `make`. Game on! ## Struct Initialization From fc04e8a241a670d8a203533aed6a78fc4574fc48 Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 00:01:03 +0000 Subject: [PATCH 108/120] Removed DLC README since GameDLC covers its info --- labs/SAW/Game/DLC/{GameOutline.md => GameDLC.md} | 2 +- labs/SAW/Game/DLC/README.md | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) rename labs/SAW/Game/DLC/{GameOutline.md => GameDLC.md} (99%) delete mode 100644 labs/SAW/Game/DLC/README.md diff --git a/labs/SAW/Game/DLC/GameOutline.md b/labs/SAW/Game/DLC/GameDLC.md similarity index 99% rename from labs/SAW/Game/DLC/GameOutline.md rename to labs/SAW/Game/DLC/GameDLC.md index ee256fe3..0783f5a7 100644 --- a/labs/SAW/Game/DLC/GameOutline.md +++ b/labs/SAW/Game/DLC/GameDLC.md @@ -1,4 +1,4 @@ -# DLC +# Game DLC This directory contains additional Game functions and SAW contracts for you to reference. You will notice: - `Makefile`: Provides the necessary steps to generate our bitcode and run our SAW Python scripts. diff --git a/labs/SAW/Game/DLC/README.md b/labs/SAW/Game/DLC/README.md deleted file mode 100644 index 7a368788..00000000 --- a/labs/SAW/Game/DLC/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Game DLC - -Contents in this directory provide an expanded example of what exists in the parent [Game](../) directory. - -[src/](./src) contains an expanded Game library with more functions for you to try out and make SAW contracts for. \ No newline at end of file From 0eddae8a56f12d2ff94218b12faf71e1c7dd8b1e Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 00:56:32 +0000 Subject: [PATCH 109/120] Added ceil(log2()) case study --- labs/SAW/SAW.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index dd493414..ac6d87b5 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1881,6 +1881,62 @@ class ceilLog2Contract(Contract): self.returns_f("lg2 {i}") ``` +We should note that `ceil(log2(i))` in C is NOT equal to Cryptol's `lg2` for values `1 << j + 1 where j >= 49`. + +To illustrate this disparity, consider the following: + +``` +Cryptol> let j49 = 1 << 49 + 1 : [64] +Cryptol> j49 +1125899906842624 +Cryptol> lg2 j49 +50 + +Cryptol> let j50 = 1 << 50 + 1 : [64] +Cryptol> j50 +2251799813685248 +Cryptol> lg2 j50 +51 +``` + +```c +#include +#include +#include + +int main() +{ + uint64_t j49 = 1125899906842624; // 1 << j + 1 where j == 49 + uint64_t j50 = 2251799813685248; // 1 << j + 1 where j == 50 + + printf("ceil(log2(%ld)) = %llu\n", j49, (unsigned long long)ceil(log2(j49))); + printf("ceil(log2(%ld)) = %llu\n", j50, (unsigned long long)ceil(log2(j50))); + + return 0; +} +``` + +``` +$ gcc -o log2Test log2Test.c +$ ./log2Test +ceil(log2(1125899906842624)) = 50 +ceil(log2(2251799813685248)) = 52 +``` + + +To account for this disparity, we could add a precondition to our SAW contract. + +```python +class ceilLog2Contract(Contract): + def specification(self): + i = self.fresh_var(i64, "i") + + self.precondition_f("{i} < 49") + + self.execute_func(i) + + self.returns_f("lg2 {i}") +``` In the unit test we would assume the `ceilLog2Contract`: From d4e63e9f135a6148ed790b3b9ad4ce1706bb58a7 Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 01:13:27 +0000 Subject: [PATCH 110/120] Updated findings for lg2 example --- labs/SAW/SAW.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index ac6d87b5..1172ffe0 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1881,7 +1881,7 @@ class ceilLog2Contract(Contract): self.returns_f("lg2 {i}") ``` -We should note that `ceil(log2(i))` in C is NOT equal to Cryptol's `lg2` for values `1 << j + 1 where j >= 49`. +We should note that `ceil(log2(i))` in C is NOT equal to Cryptol's `lg2` for values `1 << j + 1 where j > 49`. To illustrate this disparity, consider the following: @@ -1897,6 +1897,12 @@ Cryptol> j50 2251799813685248 Cryptol> lg2 j50 51 + +Cryptol> let j50m1 = j50 - 1 +Cryptol> j50m1 +2251799813685247 +Cryptol> lg2 j50m1 +51 ``` ```c @@ -1906,11 +1912,13 @@ Cryptol> lg2 j50 int main() { - uint64_t j49 = 1125899906842624; // 1 << j + 1 where j == 49 - uint64_t j50 = 2251799813685248; // 1 << j + 1 where j == 50 + uint64_t j49 = 1125899906842624; // 1 << j + 1 where j == 49 + uint64_t j50 = 2251799813685248; // 1 << j + 1 where j == 50 + uint64_t j50m1 = 2251799813685247; // j50 - 1 printf("ceil(log2(%ld)) = %llu\n", j49, (unsigned long long)ceil(log2(j49))); printf("ceil(log2(%ld)) = %llu\n", j50, (unsigned long long)ceil(log2(j50))); + printf("ceil(log2(%ld)) = %llu\n", j50m1, (unsigned long long)ceil(log2(j50m1))); return 0; } @@ -1921,9 +1929,9 @@ $ gcc -o log2Test log2Test.c $ ./log2Test ceil(log2(1125899906842624)) = 50 ceil(log2(2251799813685248)) = 52 +ceil(log2(2251799813685247)) = 52 ``` - To account for this disparity, we could add a precondition to our SAW contract. ```python @@ -1931,7 +1939,7 @@ class ceilLog2Contract(Contract): def specification(self): i = self.fresh_var(i64, "i") - self.precondition_f("{i} < 49") + self.precondition_f("lg2 {i} <= 49") self.execute_func(i) From 75f01bcffff91b77055bb1f94f2afeec06ce65c8 Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 01:21:40 +0000 Subject: [PATCH 111/120] Updated lg2 example --- labs/SAW/SAW.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 1172ffe0..09489673 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1932,21 +1932,21 @@ ceil(log2(2251799813685248)) = 52 ceil(log2(2251799813685247)) = 52 ``` -To account for this disparity, we could add a precondition to our SAW contract. +The lesson here, beware of `float`s claiming to mimic `int`s. To account for this disparity, we could add a precondition to our SAW contract. ```python class ceilLog2Contract(Contract): - def specification(self): - i = self.fresh_var(i64, "i") + def specification(self): + i = self.fresh_var(i64, "i") - self.precondition_f("lg2 {i} <= 49") + self.precondition_f("lg2 {i} <= 49") - self.execute_func(i) + self.execute_func(i) - self.returns_f("lg2 {i}") + self.returns_f("lg2 {i}") ``` -In the unit test we would assume the `ceilLog2Contract`: +In the unit test, we would assume the `ceilLog2Contract`: ``` log2i_assume = llvm_assume(mod, 'ceilLog2', ceilLog2Contract()) From ea40eb31d39ec2c947568ececd945bff1522db0d Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 01:30:56 +0000 Subject: [PATCH 112/120] Updated SAW.md based on comments --- labs/SAW/SAW.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 09489673..32eed352 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -368,9 +368,8 @@ Traceback (most recent call last): 🛑 The goal failed to verify. ``` -SAW alerted us about potentially undefined behavior mentioned in the -[C specification](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) -has the following to say about bit shifts: +SAW alerted us about potentially undefined behavior; the [C99 standard](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf) +specifies the following about bit shifts: > If the value of the right operand is negative or is greater than or > equal to the width of the promoted left operand, the behavior is @@ -533,8 +532,9 @@ Finally, `specification` must contain `self.returns` or `self.returns_f`, so we ### Python Helper Functions -To limit code reuse we can define helper functions in Python. For -example, the following construct is often used: +To limit code reuse we can define helper functions in Python. Code *reuse* +is good. Code *repetition* is bad! For example, the following construct +is often used: ```python def ptr_to_fresh(c : Contract, ty : LLVMType, name : Optional[str] = None, read_only : Optional[bool] = False) -> Tuple[FreshVar, SetupVal]: @@ -904,13 +904,10 @@ What do you think will happen if we run this code? def specification(self): (a, a_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="a") (b, b_p) = ptr_to_fresh(self, array_ty(self.length, i32), name="b", read_only=True) - length = fresh_var(i8, "length") - - self.precondition_f("{self.length} == {length}") - self.execute_func(a_p, b_p, length) + self.execute_func(a_p, b_p, cry(self.length)) - self.points_to(a_p, cry_f("rowAdd`{{{length}}} {a} {b}")) + self.points_to(a_p, cry_f("rowAdd`{{{self.length}}} {a} {b}")) self.returns(a_p) ``` From 0218c9c2352c12332ccdf42dcb3b94f0c5643c9e Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 01:37:17 +0000 Subject: [PATCH 113/120] Updated SAW.md based on comments --- labs/SAW/SAW.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 32eed352..76d1dad3 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1227,6 +1227,29 @@ self.points_to(player, cry_f("""( repeat 0x41 : [{MAX_NAME_LENGTH}][8], We use 3 double quotes `"""` in our `cry_f` call. This technique is useful when we want to separate our expected Cryptol-defined behaviors over multiple lines to improve code readability. Python considers the 3 double quotes as a multiline string. Multiline strings may also be used as block comments in Python. +Note that the setup we see above results in wide open spaces, which will be noticable when debugging strings passed to the family of `cry` functions. These spaces can be mitigated using `dedent` from the `textwrap` package that comes with Python. For example: + +```python +from textwrap import dedent +# ... + + self.returns(cry(dedent(""" + (y, z) + where + y = "foo" + z = "bar" + """).strip())) +``` + +This renders (without leading/trailing whitespace) as: + +``` +(y, z) + where + y = "foo" + z = "bar" +``` + While Cryptol's record types could also represent structs, SAW does not currently support translating Cryptol's record types into crucible-llvm's type system. If we tried to represent the struct as a Cryptol record like so: ```python From 2867137b8c768f8e9df415f80b8fa18f765b8a2b Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 21:47:09 -0400 Subject: [PATCH 114/120] Adding missing code fence formatting directives --- labs/SAW/SAW.md | 63 ++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 76d1dad3..920bebea 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -24,7 +24,7 @@ you are using the development container that comes with this course (`ghcr.io/weaversa/cryptol-course`), you can enter the following command in your terminal: -``` +```sh $ start-saw-remote-api ``` @@ -33,7 +33,7 @@ for SAW found in the [Installation lab](../../INSTALL.md). Once installed, to run `saw-remote-api`, enter the following commands into your terminal: -``` +```sh $ export SAW_SERVER_URL=http://0.0.0.0:36691 $ saw-remote-api http --host 0.0.0.0 --port 36691 & ``` @@ -605,7 +605,7 @@ def addRow5NewVar_Contract(Contract): Running a unit test yields the following error message: -``` +```sh [16:42:51.066] Subgoal failed: addRow5NewVar safety assertion: internal: error: in _SAW_verify_prestate SAWServer The following pointers had to alias, but they didn't: @@ -843,7 +843,7 @@ What do you think will happen if we run this code? ...but fails to verify the third contract. It alerts us there is a memory error - ``` + ```sh [15:40:51.527] Verifying addRowAlias ... [15:40:51.528] Simulating addRowAlias ... [15:40:51.532] Symbolic simulation completed with side conditions. @@ -855,7 +855,7 @@ What do you think will happen if we run this code? and even produces the following counterexample: - ``` + ```sh [15:40:51.575] SolverStats {solverStatsSolvers = fromList ["W4 ->z3"], solverStatsGoalSize = 331} [15:40:51.575] ----------Counterexample---------- [15:40:51.575] length0: 6 @@ -895,7 +895,7 @@ What do you think will happen if we run this code? SAW is telling us we forgot to add a precondition to assert our symbolic `length` agrees with our Python parameter `self.length`. This is an easy fix: - ``` + ```sh def addRowAlias_Contract(Contract): def __init__(self, length : int): super().__init__() @@ -981,7 +981,7 @@ int f(int *x) { The following contract has possibly unexpected behavior (in C booleans are encoded as `0` for false and `1` for true). -```Python +```python import unittest from saw_client import * from saw_client.crucible import * @@ -1030,7 +1030,7 @@ if __name__ == "__main__": ``` The counterexample produced may seem mystical. - ``` + ```sh [17:21:44.725] ----------Counterexample---------- [17:21:44.725] <> [17:21:44.725] ---------------------------------- @@ -1156,7 +1156,7 @@ class initDefaultPlayer_Contract(Contract): For every C symbol defined using `#define` we make a corresponding Python global variable. -``` +```python MAX_NAME_LENGTH = 12 SUCCESS = 170 FAILURE = 85 @@ -1164,7 +1164,7 @@ FAILURE = 85 The command `alias_ty("struct.")` creates a type corresponding to the structure, e.g., -```python3 +```python player = self.alloc(alias_ty("struct.player_t")) ``` creates a symbolic pointer variable `player` pointing to a structure of type `player_t`. @@ -1243,7 +1243,7 @@ from textwrap import dedent This renders (without leading/trailing whitespace) as: -``` +```python (y, z) where y = "foo" @@ -1263,9 +1263,9 @@ self.points_to(player, cry_f("""{{ name = repeat 0x41 : [{MAX_NAME_LENGTH}][8], SAW would return this error: -``` -clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c -python3 proof/Game.py +```sh +$ clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c +$ python3 proof/Game.py ⚠️ Failed to verify: lemma_initDefaultPlayer_Contract (defined at proof/Game.py:537): error: SAW doesn't yet support translating Cryptol's record type(s) into crucible-llvm's type system. stdout: @@ -1294,7 +1294,7 @@ If a Cryptol specification uses a record type to represent structs, then we can We can also explicitly define a struct in SAW. Let's consider another struct: -```c +```C #define GAITS 2 #define DIRECTIONS 4 #define ANIMATION_STEPS 3 @@ -1317,7 +1317,7 @@ Alright, that's enough of a crash course in animation. Let's get back to our str With that understanding, let's consider a function that uses the `sprite_t` struct: -```c +```C uint32_t initDefaultSprite(character_t* character, sprite_t* sprite) { // Initialize the character to the passed pointer @@ -1418,7 +1418,7 @@ Feeling pretty confident with our little `player_t` and `character_t` structs? H Consider the following function: -```c +```C void resolveAttack(character_t* target, uint32_t atk) { if ( target->def >= atk) @@ -1620,7 +1620,7 @@ make: *** [Makefile:14: all] Error 1 A counterexample?! Let's take a closer look at what SAW provides us and reassess our strategy. -``` +```sh [01:59:53.689] ----------Counterexample---------- [01:59:53.689] target0: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 4294967293, 0, 3, 0) [01:59:53.689] atk0: 4294967295 @@ -1637,7 +1637,7 @@ Notice that our unit case failed for case 2, but passed for cases 1 and 3. How a and if we plug in the counterexample values SAW provides us... -``` +```sh {target}.2 + {target}.4 <= {atk} 4294967293 + 3 <= 4294967295 4294967296 <= 4294967295 @@ -1647,7 +1647,7 @@ Well that doesn't make sense, now does it? Well actually, it does make some sens Taking that into consideration, then our efforts to test out the counterexample was incorrect. We forgot to account for integer overflow! -``` +```sh 4294967293 + 3 <= 4294967295 4294967295 + 1 <= 4294967295 0 <= 4294967295 @@ -1659,7 +1659,7 @@ Taking that into consideration, then our efforts to test out the counterexample self.precondition_f("({target}.2 + {target}.4) <= {atk}") ``` -```c +```C else if ( target->hp <= (atk - target->def) ) ``` @@ -1735,7 +1735,7 @@ make: *** [Makefile:14: all] Error 1 Nope, that didn't work either. But hey, SAW gave us a different counterexample. Let's look at that one: -``` +```sh [02:34:39.509] ----------Counterexample---------- [02:34:39.509] target0: ([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 134217728, 0, 4184634256, 0) [02:34:39.509] atk0: 23884688 @@ -1744,7 +1744,7 @@ Nope, that didn't work either. But hey, SAW gave us a different counterexample. Plugging those values into our updated case 2 precondition: -``` +```sh {target}.2 <= ({atk} - {target}.4) 134217728 <= 23884688 - 4184634256 134217728 <= 134217728 @@ -1752,7 +1752,7 @@ Plugging those values into our updated case 2 precondition: Nice, we got an integer underflow counterexample. Based on these input values, the source code would actually meet the first if condition: -```c +```C target->def >= atk ``` @@ -1795,7 +1795,7 @@ Whoo hoo! We finally won the battle! From our exercise, we found that SAW provid Now let's imagine that very large character stats is a concern in our game. In order to balance characters in our game and avoid problems with very large values, let's say we decided to add a function (`checkStats`) that checks character stats. Let's also assume that this function is always called before functions that use those stats like what we saw in `resolveAttack`. -```c +```C uint32_t checkStats(character_t* character) { // Assume failure by default @@ -1905,7 +1905,7 @@ We should note that `ceil(log2(i))` in C is NOT equal to Cryptol's `lg2` for val To illustrate this disparity, consider the following: -``` +```Xcryptol-session Cryptol> let j49 = 1 << 49 + 1 : [64] Cryptol> j49 1125899906842624 @@ -1925,7 +1925,7 @@ Cryptol> lg2 j50m1 51 ``` -```c +```C #include #include #include @@ -1944,7 +1944,7 @@ int main() } ``` -``` +```sh $ gcc -o log2Test log2Test.c $ ./log2Test ceil(log2(1125899906842624)) = 50 @@ -1968,22 +1968,21 @@ class ceilLog2Contract(Contract): In the unit test, we would assume the `ceilLog2Contract`: -``` +```python log2i_assume = llvm_assume(mod, 'ceilLog2', ceilLog2Contract()) - ``` If a C function ever used `ceilLog2`, then we could pass in the assumption as an optional argument to verification: -``` +```python getHeight_result = llvm_verify(mod, 'getHeight', getHeightContract(), lemmas=[ceilLog2_assume]) ``` The optional argument is a list because we can supply multiple assumptions or previous verifications. For example, we might have -``` +```python addNode_result = llvm_verify(mod, 'addNode', addNodeContract(), lemmas=[getHeight_result]) removeNode_result = llvm_verify(mod, 'removeNode', removeNodeContract(), lemmas=[getHeight_result]) addOneRemoveTwo_result = llvm_verify(mod, 'addOneRemoveTwo', addOneRemoveTwo(), lemmas=[addNode_result, removeNode_result]) From 75dd87954487d602bfaaa25a76f41ceb9aea936e Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 01:48:40 +0000 Subject: [PATCH 115/120] Updated initDefaultSprite_Contract for consistency --- labs/SAW/Game/DLC/proof/Game.py | 10 +++++----- labs/SAW/Game/proof/Game_answers.py | 6 +++--- labs/SAW/SAW.md | 12 ++++++------ 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/labs/SAW/Game/DLC/proof/Game.py b/labs/SAW/Game/DLC/proof/Game.py index 8bcc073c..bd5cf71b 100644 --- a/labs/SAW/Game/DLC/proof/Game.py +++ b/labs/SAW/Game/DLC/proof/Game.py @@ -108,8 +108,8 @@ def specification (self): class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables - character_p = self.alloc(alias_ty("struct.character_t")) # For Option 1 - #character_p = self.fresh_var(ptr_ty(alias_ty("struct.character_t"))) # For Option 2 + character = self.alloc(alias_ty("struct.character_t")) # For Option 1 + #character = self.fresh_var(ptr_ty(alias_ty("struct.character_t"))) # For Option 2 tempCharacter_p = self.alloc(alias_ty("struct.character_t")) ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) frames = self.fresh_var(ty, "sprite.frames") @@ -118,16 +118,16 @@ def specification (self): sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) # Symbolically execute the function - self.execute_func(character_p, sprite_p) + self.execute_func(character, sprite_p) # Assert postconditions # Option 1: Struct Strategy - self.points_to(sprite_p, struct( character_p, + self.points_to(sprite_p, struct( character, cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), cry_f("1 : [32]"), cry_f("2 : [32]") )) # Option 2: Full Tuple Strategy - #self.points_to(sprite_p, cry_f("""( {character_p}, + #self.points_to(sprite_p, cry_f("""( {character}, # zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], # 1 : [32], # 2 : [32]) """)) diff --git a/labs/SAW/Game/proof/Game_answers.py b/labs/SAW/Game/proof/Game_answers.py index e6b52f5b..1f83c855 100644 --- a/labs/SAW/Game/proof/Game_answers.py +++ b/labs/SAW/Game/proof/Game_answers.py @@ -67,7 +67,7 @@ def specification (self): class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables - character_p = self.alloc(alias_ty("struct.character_t")) + character = self.alloc(alias_ty("struct.character_t")) tempCharacter_p = self.alloc(alias_ty("struct.character_t")) ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) frames = self.fresh_var(ty, "sprite.frames") @@ -76,10 +76,10 @@ def specification (self): sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) # Symbolically execute the function - self.execute_func(character_p, sprite_p) + self.execute_func(character, sprite_p) # Assert postconditions - self.points_to(sprite_p, struct( character_p, + self.points_to(sprite_p, struct( character, cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), cry_f("1 : [32]"), cry_f("2 : [32]") )) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 76d1dad3..edee02f7 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1359,10 +1359,10 @@ class initDefaultSprite_Contract(Contract): self.points_to(sprite_p, sprite) # Symbolically execute the function - self.execute_func(character_p, sprite_p) + self.execute_func(character, sprite_p) # Assert postconditions - self.points_to(sprite_p, struct( character_p, + self.points_to(sprite_p, struct( character, cry_f("zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8]"), cry_f("1 : [32]"), cry_f("2 : [32]") )) @@ -1385,13 +1385,13 @@ since we don't use `sprite` later in the code. If we wanted, we could assert oth In the postcondition, we assert `sprite_p` points to some concrete structure. The benefit of using explicit structs is that it allows us to represent pointer fields that may be present in a struct. -However, explicit structs isn't the only way to represent pointer fields. We could also use a tuple to assert our postcondition. However, we will need to change our definition for `character_p`. +However, explicit structs isn't the only way to represent pointer fields. We could also use a tuple to assert our postcondition. However, we will need to change our definition for `character`. ```python class initDefaultSprite_Contract(Contract): def specification (self): # Declare variables - character_p = self.fresh_var(ptr_ty(alias_ty("struct.character_t"))) + character = self.fresh_var(ptr_ty(alias_ty("struct.character_t"))) tempCharacter_p = self.alloc(alias_ty("struct.character_t")) ty = array_ty(GAITS, array_ty(DIRECTIONS, array_ty(ANIMATION_STEPS, i8))) frames = self.fresh_var(ty, "sprite.frames") @@ -1400,10 +1400,10 @@ class initDefaultSprite_Contract(Contract): sprite_p = self.alloc(alias_ty("struct.sprite_t"), points_to = struct(tempCharacter_p, frames, xPos, yPos)) # Symbolically execute the function - self.execute_func(character_p, sprite_p) + self.execute_func(character, sprite_p) # Assert postconditions - self.points_to(sprite_p, cry_f("""( {character_p}, + self.points_to(sprite_p, cry_f("""( {character}, zero : [{GAITS}][{DIRECTIONS}][{ANIMATION_STEPS}][8], 1 : [32], 2 : [32]) """)) From 515fe11df4aabeffa56ad3d5a6d70cfe4b21e9b9 Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 01:57:20 +0000 Subject: [PATCH 116/120] Added CLZ suggestion for lg2 example --- labs/SAW/SAW.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index c3af6bdc..fef59be7 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1992,7 +1992,13 @@ One can think of lemmas as rewrite rules under the hood. Whenever SAW encounters ceilLog2 function in the C it will interpret its behavior as what is specified in the `ceilLog2Contract()`. -**Exercise**: Does the `C` function `ceilLog2` as defined above always yield the behavior outlined in `ceilLog2Contract`? That is, was our assumption a fair one to make? If not, change the `C` code (possibly using different library functions) to match `lg2` in Cryptol. +**Exercise**: Does the `C` function `ceilLog2` as defined above always yield the behavior outlined in `ceilLog2Contract`? That is, was our assumption a fair one to make? If not, change the `C` code (possibly using different library functions) to match `lg2` in Cryptol and verify it! + +Hint: consider [CLZ](https://en.wikipedia.org/wiki/Find_first_set#CLZ). + +```c +int __builtin_clzll (unsigned long long) +``` ## Uninterpreting Functions From 6226ce503eaa33237cb5de97443678870d9725ce Mon Sep 17 00:00:00 2001 From: djmorel Date: Fri, 3 Jun 2022 02:03:58 +0000 Subject: [PATCH 117/120] Added a missing reference --- labs/SAW/SAW.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index fef59be7..9ba3308e 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -1064,7 +1064,7 @@ if __name__ == "__main__": ``` -This example is from [here]() +This example is from the [GaloisInc/saw-script repo](https://github.com/GaloisInc/saw-script/blob/master/saw-remote-api/python/tests/saw/test_llvm_assert_null.py). # Structs From ac90032de3f4aa826e5423e72d770ce6c638c453 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 22:04:23 -0400 Subject: [PATCH 118/120] added prereqs --- labs/SAW/SAW.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/labs/SAW/SAW.md b/labs/SAW/SAW.md index 920bebea..ffed3b23 100644 --- a/labs/SAW/SAW.md +++ b/labs/SAW/SAW.md @@ -16,6 +16,31 @@ must have at certain stages in a software development pipeline. Some industrial examples include [AWS's s2n](https://link.springer.com/chapter/10.1007/978-3-319-96142-2_26) and [Supranational's BLST](https://github.com/GaloisInc/BLST-Verification). +## Prerequisites + +Before working through this lab, you'll need + * A recent version of Python, Cryptol, SAW, saw-remote-api, SAW's Python client package, and the clang C compiler to be installed and + * an editor for completing the exercises in this file. + +You'll also need experience with the following languages + * Python + * Cryptol + * SAW + * C + +## Skills You'll Learn + +By the end of this lab you will be comfortable using SAW's remote API +to formally verify cryptographic implementations. Since the bulk of +work with SAW lies in crafting linkages between the source language +and spec, this lab is filled with examples of different types of data +structures and how to represent them in SAW. Hence, this lab also +exists as a kind of reference that you may return to when using these +tools. As well, there are some examples +[here](https://github.com/GaloisInc/saw-script/tree/master/saw-remote-api/python/tests) +and a full-featured tutorial on SAW +[here](https://saw.galois.com/intro/). + # Setting Everything Up To run any of the examples in this lab, you need to first start the From 0c788766df2cb26e66296953fa338297659b61ae Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 22:12:09 -0400 Subject: [PATCH 119/120] Adding Game to ci --- .github/workflows/python-saw.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/python-saw.yml b/.github/workflows/python-saw.yml index 6141fe7d..a7c05e73 100644 --- a/.github/workflows/python-saw.yml +++ b/.github/workflows/python-saw.yml @@ -63,3 +63,5 @@ jobs: - run: cd labs/SAW/proof && clang ../src/addRow.c -o ../src/addRow.bc -c -emit-llvm && python3 addRow.py # Run null SAW Script - run: cd labs/SAW/proof && clang ../src/null.c -o ../src/null.bc -c -emit-llvm && python3 null.py + # Run Game SAW Script + - run: labs/SAW/Game/DLC && mkdir artifacts && clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c && python3 proof/Game.py From e90489f680a7b315759567d83b221c2c4638d0a9 Mon Sep 17 00:00:00 2001 From: Sean Weaver Date: Thu, 2 Jun 2022 22:15:36 -0400 Subject: [PATCH 120/120] ci fix --- .github/workflows/python-saw.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-saw.yml b/.github/workflows/python-saw.yml index a7c05e73..369128f3 100644 --- a/.github/workflows/python-saw.yml +++ b/.github/workflows/python-saw.yml @@ -64,4 +64,4 @@ jobs: # Run null SAW Script - run: cd labs/SAW/proof && clang ../src/null.c -o ../src/null.bc -c -emit-llvm && python3 null.py # Run Game SAW Script - - run: labs/SAW/Game/DLC && mkdir artifacts && clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c && python3 proof/Game.py + - run: cd labs/SAW/Game/DLC && mkdir artifacts && clang -c -g -emit-llvm -o artifacts/Game.bc src/Game.c && python3 proof/Game.py