Skip to content

Commit 4161fd1

Browse files
author
Remi Delmas
committed
Add "make invalid pointer" utility function and havoc mode
Operates in two sub-modes, controlled by CLI switch `--dfcc-simple-invalid-pointer-model`
1 parent e48f079 commit 4161fd1

File tree

10 files changed

+118
-16
lines changed

10 files changed

+118
-16
lines changed

doc/man/cbmc.1

+4
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,10 @@ track C string lengths and zero\-termination
337337
\fB\-\-dfcc\-debug\-lib\fR
338338
enable debug assertions in the cprover contracts library
339339
.TP
340+
\fB\-\-dfcc\-simple\-invalid\-pointer\-model\fR
341+
use simplified invalid pointer model in the cprover contracts library
342+
(faster, unsound)
343+
.TP
340344
\fB\-\-reachability\-slice\fR
341345
remove instructions that cannot appear on a trace
342346
from entry point to a property

doc/man/goto-analyzer.1

+4
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,10 @@ track C string lengths and zero\-termination
588588
.TP
589589
\fB\-\-dfcc\-debug\-lib\fR
590590
enable debug assertions in the cprover contracts library
591+
.TP
592+
\fB\-\-dfcc\-simple\-invalid\-pointer\-model\fR
593+
use simplified invalid pointer model in the cprover contracts library
594+
(faster, unsound)
591595
.SS "Standard Checks"
592596
From version \fB6.0\fR onwards, \fBcbmc\fR, \fBgoto-analyzer\fR and some other tools
593597
apply some checks to the program by default (called the "standard checks"), with the

doc/man/goto-instrument.1

+4
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,10 @@ track C string lengths and zero\-termination
709709
\fB\-\-dfcc\-debug\-lib\fR
710710
enable debug assertions in the cprover contracts library
711711
.TP
712+
\fB\-\-dfcc\-simple\-invalid\-pointer\-model\fR
713+
use simplified invalid pointer model in the cprover contracts library
714+
(faster, unsound)
715+
.TP
712716
\fB\-\-model\-argc\-argv\fR \fIn\fR
713717
Create up to \fIn\fR non-deterministic C strings as entries to \fIargv\fR and
714718
set \fIargc\fR accordingly. In absence of such modelling, \fIargv\fR is left

