Skip to content

Commit a504983

Browse files
authored
Merge pull request #4351 from xbauch/feature/memory-snapshot-harness-havoc
Memory snapshot harness havoc
2 parents b1c55b0 + ceaafdf commit a504983

File tree

11 files changed

+877
-9
lines changed

11 files changed

+877
-9
lines changed

doc/cprover-manual/goto-harness.md

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ having to manually construct an appropriate environment.
1313

1414
We have two types of harnesses we can generate for now:
1515

16-
* The `memory-snapshot` harness. TODO: Daniel can document this.
1716
* The `function-call` harness, which automatically synthesises an environment
1817
without having any information about the program state.
18+
* The `memory-snapshot` harness, which loads an existing program state (in form
19+
of a JSON file) and selectively _havoc-ing_ some variables.
1920

2021
It is used similarly to how `goto-instrument` is used by modifying an existing
2122
GOTO binary, as produced by `goto-cc`.
@@ -309,4 +310,103 @@ main.c function function
309310

310311
** 0 of 1 failed (1 iterations)
311312
VERIFICATION SUCCESSFUL
312-
```
313+
```
314+
315+
### The memory snapshot harness
316+
317+
The `function-call` harness is used in situations in which we want the analysed
318+
function to work in arbitrary environment. If we want to analyse a function
319+
starting from a _real_ program state, we can call the `memory-snapshot` harness
320+
instead.
321+
322+
Furthermore, the program state of interest may be taken at a particular location
323+
within a function. In that case we do not want the harness to instrument the
324+
whole function but rather to allow starting the execution from a specific
325+
initial location (specified via `--initial-location func[:<n>]`). Note that the
326+
initial location does not have to be the first instruction of a function: we can
327+
also specify the _location number_ `n` to set the initial location inside our
328+
function. The _location numbers_ do not have to coincide with the lines of the
329+
program code. To find the _location number_ run CBMC with
330+
`--show-goto-functions`. Most commonly, the _location number_ is the instruction
331+
of the break-point used to extract the program state for the memory snapshot.
332+
333+
Say we want to check the assertion in the following code:
334+
335+
```C
336+
// main.c
337+
#include <assert.h>
338+
339+
unsigned int x;
340+
unsigned int y;
341+
342+
unsigned int nondet_int() {
343+
unsigned int z;
344+
return z;
345+
}
346+
347+
void checkpoint() {}
348+
349+
unsigned int complex_function_which_returns_one() {
350+
unsigned int i = 0;
351+
while(++i < 1000001) {
352+
if(nondet_int() && ((i & 1) == 1))
353+
break;
354+
}
355+
return i & 1;
356+
}
357+
358+
void fill_array(unsigned int* arr, unsigned int size) {
359+
for (unsigned int i = 0; i < size; i++)
360+
arr[i]=nondet_int();
361+
}
362+
363+
unsigned int array_sum(unsigned int* arr, unsigned int size) {
364+
unsigned int sum = 0;
365+
for (unsigned int i = 0; i < size; i++)
366+
sum += arr[i];
367+
return sum;
368+
}
369+
370+
const unsigned int array_size = 100000;
371+
372+
int main() {
373+
x = complex_function_which_returns_one();
374+
unsigned int large_array[array_size];
375+
fill_array(large_array, array_size);
376+
y = array_sum(large_array, array_size);
377+
checkpoint();
378+
assert(y + 2 > x);
379+
return 0;
380+
}
381+
```
382+
383+
But are not particularly interested in analysing the complex function, since we
384+
trust that its implementation is correct. Hence we run the above program
385+
stopping after the assignments to `x` and `x` and storing the program state,
386+
e.g. using the `memory-analyzer`, in a JSON file `snapshot.json`. Then run the
387+
harness and verify the assertion with:
388+
389+
```
390+
$ goto-cc -o main.gb main.c
391+
$ goto-harness \
392+
--harness-function-name harness \
393+
--harness-type initialise-with-memory-snapshot \
394+
--memory-snapshot snapshot.json \
395+
--initial-location checkpoint \
396+
--havoc-variables x \
397+
main.gb main-mod.gb
398+
$ cbmc --function harness main-mod.gb
399+
```
400+
401+
This will result in:
402+
403+
```
404+
[...]
405+
406+
** Results:
407+
main.c function main
408+
[main.assertion.1] line 42 assertion y + 2 > x: SUCCESS
409+
410+
** 0 of 1 failed (1 iterations)
411+
VERIFICATION SUCCESSFUL
412+
```
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
int x = 1;
2+
3+
int main()
4+
{
5+
assert(x == 1);
6+
7+
return 0;
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--harness-type initialise-with-memory-snapshot --memory-snapshot ../load-snapshot-json-snapshots/global-int-x-1-snapshot.json --initial-location main:0 --havoc-variables x
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
\[main.assertion.1\] line [0-9]+ assertion x == 1: FAILURE
7+
--
8+
^warning: ignoring
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#include <assert.h>
2+
3+
unsigned int x;
4+
unsigned int y;
5+
6+
unsigned int nondet_int()
7+
{
8+
unsigned int z;
9+
return z;
10+
}
11+
12+
void checkpoint()
13+
{
14+
}
15+
16+
unsigned int complex_function_which_returns_one()
17+
{
18+
unsigned int i = 0;
19+
while(++i < 1000001)
20+
{
21+
if(nondet_int() && ((i & 1) == 1))
22+
break;
23+
}
24+
return i & 1;
25+
}
26+
27+
void fill_array(unsigned int *arr, unsigned int size)
28+
{
29+
for(unsigned int i = 0; i < size; i++)
30+
arr[i] = nondet_int();
31+
}
32+
33+
unsigned int array_sum(unsigned int *arr, unsigned int size)
34+
{
35+
unsigned int sum = 0;
36+
for(unsigned int i = 0; i < size; i++)
37+
sum += arr[i];
38+
return sum;
39+
}
40+
41+
const unsigned int array_size = 100000;
42+
43+
int main()
44+
{
45+
x = complex_function_which_returns_one();
46+
unsigned int large_array[array_size];
47+
fill_array(large_array, array_size);
48+
y = array_sum(large_array, array_size);
49+
checkpoint();
50+
assert(y + 2 > y); //y is nondet -- may overflow
51+
assert(0);
52+
return 0;
53+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--harness-type initialise-with-memory-snapshot --memory-snapshot ../load-snapshot-json-snapshots/global-int-x-y-snapshot.json --initial-location main:9 --havoc-variables y
4+
^\[main.assertion.1\] line \d+ assertion y \+ 2 > y: FAILURE$
5+
^\[main.assertion.2\] line \d+ assertion 0: FAILURE$
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
^warning: ignoring
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <assert.h>
2+
3+
struct simple_str
4+
{
5+
int i;
6+
int j;
7+
} simple;
8+
9+
void checkpoint()
10+
{
11+
}
12+
13+
int main()
14+
{
15+
simple.i = 1;
16+
simple.j = 2;
17+
18+
checkpoint();
19+
assert(simple.j > simple.i);
20+
return 0;
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--harness-type initialise-with-memory-snapshot --memory-snapshot ../load-snapshot-json-snapshots/global-struct-snapshot.json --initial-location main:3 --havoc-variables simple
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.assertion.1\] line \d+ assertion simple.j > simple.i: FAILURE$
7+
--
8+
^warning: ignoring

0 commit comments

Comments
 (0)