Skip to content

Commit ef8bc33

Browse files
committed
generate assert(false) when calling undefined function
This changes the symbolic execution engine to emit an assert(false) when processing a call to a function without body, instead of merely emitting a warning. The key benefit is that undefined function bodies are a threat to soundness, especially when CBMC is run without a human operator (say in CI) who might spot the warning. A common scenario is a call to a function that was renamed, or whose signature has changed. This scenario now triggers a verification failure. Users who prefer the previous, or other alternative behaviors, can achieve this via program instrumentation, say using goto-instrument.
1 parent 13a452d commit ef8bc33

File tree

29 files changed

+77
-79
lines changed

29 files changed

+77
-79
lines changed

regression/cbmc-cpp/Overloading_Functions4/main.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
double pow(double _X, double _Y);
22
double pow(double _X, int _Y);
33
float pow(float _X, float _Y);
4-
float pow(float _X, int _Y);
4+
float pow(float _X, int _Y)
5+
{
6+
}
57
long double pow(long double _X, long double _Y);
68
long double pow(long double _X, int _Y);
79

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
KNOWNBUG
22
main.c
33
--pointer-check --bounds-check -D_FORTIFY_SOURCE=2 -D__OPTIMIZE__=2
44
^EXIT=0$
@@ -7,3 +7,5 @@ main.c
77
--
88
^\*\*\*\* WARNING: no body for function __fprintf_chk
99
^warning: ignoring
10+
--
11+
We are missing __builtin_va_arg_pack

regression/cbmc-library/printf-01/__printf_chk.desc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
KNOWNBUG
22
main.c
33
--pointer-check --bounds-check -D_FORTIFY_SOURCE=2 -D__OPTIMIZE__=2
44
^EXIT=10$
@@ -8,3 +8,5 @@ main.c
88
--
99
^\*\*\*\* WARNING: no body for function __printf_chk
1010
^warning: ignoring
11+
--
12+
We are missing __builtin_va_arg_pack
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
KNOWNBUG
22
main.c
33
--pointer-check --bounds-check -D_FORTIFY_SOURCE=2 -D__OPTIMIZE__=2
44
^EXIT=0$
@@ -7,3 +7,5 @@ main.c
77
--
88
^\*\*\*\* WARNING: no body for function __syslog_chk
99
^warning: ignoring
10+
--
11+
We are missing _syslog$DARWIN_EXTSN
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
CORE
1+
KNOWNBUG
22
main.c
33
--pointer-check --bounds-check
44
^EXIT=0$
55
^SIGNAL=0$
66
^VERIFICATION SUCCESSFUL$
77
--
88
^warning: ignoring
9+
--
10+
We are missing _syslog$DARWIN_EXTSN

regression/cbmc/Array_UF17/main.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ int istrchr(const char *s, int c);
3131
int istrrchr(const char *s, int c);
3232
int istrncmp(const char *s1, int start, const char *s2, size_t n);
3333
int istrstr(const char *haystack, const char *needle);
34-
char *r_strncpy(char *dest, const char *src, size_t n);
34+
char *r_strncpy(char *dest, const char *src, size_t n)
35+
{
36+
}
3537
char *r_strcpy(char *dest, const char *src);
3638
char *r_strcat(char *dest, const char *src);
3739
char *r_strncat(char *dest, const char *src, size_t n);

regression/cbmc/Function_Pointer5/main.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
// this is a pointer to a function that takes a function pointer as argument
22

3-
void signal1(int, void (*)(int));
4-
void signal2(int, void (*h)(int));
3+
void signal1(int, void (*)(int))
4+
{
5+
}
6+
7+
void signal2(int, void (*h)(int))
8+
{
9+
h(123);
10+
}
511