src/ansi-c/cprover_library.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ static std::string get_cprover_library_text(
4848
if(config.ansi_c.dfcc_debug_lib)
4949
library_text << "#define " CPROVER_PREFIX "DFCC_DEBUG_LIB\n";
5050

51+
if(config.ansi_c.simple_invalid_pointer_model)
52+
library_text << "#define " CPROVER_PREFIX
53+
"DFCC_SIMPLE_INVALID_POINTER_MODEL\n";
54+
5155
// cprover_library.inc may not have been generated when running Doxygen, thus
5256
// make Doxygen skip this part
5357
/// \cond

src/ansi-c/library/cprover_contracts.c

+27
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,33 @@ void *__CPROVER_contracts_malloc(
11361136
__CPROVER_size_t,
11371137
__CPROVER_contracts_write_set_ptr_t);
11381138

1139+
/// \brief Makes the given pointer invalid.
1140+
///
1141+
/// Used to craft invalid pointers when pointer predicates return false
1142+
/// in "assume" mode.
1143+
/// We have two models for invalid pointers:
1144+
/// - default: pointer is uninitialized (empty value set, nondet bit pattern).
1145+
/// - simple: pointer is either null or pointing to a dead object of size zero.
1146+
/// The simple model is activated by a CLI switch in goto-instrument.
1147+
void __CPROVER_contracts_make_invalid_pointer(void **ptr)
1148+
{
1149+
#ifdef __CPROVER_DFCC_SIMPLE_INVALID_POINTER_MODEL
1150+
void *dummy = __CPROVER_allocate(0, 0);
1151+
__CPROVER_deallocated =
1152+
__VERIFIER_nondet___CPROVER_bool() ? dummy : __CPROVER_deallocated;
1153+
*ptr = __VERIFIER_nondet___CPROVER_bool() ? dummy : (void *)0;
1154+
#else
1155+
# pragma GCC diagnostic push
1156+
# pragma GCC diagnostic ignored "-Wuninitialized"
1157+
// We have to silence this warning to be able to generate and use an
1158+
// invalid pointer.
1159+
void *invalid;
1160+
*ptr = invalid;
1161+
# pragma GCC diagnostic pop
1162+
#endif
1163+
}
1164+
1165+
11391166
/// \brief Implementation of the `is_fresh` front-end predicate.
11401167
///
11411168
/// The behaviour depends on the boolean flags carried by \p set

src/goto-instrument/contracts/dynamic-frames/dfcc_instrument_loop.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ void dfcc_instrument_loopt::operator()(
121121
function_id,
122122
write_set_populate_instrs,
123123
loop.addr_of_write_set_var,
124+
dfcc_ptr_havoc_modet::NONDET,
124125
havoc_instrs,
125126
nof_targets);
126127
spec_functions.to_spec_assigns_instructions(

src/goto-instrument/contracts/dynamic-frames/dfcc_spec_functions.cpp

+41-9
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ void dfcc_spec_functionst::generate_havoc_function(
103103
havoc_function_id,
104104
original_program,
105105
write_set_symbol.symbol_expr(),
106+
dfcc_ptr_havoc_modet::INVALID,
106107
havoc_program,
107108
nof_targets);
108109

@@ -144,6 +145,7 @@ void dfcc_spec_functionst::generate_havoc_instructions(
144145
const irep_idt &function_id,
145146
const goto_programt &original_program,
146147
const exprt &write_set_to_havoc,
148+
dfcc_ptr_havoc_modet ptr_havoc_mode,
147149
goto_programt &havoc_program,
148150
std::size_t &nof_targets)
149151
{
@@ -191,8 +193,9 @@ void dfcc_spec_functionst::generate_havoc_instructions(
191193
// DECL __havoc_target;
192194
// CALL __havoc_target = havoc_hook(set, next_idx);
193195
// IF !__havoc_target GOTO label;
194-
// ASSIGN *__havoc_target = nondet(target_type);
195-
// label: DEAD __havoc_target;
196+
// .... add code for scalar or pointer targets ...
197+
// label:
198+
// DEAD __havoc_target;
196199
// ```
197200
// declare a local var to store targets havoced via nondet assignment
198201
auto &target_type = get_target_type(ins_it->call_arguments().at(0));
@@ -213,13 +216,42 @@ void dfcc_spec_functionst::generate_havoc_instructions(
213216
havoc_program.add(goto_programt::make_incomplete_goto(
214217
dfcc_utilst::make_null_check_expr(target_expr), location));
215218

216-
// create nondet assignment to the target
217-
side_effect_expr_nondett nondet(target_type, location);
218-
havoc_program.add(goto_programt::make_assignment(
219-
dereference_exprt{typecast_exprt::conditional_cast(
220-
target_expr, pointer_type(target_type))},
221-
nondet,
222-
location));
219+
if(
220+
ptr_havoc_mode == dfcc_ptr_havoc_modet::INVALID &&
221+
target_type.id() == ID_pointer)
222+
{
223+
// ```
224+
// DECL *__invalid_ptr;
225+
// ASSIGN *__havoc_target = __invalid_ptr;
226+
// DEAD __invalid_ptr;
227+
// ```
228+
const auto invalid_ptr_expr = dfcc_utilst::create_symbol(
229+
goto_model.symbol_table,
230+
target_type,
231+
function_id,
232+
"__invalid_ptr",
233+
location);
234+
235+
havoc_program.add(goto_programt::make_assignment(
236+
dereference_exprt{typecast_exprt::conditional_cast(
237+
target_expr, pointer_type(target_type))},
238+
invalid_ptr_expr,
239+
location));
240+
havoc_program.add(
241+
goto_programt::make_dead(invalid_ptr_expr, location));
242+
}
243+
else
244+
{
245+
// ```
246+
// ASSIGN *__havoc_target = nondet(target_type);
247+
// ```
248+
side_effect_expr_nondett nondet(target_type, location);
249+
havoc_program.add(goto_programt::make_assignment(
250+
dereference_exprt{typecast_exprt::conditional_cast(
251+
target_expr, pointer_type(target_type))},
252+
nondet,
253+
location));
254+
}
223255
auto label =
224256
havoc_program.add(goto_programt::make_dead(target_expr, location));
225257
goto_instruction->complete_goto(label);

src/goto-instrument/contracts/dynamic-frames/dfcc_spec_functions.h

+21-5
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,19 @@ class message_handlert;
3232
class symbolt;
3333
class conditional_target_group_exprt;
3434

35+
/// \brief Represents the different ways to havoc pointers.
36+
///
37+
/// Remark:
38+
/// - Function contracts use invalid
39+
/// - Loop contracts use nondet
40+
enum class dfcc_ptr_havoc_modet
41+
{
42+
/// havocs the pointer to an invalid pointer
43+
INVALID,
44+
/// havocs the pointer to an nondet pointer
45+
NONDET
46+
};
47+
3548
/// This class rewrites GOTO functions that use the built-ins:
3649
/// - `__CPROVER_assignable`,
3750
/// - `__CPROVER_object_whole`,
@@ -48,6 +61,9 @@ class dfcc_spec_functionst
4861
message_handlert &message_handler,
4962
dfcc_libraryt &library);
5063

64+
/// Generates the havoc function for a function contract.
65+
/// Pointer-typed targets are turned into invalid pointers by the havoc.
66+
///
5167
/// From a function:
5268
///
5369
/// ```
@@ -62,10 +78,9 @@ class dfcc_spec_functionst
6278
///
6379
/// Which havocs the targets specified by `function_id`, passed
6480
///
65-
/// \param function_id function to generate instructions from
66-
/// \param havoc_function_id write set variable to havoc
67-
/// \param nof_targets maximum number of targets to havoc
68-
///
81+
/// \param[in] function_id function to generate instructions from
82+
/// \param[in] havoc_function_id write set variable to havoc
83+
/// \param[out] nof_targets maximum number of targets to havoc
6984
void generate_havoc_function(
7085
const irep_idt &function_id,
7186
const irep_idt &havoc_function_id,
@@ -89,13 +104,14 @@ class dfcc_spec_functionst
89104
/// \param[in] function_id function id to use for prefixing fresh variables
90105
/// \param[in] original_program program from which to derive the havoc program
91106
/// \param[in] write_set_to_havoc write set symbol to havoc
107+
/// \param[in] ptr_havoc_mode havocing mode for pointers
92108
/// \param[out] havoc_program destination program for havoc instructions
93109
/// \param[out] nof_targets max number of havoc targets discovered
94-
///
95110
void generate_havoc_instructions(
96111
const irep_idt &function_id,
97112
const goto_programt &original_program,
98113
const exprt &write_set_to_havoc,
114+
dfcc_ptr_havoc_modet ptr_havoc_mode,
99115
goto_programt &havoc_program,
100116
std::size_t &nof_targets);
101117

src/util/config.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,11 @@ bool configt::set(const cmdlinet &cmdline)
11691169
else
11701170
ansi_c.dfcc_debug_lib = false;
11711171

1172+
if(cmdline.isset("dfcc-simple-invalid-pointer-model"))
1173+
ansi_c.simple_invalid_pointer_model = true;
1174+
else
1175+
ansi_c.simple_invalid_pointer_model = false;
1176+
11721177
if(cmdline.isset("no-library"))
11731178
ansi_c.lib=configt::ansi_ct::libt::LIB_NONE;
11741179

src/util/config.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ class symbol_table_baset;
7373
"(malloc-fail-assert)(malloc-fail-null)(malloc-may-fail)" \
7474
"(no-malloc-may-fail)" \
7575
"(string-abstraction)" \
76-
"(dfcc-debug-lib)"
76+
"(dfcc-debug-lib)" \
77+
"(dfcc-simple-invalid-pointer-model)"
7778

7879
#define HELP_CONFIG_LIBRARY \
7980
" {y--malloc-may-fail} \t allow malloc calls to return a null pointer\n" \
@@ -83,7 +84,9 @@ class symbol_table_baset;
8384
" {y--malloc-fail-null} \t set malloc failure mode to return null\n" \
8485
" {y--string-abstraction} \t track C string lengths and zero-termination\n" \
8586
" {y--dfcc-debug-lib} \t enable debug assertions in the cprover contracts " \
86-
"library\n"
87+
"library\n" \
88+
" {y--dfcc-simple-invalid-pointer-model} \t use simplified invalid pointer " \
89+
"model in the cprover contracts library (faster, unsound)\n"
8790

8891
#define OPT_CONFIG_JAVA "(classpath)(cp)(main-class)"
8992

@@ -288,6 +291,8 @@ class configt
288291
/// enable debug code in cprover_contracts library
289292
bool dfcc_debug_lib = false;
290293
294+
/// use simplified invalid pointer model in cprover_contracts library
295+
bool simple_invalid_pointer_model = false;
291296
292297
enum malloc_failure_modet
293298
{

0 commit comments

Comments
 (0)