|
| 1 | +\ingroup module_hidden |
| 2 | +\page nondet-volatile Modelling of Volatile Variables |
| 3 | + |
| 4 | +This document describes the options available in goto-instrument for modelling |
| 5 | +the behaviour of volatile variables. The following options are provided: |
| 6 | + |
| 7 | +- `--nondet-volatile` |
| 8 | +- `--nondet-volatile-variable <variable>` |
| 9 | +- `--nondet-volatile-model <variable>:<model>` |
| 10 | + |
| 11 | +By default, cbmc treats volatile variables the same as non-volatile variables. |
| 12 | +That is, it assumes that a volatile variable does not change between subsequent |
| 13 | +reads, unless it was written to by the program. With the above options, |
| 14 | +goto-instrument can be instructed to instrument the given goto program such as |
| 15 | +to (1) make reads from all volatile expressions non-deterministic |
| 16 | +(`--nondet-volatile`), (2) make reads from specific variables non-deterministic |
| 17 | +(`--nondet-volatile-variable`), or (3) model reads from specific variables by |
| 18 | +given models (`--nondet-volatile-model`). |
| 19 | + |
| 20 | +Below we give two usage examples for the options. Consider the following test, |
| 21 | +for function `get_celsius()` and with harness `test_get_celsius()`: |
| 22 | + |
| 23 | +```C |
| 24 | +#include <assert.h> |
| 25 | +#include <limits.h> |
| 26 | +#include <stdint.h> |
| 27 | + |
| 28 | +// hardware sensor for temperature in kelvin |
| 29 | +extern volatile uint16_t temperature; |
| 30 | + |
| 31 | +int get_celsius() |
| 32 | +{ |
| 33 | + if(temperature > (1000 + 273)) |
| 34 | + { |
| 35 | + return INT_MIN; // value indicating error |
| 36 | + } |
| 37 | + |
| 38 | + return temperature - 273; |
| 39 | +} |
| 40 | + |
| 41 | +void test_get_celsius() |
| 42 | +{ |
| 43 | + int t = get_celsius(); |
| 44 | + |
| 45 | + assert(t == INT_MIN || t <= 1000); |
| 46 | + assert(t == INT_MIN || t >= -273); |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +Here the variable `temperature` corresponds to a hardware sensor. It returns |
| 51 | +the current temperature on each read. The `get_celsius()` function converts the |
| 52 | +value in Kelvin to degrees Celsius, given the value is in the expected range. |
| 53 | +However, it has a bug where it reads `temperature` a second time after the |
| 54 | +check, which may yield a value for which the check would not succeed. Verifying |
| 55 | +this program as is with cbmc would yield a verification success. We can use |
| 56 | +goto-instrument to make reads from `temperature` non-deterministic: |
| 57 | + |
| 58 | +``` |
| 59 | +goto-cc -o get_celsius_test.gb get_celsius_test.c |
| 60 | +goto-instrument --nondet-volatile-variable temperature get_celsius_test.gb get_celsius_test-mod.gb |
| 61 | +cbmc --function test_get_celsius get_celsius_test-mod.gb |
| 62 | +``` |
| 63 | + |
| 64 | +Here the final invocation of cbmc correctly reports a verification failure. |
| 65 | + |
| 66 | +However, simply treating volatile variables as non-deterministic may for some |
| 67 | +use cases be too inaccurate. Consider the following test, for function |
| 68 | +`get_message()` and with harness `test_get_message()`: |
| 69 | + |
| 70 | +```C |
| 71 | +#include <assert.h> |
| 72 | +#include <stdint.h> |
| 73 | + |
| 74 | +extern volatile uint32_t clock; |
| 75 | + |
| 76 | +typedef struct message { |
| 77 | + uint32_t timestamp; |
| 78 | + void *data; |
| 79 | +} message_t; |
| 80 | + |
| 81 | +void *read_data(); |
| 82 | + |
| 83 | +message_t get_message() |
| 84 | +{ |
| 85 | + message_t msg; |
| 86 | + |
| 87 | + msg.timestamp = clock; |
| 88 | + msg.data = read_data(); |
| 89 | + |
| 90 | + return msg; |
| 91 | +} |
| 92 | + |
| 93 | +void test_get_message() |
| 94 | +{ |
| 95 | + message_t msg1 = get_message(); |
| 96 | + message_t msg2 = get_message(); |
| 97 | + |
| 98 | + assert(msg1.timestamp <= msg2.timestamp); |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +The harness verifies that `get_message()` assigns non-decreasing timestamps to |
| 103 | +the returned messages. However, simply treating `clock` as non-deterministic |
| 104 | +would not allow to prove this property. Thus, we can supply a model for reads |
| 105 | +from `clock`: |
| 106 | + |
| 107 | +```C |
| 108 | +// model for reads of the variable clock |
| 109 | +uint32_t clock_read_model() |
| 110 | +{ |
| 111 | + static uint32_t clock_value = 0; |
| 112 | + |
| 113 | + uint32_t increment; |
| 114 | + __CPROVER_assume(increment <= 100); |
| 115 | + |
| 116 | + clock_value += increment; |
| 117 | + |
| 118 | + return clock_value; |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +The model is stateful in that it keeps the current clock value between |
| 123 | +invocations in the variable `clock_value`. On each invocation, it increments |
| 124 | +the clock by a non-determinstic value in the range 0 to 100. We can tell |
| 125 | +goto-instrument to use the model `clock_read_model()` for reads from the |
| 126 | +variable `clock` as follows: |
| 127 | + |
| 128 | +``` |
| 129 | +goto-cc -o get_message_test.gb get_message_test.c |
| 130 | +goto-instrument --nondet-volatile-model clock:clock_read_model get_message_test.gb get_message_test-mod.gb |
| 131 | +cbmc --function get_message_test get_message_test-mod.gb |
| 132 | +``` |
| 133 | + |
| 134 | +Now the final invocation of cbmc reports verification success. |
| 135 | + |
0 commit comments