612
void handler(int x)
713
{

regression/cbmc/Malloc2/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
typedef unsigned int size_t;
1+
typedef __CPROVER_size_t size_t;
22
typedef int ssize_t;
33
typedef int atomic_t;
44
typedef unsigned gfp_t;

regression/cbmc/Pointer28/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE no-new-smt
1+
KNOWNBUG no-new-smt
22
main.c
33
--little-endian
44
^EXIT=0$

regression/cbmc/Promotion4/main.i

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
unsigned nondet_unsigned();
2+
13
int main(int argc, char *argv[])
24
{
35
__CPROVER_assert(sizeof(unsigned int) == 2, "[--16] size of int is 16 bit");
46
__CPROVER_assert(
57
sizeof(unsigned long) == 4, "[--LP32] size of long is 32 bit");
68

7-
unsigned int k = non_det_unsigned();
9+
unsigned int k = nondet_unsigned();
810
__CPROVER_assume(k < 1);
911
__CPROVER_assert(k != 0xffff, "false counter example 16Bit?");
1012

regression/cbmc/String_Abstraction11/anon-retval.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
void *malloc(unsigned);
1+
void *malloc(__CPROVER_size_t);
22

33
char *foo()
44
{

regression/cbmc/String_Abstraction19/structs.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
void *malloc(unsigned);
1+
void *malloc(__CPROVER_size_t);
22

33
typedef struct str_struct
44
{
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
CORE
22
main.c
33

4+
^\[f\.no-body\] line 9 no body for callee f: FAILURE$
5+
^\[main\.assertion\.1] line 10 assertion i==1: FAILURE$
6+
^VERIFICATION FAILED$
47
^EXIT=10$
58
^SIGNAL=0$
6-
^\*\*\*\* WARNING: no body for function f$
7-
^VERIFICATION FAILED$
89
--
910
^warning: ignoring
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
CORE
22
main.c
33

4+
^\[asd\.no-body\] line 7 no body for callee asd: FAILURE$
5+
^\[main\.assertion\.1\] line 8 assertion v1==v2: FAILURE$
6+
^VERIFICATION FAILED$
47
^EXIT=10$
58
^SIGNAL=0$
6-
^\*\*\*\* WARNING: no body for function asd$
7-
^VERIFICATION FAILED$
89
--
910
^warning: ignoring
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#include <assert.h>
22
#include <stdlib.h>
33

4+
_Bool nondet_bool();
5+
46
void main()
57
{
68
char *data;
7-
data = nondet() ? malloc(1) : malloc(2);
9+
data = nondet_bool() ? malloc(1) : malloc(2);
810
assert(__CPROVER_OBJECT_SIZE(data) <= 2);
911
}

regression/cbmc/issue_5952_soundness_bug_smt_encoding/test_short.desc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ short.c
44
^EXIT=0$
55
^SIGNAL=0$
66
^VERIFICATION SUCCESSFUL$
7-
\[main.assertion.\d\] line \d assertion __CPROVER_OBJECT_SIZE\(data\) <= 2: SUCCESS
7+
^\[main\.assertion.\d\] line \d assertion __CPROVER_OBJECT_SIZE\(data\) <= 2: SUCCESS$
88
--
9-
\[main.assertion.\d\] line \d assertion __CPROVER_OBJECT_SIZE\(data\) <= 2: FAILURE
9+
^\[main\.assertion.\d\] line \d assertion __CPROVER_OBJECT_SIZE\(data\) <= 2: FAILURE$
1010
--
1111
This is the reduced version of the issue described in
1212
https://github.com/diffblue/cbmc/issues/5952

regression/cbmc/memory_allocation2/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
KNOWNBUG
22
main.c
33

44
^EXIT=10$

regression/cbmc/pointer-overflow2/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
KNOWNBUG
22
main.c
33
--pointer-overflow-check
44
^EXIT=0$

regression/cbmc/union12/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE broken-smt-backend
1+
KNOWNBUG broken-smt-backend
22
main.c
33

44
^EXIT=10$

regression/contracts/assigns_replace_02/main.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
#include <assert.h>
22

3+
void bar(int *)
4+
{
5+
}
6+
37
void foo(int *x, int *y) __CPROVER_assigns(*x)
48
{
59
*x = 7;

regression/contracts/cprover-assignable-pass/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
KNOWNBUG
22
main.c
33

44
CALL __CPROVER_object_whole
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
CORE
1+
KNOWNBUG
22
main.c
33
--dump-c
44
^EXIT=0$
55
^SIGNAL=0$
66
va_list
77
--
88
^warning: ignoring
9+
--
10+
We are missing __CPROVER_va_start
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
CORE
22
main.c
33
--inline
4-
^EXIT=0$
4+
^EXIT=10$
55
^SIGNAL=0$
6-
^VERIFICATION SUCCESSFUL$
6+
^VERIFICATION FAILED$
77
func1\(\)
88
ret.*=.*func2\(\)
9-
no body for function.*func1
10-
no body for function.*func2
9+
^\[func1\.no-body\] file main.c line 3 no body for callee func1: FAILURE$
10+
^\[func2\.no-body\] file main.c line 3 no body for callee func2: FAILURE$
1111
--
1212
^warning: ignoring

regression/goto-instrument/inline_17/test.desc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
CORE
22
main.c
33
--function-inline func1
4-
^EXIT=0$
4+
^EXIT=10$
55
^SIGNAL=0$
6-
^VERIFICATION SUCCESSFUL$
6+
^\[func2\.no-body\] line 3 no body for callee func2: FAILURE$
7+
^VERIFICATION FAILED$
78
func1\(\)
89
func2\(\)
910
--

regression/goto-instrument/remove-function-body1/test.desc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
CORE
22
main.c
33
--remove-function-body foo --remove-function-body bar
4-
^EXIT=0$
4+
^EXIT=10$
55
^SIGNAL=0$
6-
^VERIFICATION SUCCESSFUL$
6+
^\[foo\.no-body\] line 19 no body for callee foo: FAILURE$
7+
^\[bar\.no-body\] line 20 no body for callee bar: FAILURE$
8+
^VERIFICATION FAILED$
79
--
810
^warning: ignoring
911
^bar

src/goto-checker/symex_bmc.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -209,18 +209,3 @@ bool symex_bmct::get_unwind_recursion(
209209

210210
return abort;
211211
}
212-
213-
void symex_bmct::no_body(const irep_idt &identifier)
214-
{
215-
if(body_warnings.insert(identifier).second)
216-
{
217-
log.warning() << "**** WARNING: no body for function " << identifier;
218-
219-
if(havoc_bodyless_functions)
220-
{
221-
log.warning()
222-
<< "; assigning non-deterministic values to any pointer arguments";
223-
}
224-
log.warning() << log.eom;
225-
}
226-
}

src/goto-checker/symex_bmc.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,6 @@ class symex_bmct : public goto_symext
110110
unsigned thread_nr,
111111
unsigned unwind) override;
112112

113-
void no_body(const irep_idt &identifier) override;
114-
115-
std::unordered_set<irep_idt> body_warnings;
116-
117113
symex_coveraget symex_coverage;
118114
};
119115

src/goto-symex/goto_symex.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -444,12 +444,6 @@ class goto_symext
444444

445445
virtual void loop_bound_exceeded(statet &state, const exprt &guard);
446446

447-
/// Log a warning that a function has no body
448-
/// \param identifier: The name of the function with no body
449-
virtual void no_body(const irep_idt &identifier)
450-
{
451-
}
452-
453447
/// Symbolically execute a FUNCTION_CALL instruction.
454448
/// Only functions that are symbols are supported, see
455449
/// \ref goto_symext::symex_function_call_symbol.

src/goto-symex/symex_function_call.cpp

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,17 @@ void goto_symext::symex_function_call_post_clean(
288288

289289
if(!goto_function.body_available())
290290
{
291-
no_body(identifier);
291+
// create an assertion
292+
if(symex_config.unwinding_assertions)
293+
{
294+
const auto &symbol = ns.lookup(identifier);
295+
const std::string property_id = id2string(identifier) + ".no-body";
296+
vcc(
297+
false_exprt(),
298+
property_id,
299+
"no body for callee " + id2string(symbol.display_name()),
300+
state);
301+
}
292302

293303
// record the return
294304
target.function_return(
@@ -301,26 +311,6 @@ void goto_symext::symex_function_call_post_clean(
301311
symex_assign(state, cleaned_lhs, rhs);
302312
}
303313

304-
if(symex_config.havoc_undefined_functions)
305-
{
306-
// assign non det to function arguments if pointers
307-
// are not const
308-
for(const auto &arg : cleaned_arguments)
309-
{
310-
if(
311-
arg.type().id() == ID_pointer &&
312-
!to_pointer_type(arg.type()).base_type().get_bool(ID_C_constant) &&
313-
to_pointer_type(arg.type()).base_type().id() != ID_code)
314-
{
315-
exprt object =
316-
dereference_exprt(arg, to_pointer_type(arg.type()).base_type());
317-
exprt cleaned_object = clean_expr(object, state, true);
318-
const guardt guard(true_exprt(), state.guard_manager);
319-
havoc_rec(state, guard, cleaned_object);
320-
}
321-
}
322-
}
323-
324314
symex_transition(state);
325315
return;
326316
}

0 commit comments

Comments
 (0)