Skip to content

Commit e062761

Browse files
committed
Memory allocations produced via alloca have function scope
We must not mark the allocation as dead when leaving the block, but instead need to defer that until the end of the function.
1 parent 8848ad8 commit e062761

File tree

5 files changed

+127
-38
lines changed

5 files changed

+127
-38
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
typedef int *int_ptr;
2+
3+
int main()
4+
{
5+
int_ptr ptr[2];
6+
for(int i = 0; i < 2; ++i)
7+
{
8+
ptr[i] = __builtin_alloca(sizeof(int));
9+
}
10+
*(ptr[0]) = 42;
11+
*(ptr[1]) = 42;
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--pointer-check
4+
^VERIFICATION SUCCESSFUL$
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
^warning: ignoring

src/goto-programs/builtin_functions.cpp

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

13991490
copy(function_call, FUNCTION_CALL, dest);
14001491
}
1401-
else if(
1402-
(identifier == "alloca" || identifier == "__builtin_alloca") &&
1403-
function.source_location().get_function() != "alloca")
1492+
else if(identifier == "alloca" || identifier == "__builtin_alloca")
14041493
{
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);
1494+
do_alloca(lhs, function, arguments, dest, mode);
14401495
}
14411496
else
14421497
{

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)