Skip to content

Commit ebfaf5a

Browse files
Add option to generate function body to goto-instrument
1 parent 69fb74a commit ebfaf5a

File tree

31 files changed

+1069
-2
lines changed

31 files changed

+1069
-2
lines changed

CHANGELOG

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
5.9
2+
===
3+
* GOTO-INSTRUMENT: --generate-function-body can be used to
4+
generate bodies for functions that don't have a body
5+
in the goto code. This supercedes the functionality
6+
of --undefined-function-is-assume-false
7+
18
5.8
29
===
310

doc/cbmc-user-manual.md

+158
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,164 @@ Flag | Check
20142014
`--uninitialized-check` | add checks for uninitialized locals (experimental)
20152015
`--error-label label` | check that given label is unreachable
20162016

2017+
#### Generating function bodies
2018+
2019+
Sometimes implementations for called functions are not available in the goto
2020+
program, or it is desirable to replace bodies of functions with certain
2021+
predetermined stubs (for example to confirm that these functions are never
2022+
called, or to indicate that these functions will never return). For this purpose
2023+
goto-instrument provides the `--generate-function-body` option, that takes a
2024+
regular expression (in [ECMAScript syntax]
2025+
(http://en.cppreference.com/w/cpp/regex/ecmascript)) that describes the names of
2026+
the functions to generate. Note that this will only generate bodies for
2027+
functions that do not already have one; If one wishes to replace the body of a
2028+
function with an existing definition, the `--remove-function-body` option can be
2029+
used to remove the body of the function prior to generating a new one.
2030+
2031+
The shape of the stub itself can be chosen with the
2032+
`--generate-function-body-options` parameter, which can take the following
2033+
values:
2034+
2035+
Option | Result
2036+
-----------------------------|-------------------------------------------------------------
2037+
`nondet-return` | Do nothing and return a nondet result (this is the default)
2038+
`assert-false` | Make the body contain an assert(false)
2039+
`assume-false` | Make the body contain an assume(false)
2040+
`assert-false-assume-false` | Combines assert-false and assume-false
2041+
`havoc` | Set the contents of parameters and globals to nondet
2042+
2043+
The various combinations of assert-false and assume-false can be used to
2044+
indicate that functions shouldn't be called, that they will never return or
2045+
both.
2046+
2047+
Example: We have a program like this:
2048+
2049+
// error_example.c
2050+
#include <stdlib.h>
2051+
2052+
void api_error(void);
2053+
void internal_error(void);
2054+
2055+
int main(void)
2056+
{
2057+
int arr[10] = {1,2,3,4,5, 6, 7, 8, 9, 10};
2058+
int sum = 0;
2059+
for(int i = 1; i < 10; ++i)
2060+
{
2061+
sum += arr[i];
2062+
}
2063+
if(sum != 55)
2064+
{
2065+
// we made a mistake when calculating the sum
2066+
internal_error();
2067+
}
2068+
if(rand() < 0)
2069+
{
2070+
// we think this cannot happen
2071+
api_error();
2072+
}
2073+
return 0;
2074+
}
2075+
2076+
Now, we can compile the program and detect that the error functions are indeed
2077+
called by invoking these commands
2078+
2079+
goto-cc error_example.c -o error_example.goto
2080+
# Replace all functions ending with _error
2081+
# (Excluding those starting with __)
2082+
# With ones that have an assert(false) body
2083+
goto-instrument error_example.goto error_example_replaced.goto \
2084+
--generate-function-body '(?!__).*_error' \
2085+
--generate-function-body-options assert-false
2086+
cbmc error_example_replaced.goto
2087+
2088+
Which gets us the output
2089+
2090+
> ** Results:
2091+
> [internal_error.assertion.1] assertion false: FAILURE
2092+
> [api_error.assertion.1] assertion false: FAILURE
2093+
>
2094+
>
2095+
> ** 2 of 2 failed (2 iterations)
2096+
> VERIFICATION FAILED
2097+
2098+
As opposed to the verification success we would have gotten without the
2099+
generation.
2100+
2101+
2102+
The havoc option takes further parameters `globals` and `params` with this
2103+
syntax: `havoc[,globals:<regex>][,params:<regex>]` (where the square brackets
2104+
indicate an optional part). The regular expressions have the same format as the
2105+
those for the `--generate-function-body` option and indicate which globals and
2106+
function parameters should be set to nondet. All regular expressions require
2107+
exact matches (i.e. the regular expression `a|b` will match 'a' and 'b' but not
2108+
'adrian' or 'bertha').
2109+
2110+
Example: With a C program like this
2111+
2112+
struct Complex {
2113+
double real;
2114+
double imag;
2115+
};
2116+
2117+
struct Complex AGlobalComplex;
2118+
int do_something_with_complex(struct Complex *complex);
2119+
2120+
And the command line
2121+
2122+
goto-instrument in.goto out.goto
2123+
--generate-function-body do_something_with_complex
2124+
--generate-function-body-options
2125+
'havoc,params:.*,globals:AGlobalComplex'
2126+
2127+
The goto code equivalent of the following will be generated:
2128+
2129+
int do_something_with_complex(struct Complex *complex)
2130+
{
2131+
if(complex)
2132+
{
2133+
complex->real = nondet_double();
2134+
complex->imag = nondet_double();
2135+
}
2136+
AGlobalComplex.real = nondet_double();
2137+
AGlobalComplex.imag = nondet_double();
2138+
return nondet_int();
2139+
}
2140+
2141+
A note on limitations: Because only static information is used for code
2142+
generation, arrays of unknown size and pointers will not be affected by this;
2143+
Which means that for code like this:
2144+
2145+
struct Node {
2146+
int val;
2147+
struct Node *next;
2148+
};
2149+
2150+
void do_something_with_node(struct Node *node);
2151+
2152+
Code like this will be generated:
2153+
2154+
void do_something_with_node(struct Node *node)
2155+
{
2156+
if(node)
2157+
{
2158+
node->val = nondet_int();
2159+
node->next = nondet_0();
2160+
}
2161+
}
2162+
2163+
Note that no attempt to follow the `next` pointer is made. If an array of
2164+
unknown (or 0) size is encountered, a diagnostic is emitted and the array is not
2165+
further examined.
2166+
2167+
Some care must be taken when choosing the regular expressions for globals and
2168+
functions. Names starting with `__` are reserved for internal purposes; For
2169+
example, replacing functions or setting global variables with the `__CPROVER`
2170+
prefix might make analysis impossible. To avoid doing this by accident, negative
2171+
lookahead can be used. For example, `(?!__).*` matches all names not starting
2172+
with `__`.
2173+
2174+
20172175
\subsection man_instrumentation-api The CPROVER API Reference
20182176

20192177
The following sections summarize the functions available to programs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <assert.h>
2+
3+
void crashes_program(void);
4+
5+
int main(void)
6+
{
7+
int flag;
8+
if(flag)
9+
{
10+
crashes_program();
11+
assert(0);
12+
}
13+
return 0;
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--generate-function-body crashes_program --generate-function-body-options assert-false-assume-false
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
^\[main.assertion.1\] assertion 0: SUCCESS$
8+
^\[crashes_program.assertion.1\] assertion false: FAILURE$
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
void do_not_call_this(void);
2+
3+
int main(void)
4+
{
5+
do_not_call_this();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--generate-function-body do_not_call_this --generate-function-body-options assert-false
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[do_not_call_this.assertion.1\] assertion false: FAILURE$
7+
--
8+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <assert.h>
2+
3+
void will_not_return(void);
4+
5+
int main(void)
6+
{
7+
will_not_return();
8+
assert(0);
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--generate-function-body will_not_return --generate-function-body-options assume-false
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
struct ComplexStruct
5+
{
6+
struct
7+
{
8+
int some_variable;
9+
const int some_constant;
10+
} struct_contents;
11+
union {
12+
int some_integer;
13+
double some_double;
14+
} union_contents;
15+
struct ComplexStruct *pointer_contents;
16+
};
17+
18+
void havoc_complex_struct(struct ComplexStruct *p);
19+
20+
int main(void)
21+
{
22+
struct ComplexStruct child_struct = {{11, 21}, {.some_integer = 31}, NULL};
23+
struct ComplexStruct main_struct = {
24+
{10, 20}, {.some_double = 13.0}, &child_struct};
25+
assert(main_struct.pointer_contents->struct_contents.some_variable == 11);
26+
assert(main_struct.struct_contents.some_variable == 10);
27+
assert(main_struct.struct_contents.some_constant == 20);
28+
assert(
29+
main_struct.union_contents.some_double < 14.0 &&
30+
main_struct.union_contents.some_double > 12.0);
31+
32+
havoc_complex_struct(&main_struct);
33+
34+
// everything (except constants) in the main struct was havocced
35+
assert(main_struct.pointer_contents->struct_contents.some_variable == 11);
36+
assert(main_struct.struct_contents.some_variable == 10);
37+
assert(main_struct.struct_contents.some_constant == 20);
38+
assert(
39+
main_struct.union_contents.some_double < 14.0 &&
40+
main_struct.union_contents.some_double > 12.0);
41+
// the child struct was NOT havocced because we can't follow pointers
42+
assert(child_struct.struct_contents.some_variable == 11);
43+
assert(child_struct.union_contents.some_integer == 31);
44+
assert(!child_struct.pointer_contents);
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE
2+
main.c
3+
--generate-function-body 'havoc_complex_struct' --generate-function-body-options 'havoc,params:.*'
4+
^SIGNAL=0$
5+
^EXIT=10$
6+
\[main.assertion.1\] assertion main_struct.pointer_contents->struct_contents.some_variable == 11: SUCCESS
7+
\[main.assertion.2\] assertion main_struct.struct_contents.some_variable == 10: SUCCESS
8+
\[main.assertion.3\] assertion main_struct.struct_contents.some_constant == 20: SUCCESS
9+
\[main.assertion.4\] assertion main_struct.union_contents.some_double < 14.0 && main_struct.union_contents.some_double > 12.0: SUCCESS
10+
\[main.assertion.5\] assertion main_struct.pointer_contents->struct_contents.some_variable == 11: FAILURE
11+
\[main.assertion.6\] assertion main_struct.struct_contents.some_variable == 10: FAILURE
12+
\[main.assertion.7\] assertion main_struct.struct_contents.some_constant == 20: SUCCESS
13+
\[main.assertion.8\] assertion main_struct.union_contents.some_double < 14.0 && main_struct.union_contents.some_double > 12.0: FAILURE
14+
\[main.assertion.9\] assertion child_struct.struct_contents.some_variable == 11: SUCCESS
15+
\[main.assertion.10\] assertion child_struct.union_contents.some_integer == 31: SUCCESS
16+
\[main.assertion.11\] assertion !child_struct.pointer_contents: SUCCESS
17+
^VERIFICATION FAILED$
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <assert.h>
2+
3+
void change_pointer_target_of_const_pointer(
4+
int *const constant_pointer_to_nonconst);
5+
6+
int main(void)
7+
{
8+
int x = 10;
9+
change_pointer_target_of_const_pointer(&x);
10+
assert(x == 10);
11+
return 0;
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
main.c
3+
--generate-function-body change_pointer_target_of_const_pointer --generate-function-body-options havoc,params:.*
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.assertion.1\] assertion x == 10: FAILURE$
7+
--
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include <assert.h>
2+
3+
int global = 10;
4+
const int constant_global = 10;
5+
6+
void touches_globals(void);
7+
8+
int main(void)
9+
{
10+
touches_globals();
11+
assert(global == 10);
12+
assert(constant_global == 10);
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--generate-function-body touches_globals --generate-function-body-options 'havoc,globals:(?!__).*'
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
^\[main.assertion.1\] assertion global == 10: FAILURE$
8+
^\[main.assertion.2\] assertion constant_global == 10: SUCCESS$
9+
--
10+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <assert.h>
2+
3+
void touches_parameter(int *param, const int *const_param);
4+
5+
int main(void)
6+
{
7+
int parameter = 10;
8+
int constant_parameter = 10;
9+
touches_parameter(&parameter, &constant_parameter);
10+
assert(parameter == 10);
11+
assert(constant_parameter == 10);
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--generate-function-body touches_parameter --generate-function-body-options 'havoc,params:.*'
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
^\[main.assertion.1\] assertion parameter == 10: FAILURE$
8+
^\[main.assertion.2\] assertion constant_parameter == 10: SUCCESS$
9+
--
10+
^warning: ignoring
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <assert.h>
2+
3+
void change_target_of_pointer_to_pointer_to_const(
4+
const int **pointer_to_pointer_to_const);
5+
6+
int main(void)
7+
{
8+
int x = 10;
9+
int *px = &x;
10+
assert(*px == 10);
11+
change_target_of_pointer_to_pointer_to_const(&px);
12+
assert(x == 10);
13+
assert(*px == 10);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--generate-function-body change_target_of_pointer_to_pointer_to_const --generate-function-body-options havoc,params:.*
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.assertion.1\] assertion \*px == 10: SUCCESS$
7+
^\[main.assertion.2\] assertion x == 10: SUCCESS$
8+
^\[main.assertion.3\] assertion \*px == 10: FAILURE$
9+
--

0 commit comments

Comments
 (0)