Skip to content

Commit 18c0550

Browse files
committed
Some developer documentation for memory bounds checking
1 parent 778fb97 commit 18c0550

File tree

1 file changed

+85
-0
lines changed

1 file changed

+85
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
\ingroup module_hidden
2+
\page memory-bounds-checking Memory Bounds Checking
3+
4+
cbmc provides means to verify that pointers are within the bounds of (statically
5+
or dynamically) allocated memory blocks. When the option `--pointer-check` is
6+
used, cbmc checks the safety of each pointer dereference in the program.
7+
Similarly, the primitive `__CPROVER_r_ok(pointer, size)` returns true when it is
8+
safe to read from the memory segment starting at `pointer` and extending for
9+
`size` bytes. `__CPROVER_w_ok(pointer, size)` indicates if writing to the given
10+
segment is safe. At present, both primitives behave the same.
11+
12+
Each memory segment is referred to internally in cbmc via an object id. Pointers
13+
are represented as bitvectors (typically of length 32 or 64). The topmost `n`
14+
bits encode the id of the memory segment the pointer is pointing to, and the
15+
remaining bits encode the offset within the segment. The number of bits used to
16+
represent the object id can be specified via `--object-bits n`. Object offsets
17+
are signed. This allows to distinguish the case of when a pointer has been
18+
incremented too much from the case of when a pointer has been decremented too
19+
much. In the latter case, a negative value would be visible for the offset
20+
portion of a pointer in an error trace.
21+
22+
In the following, we describe how the memory bounds checks are implemented in
23+
cbmc for dynamically allocated memory. Dynamic memory is allocated in C via the
24+
`malloc()` library function. Its model in cbmc looks as follows (see
25+
`src/ansi-c/library/stdlib.c`, details not related to memory bounds checking are
26+
left off):
27+
28+
```C
29+
inline void *malloc(__CPROVER_size_t malloc_size)
30+
{
31+
void *malloc_res;
32+
malloc_res = __CPROVER_allocate(malloc_size, 0);
33+
34+
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
35+
__CPROVER_malloc_object = record_malloc ? malloc_res : __CPROVER_malloc_object;
36+
__CPROVER_malloc_size = record_malloc ? malloc_size : __CPROVER_malloc_size;
37+
38+
return malloc_res;
39+
}
40+
```
41+
42+
Both internal variables `__CPROVER_malloc_object` and `__CPROVER_malloc_size`
43+
are initialized to 0 in the `__CPROVER_initialize()` function of a goto program.
44+
The nondeterministic switch controls whether the address and size of the memory
45+
block allocated in this particular invocation of `malloc()` are recorded.
46+
47+
When the option `--pointer-check` is used, cbmc generates the following
48+
verification condition for each pointer dereference expression (e.g.,
49+
`*pointer`):
50+
51+
```C
52+
__CPROVER_POINTER_OBJECT(pointer) == __CPROVER_POINTER_OBJECT(__CPROVER_malloc_object) ==>
53+
__CPROVER_POINTER_OFFSET(pointer) >= 0 && __CPROVER_POINTER_OFFSET(pointer) < __CPROVER_malloc_size
54+
```
55+
56+
The primitives `__CPROVER_POINTER_OBJECT()` and `__CPROVER_POINTER_OFFSET()` extract
57+
the object id, and pointer offset, respectively. Similar conditions are
58+
generated for `assert(__CPROVER_r_ok(pointer, size))` and
59+
`assert(__CPROVER_w_ok(pointer, size))`.
60+
61+
To illustrate how the bounds checking works, consider the following example
62+
program which is unsafe:
63+
64+
65+
```C
66+
int main()
67+
{
68+
char *p1 = malloc(1);
69+
p1++;
70+
char *p2 = malloc(2);
71+
72+
*p1 = 1;
73+
}
74+
```
75+
76+
Here the verification condition generated for the pointer dereference should
77+
fail. In the approach outlined above it indeed can, as one can choose true for
78+
`__VERIFIER_nondet___CPROVER_bool()` in the first call
79+
to `malloc()`, and false for `__VERIFIER_nondet___CPROVER_bool()` in the second
80+
call to `malloc()`. Thus, the object address and size of the first call to
81+
`malloc()` are recorded in `__CPROVER_malloc_object` and `__CPROVER_malloc_size`
82+
respectively. Thus, the premise of the implication in the verification condition
83+
above is true, while the conclusion is false, hence the overall condition is
84+
false.
85+

0 commit comments

Comments
 (0)