Skip to content

Commit 9739c2e

Browse files
Add a helper function that behaves like malloc in GOTO
This adds a function that generates a malloc function that replicates the behaviour of malloc from CBMCs stdlib implementation. This is useful in situations in which we can't rely on malloc being present.
1 parent f3793bd commit 9739c2e

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed

src/ansi-c/c_nondet_symbol_factory.cpp

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ class symbol_factoryt
4848
allocate_objects(ID_C, loc, loc.get_function(), symbol_table)
4949
{}
5050

51+
/// Generate a function that behaves like malloc from our stdlib
52+
/// implementation
53+
/// \param malloc_symbol_name The name of the malloc function
54+
const symbolt &gen_malloc_function(const irep_idt &malloc_symbol_name);
55+
5156
void gen_nondet_init(
5257
code_blockt &assignments,
5358
const exprt &expr,
@@ -210,6 +215,144 @@ void symbol_factoryt::gen_nondet_array_init(
210215
}
211216
}
212217

218+
const symbolt &
219+
symbol_factoryt::gen_malloc_function(const irep_idt &malloc_symbol_name)
220+
{
221+
// the name passed in as parameter should not exist in the symbol
222+
// table already
223+
PRECONDITION(symbol_table.lookup(malloc_symbol_name) == nullptr);
224+
225+
auto source_location = source_locationt{};
226+
source_location.set_file(
227+
"<builtin-library-" + id2string(malloc_symbol_name) + ">");
228+
symbolt malloc_sym;
229+
malloc_sym.base_name = malloc_sym.name = malloc_sym.pretty_name =
230+
malloc_symbol_name;
231+
malloc_sym.mode = "C";
232+
233+
auto malloc_body = code_blockt{};
234+
malloc_body.add_source_location() = source_location;
235+
236+
// create a new local variable with this name and type, and return
237+
// a symbol_expr that represents this variable
238+
auto declare_local_variable = [&](
239+
const std::string &name, const typet &type) {
240+
auto const local_id = irep_idt{id2string(malloc_symbol_name) + "::" + name};
241+
auto local_sym = symbolt{};
242+
local_sym.type = type;
243+
local_sym.pretty_name = name;
244+
local_sym.name = id2string(local_id);
245+
local_sym.base_name = name;
246+
local_sym.is_lvalue = false;
247+
local_sym.is_static_lifetime = false;
248+
local_sym.is_type = false;
249+
local_sym.mode = "C";
250+
symbol_table.insert(local_sym);
251+
malloc_body.add(code_declt{local_sym.symbol_expr()});
252+
return local_sym.symbol_expr();
253+
};
254+
255+
// declare the parameter `size_t malloc_size` for malloc
256+
auto malloc_size_param_symbol = symbolt{};
257+
malloc_size_param_symbol.type = size_type();
258+
malloc_size_param_symbol.name =
259+
id2string(malloc_symbol_name) + "::malloc_size";
260+
malloc_size_param_symbol.pretty_name = "malloc_size";
261+
malloc_size_param_symbol.base_name = "malloc_size";
262+
malloc_size_param_symbol.is_static_lifetime = false;
263+
malloc_size_param_symbol.is_parameter = true;
264+
symbol_table.insert(malloc_size_param_symbol);
265+
auto malloc_size_param = code_typet::parametert{size_type()};
266+
malloc_size_param.set_base_name("malloc_size");
267+
malloc_size_param.set_identifier(malloc_size_param_symbol.name);
268+
269+
// the signature for our malloc is
270+
// void *__CPROVER_malloc(size_t malloc_size);
271+
malloc_sym.type = code_typet{code_typet::parameterst{malloc_size_param},
272+
pointer_type(void_type())};
273+
274+
auto const &local_size_variable = malloc_size_param_symbol.symbol_expr();
275+
276+
// the variable that holds the allocation result, i.e. a valid void*
277+
// that points to a memory region of malloc_size bytes
278+
// void *malloc_res = __CPROVER_allocate(malloc_size, 0);
279+
auto const malloc_res =
280+
declare_local_variable("malloc_res", pointer_type(void_type()));
281+
282+
// the actual allocation
283+
auto malloc_allocate = side_effect_exprt{ID_allocate};
284+
malloc_allocate.copy_to_operands(local_size_variable);
285+
malloc_allocate.copy_to_operands(false_exprt{});
286+
malloc_body.add(code_assignt{malloc_res, malloc_allocate});
287+
288+
// the following are all setting various CBMC book-keeping variables
289+
290+
// __CPROVER_deallocated=(malloc_res==__CPROVER_deallocated)?0:__CPROVER_deallocated;
291+
auto const &cprover_deallocated =
292+
symbol_table.lookup_ref(CPROVER_PREFIX "deallocated");
293+
malloc_body.add(code_assignt{
294+
cprover_deallocated.symbol_expr(),
295+
if_exprt{equal_exprt{malloc_res, cprover_deallocated.symbol_expr()},
296+
from_integer(0, cprover_deallocated.type),
297+
cprover_deallocated.symbol_expr()}});
298+
299+
// __CPROVER_bool record_malloc=__VERIFIER_nondet___CPROVER_bool();
300+
auto const record_malloc =
301+
declare_local_variable("record_malloc", bool_typet{});
302+
malloc_body.add(
303+
code_assignt{record_malloc, get_nondet_bool(bool_typet{}, loc)});
304+
305+
// __CPROVER_malloc_object=record_malloc?malloc_res:__CPROVER_malloc_object;
306+
auto const &malloc_object =
307+
symbol_table.lookup_ref(CPROVER_PREFIX "malloc_object");
308+
malloc_body.add(code_assignt{malloc_object.symbol_expr(),
309+
if_exprt{record_malloc,
310+
malloc_res,
311+
malloc_object.symbol_expr(),
312+
malloc_object.type}});
313+
314+
// __CPROVER_malloc_size=record_malloc?malloc_size:__CPROVER_malloc_size;
315+
auto const &malloc_size =
316+
symbol_table.lookup_ref(CPROVER_PREFIX "malloc_size");
317+
malloc_body.add(code_assignt{malloc_size.symbol_expr(),
318+
if_exprt{record_malloc,
319+
local_size_variable,
320+
malloc_size.symbol_expr(),
321+
malloc_size.type}});
322+
323+
// __CPROVER_malloc_is_new_array=record_malloc?0:__CPROVER_malloc_is_new_array;
324+
auto const &malloc_is_new_array =
325+
symbol_table.lookup_ref(CPROVER_PREFIX "malloc_is_new_array");
326+
malloc_body.add(code_assignt{
327+
malloc_is_new_array.symbol_expr(),
328+
if_exprt{record_malloc, false_exprt{}, malloc_is_new_array.symbol_expr()}});
329+
330+
// __CPROVER_bool record_may_leak=__VERIFIER_nondet___CPROVER_bool();
331+
auto const record_may_leak =
332+
declare_local_variable("record_may_leak", bool_typet{});
333+
malloc_body.add(code_declt{record_may_leak});
334+
malloc_body.add(
335+
code_assignt{record_may_leak, get_nondet_bool(bool_typet{}, loc)});
336+
337+
// __CPROVER_memory_leak=record_may_leak?malloc_res:__CPROVER_memory_leak;
338+
auto const &memory_leak =
339+
symbol_table.lookup_ref(CPROVER_PREFIX "memory_leak");
340+
malloc_body.add(code_assignt{
341+
memory_leak.symbol_expr(),
342+
if_exprt{record_may_leak, malloc_res, memory_leak.symbol_expr()}});
343+
344+
// return malloc_res;
345+
malloc_body.add(code_returnt{malloc_res});
346+
347+
malloc_sym.value = malloc_body;
348+
auto inserted_sym = symbol_table.insert(malloc_sym);
349+
350+
// since the function is only called when there's no symbol with
351+
// malloc_sym.name already in the symbol table, inserting it does succeed
352+
CHECK_RETURN(inserted_sym.second);
353+
return inserted_sym.first;
354+
}
355+
213356
/// Creates a symbol and generates code so that it can vary over all possible
214357
/// values for its type. For pointers this involves allocating symbols which it
215358
/// can point to.

0 commit comments

Comments
 (0)