|
| 1 | +# Shadow Memory |
| 2 | + |
| 3 | +The Symbolic Shadow Memory module described below is an implementation of |
| 4 | +what is outlined in the paper [CBMC-SSM: Bounded Model Checking of C Programs |
| 5 | +with Symbolic Shadow Memory](https://dl.acm.org/doi/abs/10.1145/3551349.3559523). |
| 6 | + |
| 7 | +## Introduction |
| 8 | + |
| 9 | +CBMC implements *Symbolic Shadow Memory*. Symbolic Shadow Memory (from now on, |
| 10 | +SSM) allows one to create a parallel memory structure that is maintained by the |
| 11 | +analysis program and that shadows the standard memory of the program. In the |
| 12 | +Shadow Memory structure, a user can create fields, for which he can get |
| 13 | +(retrieve) and set values. |
| 14 | + |
| 15 | +By doing so, a user can organise their own tracking of metadata related to the |
| 16 | +code, in a way that is then considered by the backend of CBMC during analysis. |
| 17 | +This can be used to implement novel analyses that the CBMC framework itself does |
| 18 | +not support. For example, the paper above presents as an example a taint |
| 19 | +analysis implemented with the use of the SSM component described here. |
| 20 | + |
| 21 | +## Usage |
| 22 | + |
| 23 | +A user can interact with the Symbolic Shadow Memory component through four CBMC |
| 24 | +primitives. These allow the declaration of shadow memory fields, and get/set |
| 25 | +their corresponding values: |
| 26 | + |
| 27 | +* `__CPROVER_field_decl_local` |
| 28 | +* `__CPROVER_field_decl_global` |
| 29 | +* `__CPROVER_get_field` |
| 30 | +* `__CPROVER_set_field` |
| 31 | + |
| 32 | +More precisely, their signatures (in pseudo-C, because of some constraints that |
| 33 | +we cannot express using the type system), along with some small examples of their |
| 34 | +usage, are described below: |
| 35 | + |
| 36 | +### `void __CPROVER_field_decl_local(type1 field_name, SSM_value_type init_value)` |
| 37 | + |
| 38 | +Type constraints: |
| 39 | + |
| 40 | +* `type1`: string literal, such as `"field"`, |
| 41 | +* `SSM_value_type`: any value up to 8 bits in size (signed or unsigned). |
| 42 | + |
| 43 | +Declares a local shadow memory field called `field_name`, and initialises it |
| 44 | +with the value `init_value`. The field is going to be associated with |
| 45 | +function local-scope objects (i.e. a variable on the stack). |
| 46 | + |
| 47 | +Note that each function scope will have a separate *local* shadow memory, so the |
| 48 | +value stored in the *local* shadow memory is not propagated to subcalls (even |
| 49 | +when the call is recursive). For this reason, to be able to access shadow memory |
| 50 | +argument values from a called function or the value of the return from the |
| 51 | +callee it is necessary to use the global shadow memory and passing arguments and |
| 52 | +return values as pointers or to use global variables. |
| 53 | + |
| 54 | +```c |
| 55 | +void func() { |
| 56 | + // Sample local object (local variable) |
| 57 | + char x = 0; |
| 58 | + |
| 59 | + // Shadow memory field associated with local objects. |
| 60 | + __CPROVER_field_decl_local("shadow", (_Bool)1); |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +### `void __CPROVER_field_decl_global(type1 field_name, SSM_value_type init_value)` |
| 65 | + |
| 66 | +Type constraints: |
| 67 | + |
| 68 | +* `type1`: string literal, such as `"field"`, |
| 69 | +* `SSM_value_type`: any value up to 8 bits in size (signed or unsigned). |
| 70 | + |
| 71 | +As for `__CPROVER_field_decl_local`, but the field declared is associated with |
| 72 | +objects whose lifetime exceeds the current function scope (i.e. global variables |
| 73 | +or heap allocated objects). |
| 74 | + |
| 75 | +```c |
| 76 | +// Sample global object |
| 77 | +int a = 10; |
| 78 | + |
| 79 | +void func() { |
| 80 | + // Shadow memory field associated with global objects. |
| 81 | + __CPROVER_field_decl_global("shadow", (_Bool)0); |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +### `SSM_VALUE_TYPE __CPROVER_get_field(type1 *p, type2 field_name)` |
| 86 | + |
| 87 | +Type constraints: |
| 88 | + |
| 89 | +* `SSM_VALUE_TYPE`: the type of the returned value is the same of the SSM field, |
| 90 | + i.e. the type that was used to intialise the SSM field during declaration, |
| 91 | +* `type1 *`: a non-`void` pointer to an object of type `type1`, whose address we |
| 92 | + are going to use for indexing the shadow memory component for the field |
| 93 | + declared, |
| 94 | +* `type2`: a string literal-typed value, denoting the name of the field whose |
| 95 | + value we want to retrieve, such as `"field"`. |
| 96 | + |
| 97 | +Retrieves the latest value associated with a SSM field to the given pointer. |
| 98 | +This would be either the value the field was declared to be initialised with, |
| 99 | +or, if there had been subsequent changes to it through a `__CPROVER_set_field` |
| 100 | +(see `__CPROVER_set_field` section below), the value that it was last `set` |
| 101 | +with. |
| 102 | + |
| 103 | +```c |
| 104 | +// Sample global object |
| 105 | +int a = 10; |
| 106 | + |
| 107 | +void func() { |
| 108 | + // Shadow memory field (called "field") associated with global objects. |
| 109 | + __CPROVER_field_decl_global("shadow", (_Bool)0); |
| 110 | + |
| 111 | + _Bool shadow_x = __CPROVER_get_field(&a, "shadow"); |
| 112 | + __CPROVER_assert(shadow_x == 0, "expected success: field initialised with 0"); |
| 113 | + __CPROVER_assert(shadow_x == 1, "expected fail: field initialised with 0"); |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +Note that getting the value of a local variable from a global SSM field or the |
| 118 | +opposite will return the default value for that SSM field (and it **will not** |
| 119 | +fail). |
| 120 | + |
| 121 | +### `void __CPROVER_set_field(type1 *p, type2 field_name, type3 set_value)` |
| 122 | + |
| 123 | +Type constraints: |
| 124 | + |
| 125 | +* `type1 *`: a non-`void` pointer to an object of type `type1`, whose address we |
| 126 | + are going to use for indexing the shadow memory component for the field |
| 127 | + declared, |
| 128 | +* `type2`: a string literal-typed value, denoting the name of the field whose |
| 129 | + value we want to retrieve, such as `"field"`, |
| 130 | +* `type3`: type of the value to be set. This can be any integer type signed or |
| 131 | + unsigned (including `_Bool`). Notice that if this type differs from the type |
| 132 | + the SSM field was declared with (`SSM_VALUE_TYPE` above) it will be |
| 133 | + implicitly casted to it. |
| 134 | + |
| 135 | +Sets the value associated with a SSM field to the given pointer `p` with the |
| 136 | +given value `set_value`. If the `set_value` type is not the `SSM_VALUE_TYPE`, it |
| 137 | +will be implicitly casted to it. |
| 138 | + |
| 139 | +```c |
| 140 | +// Sample global object, used for addressing within the SSM component. |
| 141 | +int a = 10; |
| 142 | + |
| 143 | +void func() { |
| 144 | + // Shadow memory field (called "field") associated with global objects. |
| 145 | + // Originally assigned a value of `0`, of type `_Bool`. |
| 146 | + __CPROVER_field_decl_global("shadow", (_Bool)0); |
| 147 | + |
| 148 | + // Retrieve the value of the field named "shadow" associated with the address |
| 149 | + // of the object `a`. |
| 150 | + _Bool shadow_x = __CPROVER_get_field(&a, "shadow"); |
| 151 | + __CPROVER_assert(shadow_x == 0, "expected success: field defaulted to a value of 0"); |
| 152 | + |
| 153 | + // Set field "shadow" for the memory location denoted by the address of `a` |
| 154 | + // to a value of `1`. |
| 155 | + __CPROVER_set_field(&a, "shadow", 1); |
| 156 | + // Retrieve the value of the field named "shadow" associated with the address |
| 157 | + // of the object `a`. |
| 158 | + shadow_x = __CPROVER_get_field(&a, "shadow"); |
| 159 | + __CPROVER_assert(shadow_x == 1, "expected success: field set to a value of 1"); |
| 160 | +} |
| 161 | +``` |
| 162 | + |
| 163 | +Note that setting the value of a local variable from a global SSM field or the |
| 164 | +opposite will produce no effect (and it **will not** fail). |
| 165 | + |
| 166 | +### Working with Compound Type Objects |
| 167 | + |
| 168 | +When using SSM on compound type pointers (e.g. `struct` and `union`) the value |
| 169 | +used for the `__CPROVER_set_field` will be replicated in each of the fields of |
| 170 | +the type, and aggregated again when retrieving them with `__CPROVER_get_field`. |
| 171 | +The aggregation function is `or` for an SSM field of `_Bool` type, and `max` for |
| 172 | +other types. |
| 173 | + |
| 174 | +This is helpful, for example, in the case of taint analysis (as presented in the |
| 175 | +paper and shown below). In this case, when retrieving the taint value of a |
| 176 | +struct containing a tainted field the result value will indicate taint (without |
| 177 | +the need for changing non-tainted field values), and when setting the taint |
| 178 | +value of a struct then all its fields will be set to the given value. |
| 179 | + |
| 180 | +```c |
| 181 | +struct S { |
| 182 | + unsigned long f1; |
| 183 | + char f2; |
| 184 | +}; |
| 185 | + |
| 186 | +void func() { |
| 187 | + // Shadow memory field (called "field") associated with local objects. |
| 188 | + // Originally assigned a value of `0`, of type `_Bool`. |
| 189 | + __CPROVER_field_decl_local("shadow", (_Bool)0); |
| 190 | + |
| 191 | + // Struct typed variable |
| 192 | + struct S s; |
| 193 | + |
| 194 | + // Retrieve the value of the field named "shadow" associated with the address |
| 195 | + // of the object `s`. Here we expect a `0` as default. |
| 196 | + __CPROVER_assert(__CPROVER_get_field(&s, "shadow") == 0, |
| 197 | + "expected success: field defaulted to a value of 0"); |
| 198 | + // Retrieve the value of the field named "shadow" associated with the address |
| 199 | + // of the field `f1` of the object `s`. Here we expect a `0` as default. |
| 200 | + __CPROVER_assert(__CPROVER_get_field(&s.f1, "shadow") == 0, |
| 201 | + "expected success: field defaulted to a value of 0"); |
| 202 | + // Retrieve the value of the field named "shadow" associated with the address |
| 203 | + // of the field `f1` of the object `s`. Here we expect a `0` as default. |
| 204 | + __CPROVER_assert(__CPROVER_get_field(&s.f2, "shadow") == 0, |
| 205 | + "expected success: field defaulted to a value of 0"); |
| 206 | + |
| 207 | + |
| 208 | + // Set the shadow memory of the field `f1` (ONLY) of the object `s`. |
| 209 | + __CPROVER_set_field(&s.f1, "shadow", 1); |
| 210 | + |
| 211 | + // Retrieve the value of the field named "shadow" associated with the address |
| 212 | + // of the object `s`. Here we expect a `1` as the value of field `f1` (after |
| 213 | + // aggregating all its field values using `max`). |
| 214 | + __CPROVER_assert(__CPROVER_get_field(&s, "shadow") == 1, |
| 215 | + "expected success: field previously set to a value of 1"); |
| 216 | + // Retrieve the value of the field named "shadow" associated with the address |
| 217 | + // of the field `f1` of the object `s`. Here we expect a `1` as set above. |
| 218 | + __CPROVER_assert(__CPROVER_get_field(&s.f1, "shadow") == 1, |
| 219 | + "expected success: field previously set to a value of 1"); |
| 220 | + // Retrieve the value of the field named "shadow" associated with the address |
| 221 | + // of the field `f2` of the object `s`. Here we expect a `0` as default. |
| 222 | + __CPROVER_assert(__CPROVER_get_field(&s.f2, "shadow") == 0, |
| 223 | + "expected success: field defaulted to a value of 0"); |
| 224 | + |
| 225 | + |
| 226 | + // Set the shadow memory of the object `s`. This in turns sets also the |
| 227 | + // values of each (shadow) field of `s`. |
| 228 | + __CPROVER_set_field(&s, "shadow", 0); |
| 229 | + |
| 230 | + // Retrieve the value of the field named "shadow" associated with the address |
| 231 | + // of the object `s`. Here we expect a `0` as set above. |
| 232 | + __CPROVER_assert(__CPROVER_get_field(&s, "shadow") == 0, |
| 233 | + "expected success: field previously set to a value of 0"); |
| 234 | + // Retrieve the value of the field named "shadow" associated with the address |
| 235 | + // of the field `f1` of the object `s`. Here we expect a `0` as the set |
| 236 | + // above was replicated on all the fields of `s`. |
| 237 | + __CPROVER_assert(__CPROVER_get_field(&s.f1, "shadow") == 0, |
| 238 | + "expected success: field previously set to a value of 0"); |
| 239 | + // Retrieve the value of the field named "shadow" associated with the address |
| 240 | + // of the field `f2` of the object `s`. Here we expect a `0` as the set |
| 241 | + // above was replicated on all the fields of `s`. |
| 242 | + __CPROVER_assert(__CPROVER_get_field(&s.f2, "shadow") == 0, |
| 243 | + "expected success: field previously set to a value of 0"); |
| 244 | +} |
| 245 | +``` |
| 246 | + |
| 247 | + |
0 commit comments