Skip to content

Commit 8279497

Browse files
committed
Add malloc-may-fail option to cbmc
and hard-code appropriate check inside our `stdlib.c`. Plus some documentation and test.
1 parent cd2c65b commit 8279497

File tree

11 files changed

+83
-36
lines changed

11 files changed

+83
-36
lines changed

doc/cprover-manual/properties.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ with `__`.
326326
|------------------------|-------------------------------------------------|
327327
| `--malloc-fail-null` | in case malloc fails return NULL |
328328
| `--malloc-fail-assert` | in case malloc fails report as failed property |
329+
| `--malloc-may-fail` | malloc may non-deterministically fail |
329330

330331
Calling `malloc` may fail for a number of reasons and the function may return a
331332
NULL pointer. The users can choose if and how they want the `malloc`-related
@@ -335,3 +336,7 @@ additional properties inside `malloc` that are checked and if failing the
335336
verification is terminated (by `assume(false)`). One such property is that the
336337
allocated size is not too large, i.e. internally representable. When neither of
337338
those two options are used, CBMC will assume that `malloc` does not fail.
339+
340+
Malloc may also fail for external reasons which are not modelled by CProver. If
341+
you want to replicate this behaviour use the option `--malloc-may-fail` in
342+
conjunction with one of the above modes of failure.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include <stdlib.h>
2+
3+
int main()
4+
{
5+
char *p = malloc(100);
6+
assert(p); // should fail, given the malloc-may-fail option
7+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--malloc-may-fail --malloc-fail-null
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^\[main.assertion.\d+\] line \d+ assertion p: FAILURE$
7+
^VERIFICATION FAILED$
8+
--
9+
^warning: ignoring
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^\[main.assertion.\d+\] line \d+ assertion p: SUCCESS$
7+
^VERIFICATION SUCCESSFUL$
8+
--
9+
^warning: ignoring

src/ansi-c/ansi_c_internal_additions.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ void ansi_c_internal_additions(std::string &code)
155155
"void *" CPROVER_PREFIX "allocate("
156156
CPROVER_PREFIX "size_t size, " CPROVER_PREFIX "bool zero);\n"
157157
"const void *" CPROVER_PREFIX "alloca_object = 0;\n"
158+
158159
"int " CPROVER_PREFIX "malloc_failure_mode="+
159160
std::to_string(config.ansi_c.malloc_failure_mode)+";\n"
160161
"int " CPROVER_PREFIX "malloc_failure_mode_return_null="+
@@ -164,6 +165,8 @@ void ansi_c_internal_additions(std::string &code)
164165
CPROVER_PREFIX "size_t " CPROVER_PREFIX "max_malloc_size="+
165166
std::to_string(1 << (config.ansi_c.pointer_width -
166167
config.bv_encoding.object_bits - 1))+";\n"
168+
CPROVER_PREFIX "bool " CPROVER_PREFIX "malloc_may_fail = " +
169+
std::to_string(config.ansi_c.malloc_may_fail) + ";\n"
167170

168171
// this is ANSI-C
169172
"extern " CPROVER_PREFIX "thread_local const char __func__["

src/ansi-c/library/cprover.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ extern const void *__CPROVER_malloc_object;
1616
extern __CPROVER_size_t __CPROVER_malloc_size;
1717
extern _Bool __CPROVER_malloc_is_new_array;
1818
extern const void *__CPROVER_memory_leak;
19+
1920
extern int __CPROVER_malloc_failure_mode;
2021
extern __CPROVER_size_t __CPROVER_max_malloc_size;
22+
extern _Bool __CPROVER_malloc_may_fail;
2123

2224
// malloc failure modes
2325
extern int __CPROVER_malloc_failure_mode_return_null;

src/ansi-c/library/stdlib.c

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -112,50 +112,55 @@ __CPROVER_bool __VERIFIER_nondet___CPROVER_bool();
112112

113113
inline void *malloc(__CPROVER_size_t malloc_size)
114114
{
115-
// realistically, malloc may return NULL,
116-
// and __CPROVER_allocate doesn't, but no one cares
117-
__CPROVER_HIDE:;
115+
// realistically, malloc may return NULL,
116+
// but we only do so if `--malloc-may-fail` is set
117+
__CPROVER_HIDE:;
118118

119+
if(__CPROVER_malloc_failure_mode == __CPROVER_malloc_failure_mode_return_null)
120+
{
121+
__CPROVER_bool should_malloc_fail = __VERIFIER_nondet___CPROVER_bool();
119122
if(
120-
__CPROVER_malloc_failure_mode ==
121-
__CPROVER_malloc_failure_mode_return_null)
122-
{
123-
if(malloc_size > __CPROVER_max_malloc_size)
124-
{
125-
return (void *)0;
126-
}
127-
}
128-
else if(
129-
__CPROVER_malloc_failure_mode ==
130-
__CPROVER_malloc_failure_mode_assert_then_assume)
123+
malloc_size > __CPROVER_max_malloc_size ||
124+
(__CPROVER_malloc_may_fail && should_malloc_fail))
131125
{
132-
__CPROVER_assert(
133-
malloc_size <= __CPROVER_max_malloc_size,
134-
"max allocation size exceeded");
135-
__CPROVER_assume(malloc_size <= __CPROVER_max_malloc_size);
126+
return (void *)0;
136127
}
128+
}
129+
else if(
130+
__CPROVER_malloc_failure_mode ==
131+
__CPROVER_malloc_failure_mode_assert_then_assume)
132+
{
133+
__CPROVER_assert(
134+
malloc_size <= __CPROVER_max_malloc_size, "max allocation size exceeded");
135+
__CPROVER_assume(malloc_size <= __CPROVER_max_malloc_size);
136+
137+
__CPROVER_bool should_malloc_fail = __VERIFIER_nondet___CPROVER_bool();
138+
__CPROVER_assert(
139+
!__CPROVER_malloc_may_fail || !should_malloc_fail,
140+
"max allocation may fail");
141+
__CPROVER_assume(!__CPROVER_malloc_may_fail || !should_malloc_fail);
142+
}
137143

138-
void *malloc_res;
139-
malloc_res = __CPROVER_allocate(malloc_size, 0);
144+
void *malloc_res;
145+
malloc_res = __CPROVER_allocate(malloc_size, 0);
140146

141-
// make sure it's not recorded as deallocated
142-
__CPROVER_deallocated =
143-
(malloc_res == __CPROVER_deallocated) ? 0 : __CPROVER_deallocated;
147+
// make sure it's not recorded as deallocated
148+
__CPROVER_deallocated =
149+
(malloc_res == __CPROVER_deallocated) ? 0 : __CPROVER_deallocated;
144150

145-
// record the object size for non-determistic bounds checking
146-
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
147-
__CPROVER_malloc_object =
148-
record_malloc ? malloc_res : __CPROVER_malloc_object;
149-
__CPROVER_malloc_size = record_malloc ? malloc_size : __CPROVER_malloc_size;
150-
__CPROVER_malloc_is_new_array =
151-
record_malloc ? 0 : __CPROVER_malloc_is_new_array;
151+
// record the object size for non-determistic bounds checking
152+
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
153+
__CPROVER_malloc_object =
154+
record_malloc ? malloc_res : __CPROVER_malloc_object;
155+
__CPROVER_malloc_size = record_malloc ? malloc_size : __CPROVER_malloc_size;
156+
__CPROVER_malloc_is_new_array =
157+
record_malloc ? 0 : __CPROVER_malloc_is_new_array;
152158

153-
// detect memory leaks
154-
__CPROVER_bool record_may_leak = __VERIFIER_nondet___CPROVER_bool();
155-
__CPROVER_memory_leak =
156-
record_may_leak ? malloc_res : __CPROVER_memory_leak;
159+
// detect memory leaks
160+
__CPROVER_bool record_may_leak = __VERIFIER_nondet___CPROVER_bool();
161+
__CPROVER_memory_leak = record_may_leak ? malloc_res : __CPROVER_memory_leak;
157162

158-
return malloc_res;
163+
return malloc_res;
159164
}
160165

