Skip to content

Commit 3277cf3

Browse files
authored
Merge pull request #7375 from tautschnig/bugfixes/alloca
Memory allocations produced via alloca have function scope
2 parents e04d0eb + d397f58 commit 3277cf3

File tree

5 files changed

+150
-38
lines changed

5 files changed

+150
-38
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <stdlib.h>
2+
3+
#ifdef _WIN32
4+
void *alloca(size_t alloca_size);
5+
#endif
6+
7+
typedef int *int_ptr;
8+
9+
int_ptr global;
10+
11+
void foo()
12+
{
13+
int_ptr ptr[2];
14+
for(int i = 0; i < 2; ++i)
15+
{
16+
ptr[i] = alloca(sizeof(int));
17+
}
18+
*(ptr[0]) = 42;
19+
*(ptr[1]) = 42;
20+
21+
_Bool nondet;
22+
if(nondet)
23+
global = ptr[0];
24+
else
25+
global = ptr[1];
26+
}
27+
28+
int main()
29+
{
30+
foo();
31+
*global = 42;
32+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--pointer-check
4+
^\[main.pointer_dereference.\d+\] line 31 dereference failure: dead object in \*global: FAILURE$
5+
^\*\* 1 of \d+ failed
6+
^VERIFICATION FAILED$
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
^warning: ignoring

src/goto-programs/builtin_functions.cpp

Lines changed: 94 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Author: Daniel Kroening, [email protected]
1616
#include <util/cprover_prefix.h>
1717
#include <util/expr_initializer.h>
1818
#include <util/expr_util.h>
19+
#include <util/fresh_symbol.h>
1920
#include <util/mathematical_expr.h>
2021
#include <util/mathematical_types.h>
2122
#include <util/pointer_expr.h>
@@ -721,6 +722,97 @@ void goto_convertt::do_havoc_slice(
721722
dest.add(goto_programt::make_other(array_replace, source_location));
722723
}
723724

725+
/// alloca allocates memory that is freed when leaving the function (and not the
726+
/// block, as regular destructors would do).
727+
void goto_convertt::do_alloca(
728+
const exprt &lhs,
729+
const symbol_exprt &function,
730+
const exprt::operandst &arguments,
731+
goto_programt &dest,
732+
const irep_idt &mode)
733+
{
734+
const source_locationt &source_location = function.source_location();
735+
exprt new_lhs = lhs;
736+
737+
// make sure we have a left-hand side to track the allocation even when the
738+
// original program did not
739+
if(lhs.is_nil())
740+
{
741+
new_lhs = new_tmp_symbol(
742+
to_code_type(function.type()).return_type(),
743+
"alloca",
744+
dest,
745+
source_location,
746+
mode)
747+
.symbol_expr();
748+
}
749+
750+
// do the actual function call
751+
code_function_callt function_call(new_lhs, function, arguments);
752+
function_call.add_source_location() = source_location;
753+
copy(function_call, FUNCTION_CALL, dest);
754+
755+
// Don't add instrumentation when we're in alloca (which might in turn call
756+
// __builtin_alloca) -- the instrumentation will be done for the call of
757+
// alloca. Also, we can only add instrumentation when we're in a function
758+
// context.
759+
if(
760+
function.source_location().get_function() == "alloca" || !targets.prefix ||
761+
!targets.suffix)
762+
{
763+
return;
764+
}
765+
766+
// create a symbol to eventually (and non-deterministically) mark the
767+
// allocation as dead; this symbol has function scope and is initialised to
768+
// NULL
769+
symbol_exprt this_alloca_ptr =
770+
get_fresh_aux_symbol(
771+
to_code_type(function.type()).return_type(),
772+
id2string(function.source_location().get_function()),
773+
"tmp_alloca",
774+
source_location,
775+
mode,
776+
symbol_table)
777+
.symbol_expr();
778+
goto_programt decl_prg;
779+
decl_prg.add(goto_programt::make_decl(this_alloca_ptr, source_location));
780+
decl_prg.add(goto_programt::make_assignment(
781+
this_alloca_ptr,
782+
null_pointer_exprt{to_pointer_type(this_alloca_ptr.type())},
783+
source_location));
784+
targets.prefix->destructive_insert(
785+
targets.prefix->instructions.begin(), decl_prg);
786+
787+
// non-deterministically update this_alloca_ptr
788+
if_exprt rhs{
789+
side_effect_expr_nondett{bool_typet(), source_location},
790+
new_lhs,
791+
this_alloca_ptr};
792+
dest.add(goto_programt::make_assignment(
793+
this_alloca_ptr, std::move(rhs), source_location));
794+
795+
// mark pointer to alloca result as dead, unless the alloca result (in
796+
// this_alloca_ptr) is still NULL
797+
symbol_exprt dead_object_sym =
798+
ns.lookup(CPROVER_PREFIX "dead_object").symbol_expr();
799+
exprt alloca_result =
800+
typecast_exprt::conditional_cast(this_alloca_ptr, dead_object_sym.type());
801+
if_exprt not_null{
802+
equal_exprt{
803+
this_alloca_ptr,
804+
null_pointer_exprt{to_pointer_type(this_alloca_ptr.type())}},
805+
dead_object_sym,
806+
std::move(alloca_result)};
807+
auto assign = goto_programt::make_assignment(
808+
std::move(dead_object_sym), std::move(not_null), source_location);
809+
targets.suffix->insert_before_swap(
810+
targets.suffix->instructions.begin(), assign);
811+
targets.suffix->insert_after(
812+
targets.suffix->instructions.begin(),
813+
goto_programt::make_dead(this_alloca_ptr, source_location));
814+
}
815+
724816
/// add function calls to function queue for later processing
725817
void goto_convertt::do_function_call_symbol(
726818
const exprt &lhs,
@@ -1398,45 +1490,9 @@ void goto_convertt::do_function_call_symbol(
13981490

13991491
copy(function_call, FUNCTION_CALL, dest);
14001492
}
1401-
else if(
1402-
(identifier == "alloca" || identifier == "__builtin_alloca") &&
1403-
function.source_location().get_function() != "alloca")
1493+
else if(identifier == "alloca" || identifier == "__builtin_alloca")
14041494
{
1405-
const source_locationt &source_location = function.source_location();
1406-
exprt new_lhs = lhs;
1407-
1408-
if(lhs.is_nil())
1409-
{
1410-
new_lhs = new_tmp_symbol(
1411-
to_code_type(function.type()).return_type(),
1412-
"alloca",
1413-
dest,
1414-
source_location,
1415-
mode)
1416-
.symbol_expr();
1417-
}
1418-
1419-
code_function_callt function_call(new_lhs, function, arguments);
1420-
function_call.add_source_location() = source_location;
1421-
copy(function_call, FUNCTION_CALL, dest);
1422-
1423-
// create a backup copy to ensure that no assignments to the pointer affect
1424-
// the destructor code that will execute eventually
1425-
if(!lhs.is_nil())
1426-
make_temp_symbol(new_lhs, "alloca", dest, mode);
1427-
1428-
// mark pointer to alloca result as dead
1429-
symbol_exprt dead_object_sym =
1430-
ns.lookup(CPROVER_PREFIX "dead_object").symbol_expr();
1431-
exprt alloca_result =
1432-
typecast_exprt::conditional_cast(new_lhs, dead_object_sym.type());
1433-
if_exprt rhs{
1434-
side_effect_expr_nondett{bool_typet(), source_location},
1435-
std::move(alloca_result),
1436-
dead_object_sym};
1437-
code_assignt assign{
1438-
std::move(dead_object_sym), std::move(rhs), source_location};
1439-
targets.destructor_stack.add(assign);
1495+
do_alloca(lhs, function, arguments, dest, mode);
14401496
}
14411497
else
14421498
{

src/goto-programs/goto_convert_class.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,9 @@ class goto_convertt:public messaget
382382

383383
struct targetst
384384
{
385+
goto_programt *prefix = nullptr;
386+
goto_programt *suffix = nullptr;
387+
385388
bool return_set, has_return_value, break_set, continue_set,
386389
default_set, throw_set, leave_set;
387390

@@ -688,6 +691,12 @@ class goto_convertt:public messaget
688691
const exprt::operandst &arguments,
689692
goto_programt &dest,
690693
const irep_idt &mode);
694+
void do_alloca(
695+
const exprt &lhs,
696+
const symbol_exprt &function,
697+
const exprt::operandst &arguments,
698+
goto_programt &dest,
699+
const irep_idt &mode);
691700

692701
exprt get_array_argument(const exprt &src);
693702
};

src/goto-programs/goto_convert_functions.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,8 @@ void goto_convert_functionst::convert_function(
196196
tmp_end_function.add(goto_programt::make_end_function(end_location));
197197

198198
targets = targetst();
199+
targets.prefix = &f.body;
200+
targets.suffix = &tmp_end_function;
199201
targets.set_return(end_function);
200202
targets.has_return_value = code_type.return_type().id() != ID_empty &&
201203
code_type.return_type().id() != ID_constructor &&
@@ -235,6 +237,9 @@ void goto_convert_functionst::convert_function(
235237
f.make_hidden();
236238

237239
lifetime = parent_lifetime;
240+
241+
targets.prefix = nullptr;
242+
targets.suffix = nullptr;
238243
}
239244

240245
void goto_convert(goto_modelt &goto_model, message_handlert &message_handler)

0 commit comments

Comments
 (0)