Skip to content

Commit 45fa2ca

Browse files
author
Enrico Steffinlongo
authored
Merge pull request #7913 from NlightNFotis/shadow_mem_docs
[DOCS] Add documentation on CBMC Shadow Memory intrinsics
2 parents a043228 + e68aa9d commit 45fa2ca

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

doc/cprover-manual/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
* [Floating Point](modeling/floating-point/)
2525
* [Generating Environments](goto-harness/)
2626
* [Memory-mapped I/O](modeling/mmio/)
27+
* [Shadow Memory](modeling/shadow-memory/)
2728

2829
8. Build Systems
2930

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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

Comments
 (0)