161166
/* FUNCTION: __builtin_alloca */
@@ -446,7 +451,8 @@ inline void *realloc(void *ptr, __CPROVER_size_t malloc_size)
446451
// this shouldn't move if the new size isn't bigger
447452
void *res;
448453
res=malloc(malloc_size);
449-
__CPROVER_array_copy(res, ptr);
454+
if(res != (void *)0)
455+
__CPROVER_array_copy(res, ptr);
450456
free(ptr);
451457

452458
return res;

src/cbmc/cbmc_parse_options.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,6 +1084,8 @@ void cbmc_parse_optionst::help()
10841084
"\n"
10851085
"Backend options:\n"
10861086
" --object-bits n number of bits used for object addresses\n"
1087+
// NOLINTNEXTLINE(whitespace/line_length)
1088+
" --malloc-may-fail allow malloc calls to return a null pointer\n"
10871089
" --dimacs generate CNF in DIMACS format\n"
10881090
" --beautify beautify the counterexample (greedy heuristic)\n" // NOLINT(*)
10891091
" --localize-faults localize faults (experimental)\n"

src/cbmc/cbmc_parse_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class optionst;
4848
"(document-subgoals)(outfile):(test-preprocessor)" \
4949
"D:I:(c89)(c99)(c11)(cpp98)(cpp03)(cpp11)" \
5050
"(object-bits):" \
51+
"(malloc-may-fail)" \
5152
OPT_GOTO_CHECK \
5253
"(no-assertions)(no-assumptions)" \
5354
"(malloc-fail-assert)(malloc-fail-null)" \

src/util/config.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,8 @@ bool configt::set(const cmdlinet &cmdline)
11031103
if(cmdline.isset("malloc-fail-assert"))
11041104
ansi_c.malloc_failure_mode = ansi_c.malloc_failure_mode_assert_then_assume;
11051105

1106+
ansi_c.malloc_may_fail = cmdline.isset("malloc-may-fail");
1107+
11061108
return false;
11071109
}
11081110

src/util/config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class configt
129129
libt lib;
130130

131131
bool string_abstraction;
132+
bool malloc_may_fail = false;
132133

133134
enum malloc_failure_modet
134135
{

0 commit comments

Comments
 (0)