@@ -13,9 +13,10 @@ having to manually construct an appropriate environment.
13
13
14
14
We have two types of harnesses we can generate for now:
15
15
16
- * The ` memory-snapshot ` harness. TODO: Daniel can document this.
17
16
* The ` function-call ` harness, which automatically synthesises an environment
18
17
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.
19
20
20
21
It is used similarly to how ` goto-instrument ` is used by modifying an existing
21
22
GOTO binary, as produced by ` goto-cc ` .
@@ -309,4 +310,97 @@ main.c function function
309
310
310
311
** 0 of 1 failed (1 iterations)
311
312
VERIFICATION SUCCESSFUL
312
- ```
313
+ ```
314
+
315
+ ### The memory snapshot harness
316
+
317
+ The `function-call` harness is used in situations when we want the analysed
318
+ function to work in arbitrary environment. If we want a more restricted
319
+ environment or if we have the program in which our function will only be called,
320
+ we call the `memory-snapshot` harness instead.
321
+
322
+ Furthermore, the program state of interest may be taken 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>]`).
326
+
327
+ Say we want to check the assertion in the following code:
328
+
329
+ ```C
330
+ // main.c
331
+ #include <assert.h>
332
+
333
+ unsigned int x;
334
+ unsigned int y;
335
+
336
+ unsigned int nondet_int() {
337
+ unsigned int z;
338
+ return z;
339
+ }
340
+
341
+ void checkpoint() {}
342
+
343
+ unsigned int complex_function_which_returns_one() {
344
+ unsigned int i = 0;
345
+ while(++i < 1000001) {
346
+ if(nondet_int() && ((i & 1) == 1))
347
+ break;
348
+ }
349
+ return i & 1;
350
+ }
351
+
352
+ void fill_array(unsigned int* arr, unsigned int size) {
353
+ for (unsigned int i = 0; i < size; i++)
354
+ arr[i]=nondet_int();
355
+ }
356
+
357
+ unsigned int array_sum(unsigned int* arr, unsigned int size) {
358
+ unsigned int sum = 0;
359
+ for (unsigned int i = 0; i < size; i++)
360
+ sum += arr[i];
361
+ return sum;
362
+ }
363
+
364
+ const unsigned int array_size = 100000;
365
+
366
+ int main() {
367
+ x = complex_function_which_returns_one();
368
+ unsigned int large_array[array_size];
369
+ fill_array(large_array, array_size);
370
+ y = array_sum(large_array, array_size);
371
+ checkpoint();
372
+ assert(y + 2 > x);
373
+ return 0;
374
+ }
375
+ ```
376
+
377
+ But are not particularly interested in the analysing the complex function, since
378
+ we trust that its implementation is correct. Hence we run the above program
379
+ stopping after the assignments to ` x ` and ` x ` and storing the program state,
380
+ e.g. using the ` memory-analyzer ` , in a JSON file ` snapshot.json ` . Then run the
381
+ harness and verify the assertion with:
382
+
383
+ ```
384
+ $ goto-cc -o main.gb main.c
385
+ $ goto-harness \
386
+ --harness-function-name harness \
387
+ --harness-type initialise-with-memory-snapshot \
388
+ --memory-snapshot snapshot.json \
389
+ --initial-location checkpoint \
390
+ --havoc-variables x \
391
+ main.gb main-mod.gb
392
+ $ cbmc --function harness main-mod.gb
393
+ ```
394
+
395
+ This will result in:
396
+
397
+ ```
398
+ [...]
399
+
400
+ ** Results:
401
+ main.c function main
402
+ [main.assertion.1] line 42 assertion y + 2 > x: SUCCESS
403
+
404
+ ** 0 of 1 failed (1 iterations)
405
+ VERIFICATION SUCCESSFUL
406
+ ```
0 commit comments