Skip to content

Commit 79c1637

Browse files
authored
Merge pull request #6643 from remi-delmas-3000/assigns-checking-self-contained
CONTRACTS: Moving `check_frame_conditions` to `instrument_assigns_clauset`.
2 parents 5469136 + 222d266 commit 79c1637

File tree

6 files changed

+367
-381
lines changed

6 files changed

+367
-381
lines changed

src/goto-instrument/contracts/contracts.cpp

Lines changed: 7 additions & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -321,14 +321,11 @@ void code_contractst::check_apply_loop_contracts(
321321
optionalt<cfg_infot> cfg_empty_info;
322322

323323
// Perform write set instrumentation on the entire loop.
324-
check_frame_conditions(
325-
function_name,
324+
instrument_spec_assigns.instrument_instructions(
326325
goto_function.body,
327326
loop_head,
328327
loop_end,
329-
instrument_spec_assigns,
330-
// do not skip checks on function parameter assignments
331-
skipt::DontSkip,
328+
skip_function_paramst::NO,
332329
// do not use CFG info for now
333330
cfg_empty_info);
334331

@@ -960,68 +957,6 @@ goto_functionst &code_contractst::get_goto_functions()
960957
return goto_functions;
961958
}
962959

963-
void code_contractst::instrument_assign_statement(
964-
goto_programt::targett &instruction_it,
965-
goto_programt &program,
966-
instrument_spec_assignst &instrument_spec_assigns,
967-
optionalt<cfg_infot> &cfg_info_opt)
968-
{
969-
auto lhs = instruction_it->assign_lhs();
970-
lhs.add_source_location() = instruction_it->source_location();
971-
goto_programt payload;
972-
instrument_spec_assigns.check_inclusion_assignment(
973-
lhs, cfg_info_opt, payload);
974-
insert_before_swap_and_advance(program, instruction_it, payload);
975-
}
976-
977-
void code_contractst::instrument_call_statement(
978-
goto_programt::targett &instruction_it,
979-
const irep_idt &function,
980-
goto_programt &body,
981-
instrument_spec_assignst &instrument_spec_assigns,
982-
optionalt<cfg_infot> &cfg_info_opt)
983-
{
984-
INVARIANT(
985-
instruction_it->is_function_call(),
986-
"The first argument of instrument_call_statement should always be "
987-
"a function call");
988-
989-
const auto &callee_name =
990-
to_symbol_expr(instruction_it->call_function()).get_identifier();
991-
992-
if(callee_name == "malloc")
993-
{
994-
const auto &function_call =
995-
to_code_function_call(instruction_it->get_code());
996-
if(function_call.lhs().is_not_nil())
997-
{
998-
// grab the returned pointer from malloc
999-
auto object = pointer_object(function_call.lhs());
1000-
object.add_source_location() = function_call.source_location();
1001-
// move past the call and then insert the CAR
1002-
instruction_it++;
1003-
goto_programt payload;
1004-
instrument_spec_assigns.track_heap_allocated(object, payload);
1005-
insert_before_swap_and_advance(body, instruction_it, payload);
1006-
// since CAR was inserted *after* the malloc call,
1007-
// move the instruction pointer backward,
1008-
// because the caller increments it in a `for` loop
1009-
instruction_it--;
1010-
}
1011-
}
1012-
else if(callee_name == "free")
1013-
{
1014-
const auto &ptr = instruction_it->call_arguments().front();
1015-
auto object = pointer_object(ptr);
1016-
object.add_source_location() = instruction_it->source_location();
1017-
goto_programt payload;
1018-
instrument_spec_assigns
1019-
.check_inclusion_heap_allocated_and_invalidate_aliases(
1020-
object, cfg_info_opt, payload);
1021-
insert_before_swap_and_advance(body, instruction_it, payload);
1022-
}
1023-
}
1024-
1025960
bool code_contractst::check_for_looped_mallocs(const goto_programt &program)
1026961
{
1027962
// Collect all GOTOs and mallocs
@@ -1101,9 +1036,9 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11011036
auto &function_body = function_obj->second.body;
11021037

11031038
// Get assigns clause for function
1104-
const symbolt &function_sybmol = ns.lookup(function);
1039+
const symbolt &function_symbol = ns.lookup(function);
11051040
const auto &function_with_contract =
1106-
to_code_with_contract_type(function_sybmol.type);
1041+
to_code_with_contract_type(function_symbol.type);
11071042

11081043
instrument_spec_assignst instrument_spec_assigns(
11091044
function, goto_functions, symbol_table, log.get_message_handler());
@@ -1158,7 +1093,7 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11581093

11591094
// Track formal parameters
11601095
goto_programt snapshot_function_parameters;
1161-
for(const auto &param : to_code_type(function_sybmol.type).parameters())
1096+
for(const auto &param : to_code_type(function_symbol.type).parameters())
11621097
{
11631098
goto_programt payload;
11641099
instrument_spec_assigns.track_stack_allocated(
@@ -1170,184 +1105,16 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function)
11701105
goto_functions.update();
11711106

11721107
// Insert write set inclusion checks.
1173-
check_frame_conditions(
1174-
function,
1108+
instrument_spec_assigns.instrument_instructions(
11751109
function_body,
11761110
instruction_it,
11771111
function_body.instructions.end(),
1178-
instrument_spec_assigns,
1179-
// skip checks on function parameter assignments
1180-
skipt::Skip,
1112+
skip_function_paramst::YES,
11811113
cfg_info_opt);
11821114

11831115
return false;
11841116
}
11851117

1186-
/// Returns true iff the target instruction is tagged with a
1187-
/// 'CONTRACT_PRAGMA_DISABLE_ASSIGNS_CHECK' pragma.
1188-
bool has_disable_assigns_check_pragma(
1189-
const goto_programt::const_targett &target)
1190-
{
1191-
const auto &pragmas = target->source_location().get_pragmas();
1192-
return pragmas.find(CONTRACT_PRAGMA_DISABLE_ASSIGNS_CHECK) != pragmas.end();
1193-
}
1194-
1195-
/// Returns true iff an `ASSIGN lhs := rhs` instruction must be instrumented.
1196-
bool must_check_assign(
1197-
const goto_programt::const_targett &target,
1198-
code_contractst::skipt skip_parameter_assigns,
1199-
const namespacet ns,
1200-
const optionalt<cfg_infot> cfg_info_opt)
1201-
{
1202-
if(
1203-
const auto &symbol_expr =
1204-
expr_try_dynamic_cast<symbol_exprt>(target->assign_lhs()))
1205-
{
1206-
if(
1207-
skip_parameter_assigns == code_contractst::skipt::DontSkip &&
1208-
ns.lookup(symbol_expr->get_identifier()).is_parameter)
1209-
return true;
1210-
1211-
if(cfg_info_opt.has_value())
1212-
return !cfg_info_opt.value().is_local(symbol_expr->get_identifier());
1213-
}
1214-
1215-
return true;
1216-
}
1217-
1218-
/// Returns true iff a `DECL x` must be added to the local write set.
1219-
///
1220-
/// A variable is called 'dirty' if its address gets taken at some point in
1221-
/// the program.
1222-
///
1223-
/// Assuming the goto program is obtained from a structured C program that
1224-
/// passed C compiler checks, non-dirty variables can only be assigned to
1225-
/// directly by name, cannot escape their lexical scope, and are always safe
1226-
/// to assign. Hence, we only track dirty variables in the write set.
1227-
bool must_track_decl(
1228-
const goto_programt::const_targett &target,
1229-
const optionalt<cfg_infot> &cfg_info_opt)
1230-
{
1231-
if(cfg_info_opt.has_value())
1232-
return cfg_info_opt.value().is_not_local_or_dirty_local(
1233-
target->decl_symbol().get_identifier());
1234-
1235-
// Unless proved non-dirty by the CFG analysis we assume it is dirty.
1236-
return true;
1237-
}
1238-
1239-
/// Returns true iff a `DEAD x` must be processed to upate the local write set.
1240-
/// The conditions are the same than for tracking a `DECL x` instruction.
1241-
bool must_track_dead(
1242-
const goto_programt::const_targett &target,
1243-
const optionalt<cfg_infot> &cfg_info_opt)
1244-
{
1245-
// Unless proved non-dirty by the CFG analysis we assume it is dirty.
1246-
if(!cfg_info_opt.has_value())
1247-
return true;
1248-
1249-
return cfg_info_opt.value().is_not_local_or_dirty_local(
1250-
target->dead_symbol().get_identifier());
1251-
}
1252-
1253-
void code_contractst::check_frame_conditions(
1254-
const irep_idt &function,
1255-
goto_programt &body,
1256-
goto_programt::targett instruction_it,
1257-
const goto_programt::targett &instruction_end,
1258-
instrument_spec_assignst &instrument_spec_assigns,
1259-
skipt skip_parameter_assigns,
1260-
optionalt<cfg_infot> &cfg_info_opt)
1261-
{
1262-
while(instruction_it != instruction_end)
1263-
{
1264-
// Skip instructions marked as disabled for assigns clause checking
1265-
if(has_disable_assigns_check_pragma(instruction_it))
1266-
{
1267-
instruction_it++;
1268-
if(cfg_info_opt.has_value())
1269-
cfg_info_opt.value().step();
1270-
continue;
1271-
}
1272-
1273-
// Do not instrument this instruction again in the future,
1274-
// since we are going to instrument it now below.
1275-
add_pragma_disable_assigns_check(*instruction_it);
1276-
1277-
if(
1278-
instruction_it->is_decl() &&
1279-
must_track_decl(instruction_it, cfg_info_opt))
1280-
{
1281-
// grab the declared symbol
1282-
const auto &decl_symbol = instruction_it->decl_symbol();
1283-
// move past the call and then insert the CAR
1284-
instruction_it++;
1285-
goto_programt payload;
1286-
instrument_spec_assigns.track_stack_allocated(decl_symbol, payload);
1287-
insert_before_swap_and_advance(body, instruction_it, payload);
1288-
// since CAR was inserted *after* the DECL instruction,
1289-
// move the instruction pointer backward,
1290-
// because the loop step takes care of the increment
1291-
instruction_it--;
1292-
}
1293-
else if(
1294-
instruction_it->is_assign() &&
1295-
must_check_assign(
1296-
instruction_it, skip_parameter_assigns, ns, cfg_info_opt))
1297-
{
1298-
instrument_assign_statement(
1299-
instruction_it, body, instrument_spec_assigns, cfg_info_opt);
1300-
}
1301-
else if(instruction_it->is_function_call())
1302-
{
1303-
instrument_call_statement(
1304-
instruction_it, function, body, instrument_spec_assigns, cfg_info_opt);
1305-
}
1306-
else if(
1307-
instruction_it->is_dead() &&
1308-
must_track_dead(instruction_it, cfg_info_opt))
1309-
{
1310-
const auto &symbol = instruction_it->dead_symbol();
1311-
if(instrument_spec_assigns.stack_allocated_is_tracked(symbol))
1312-
{
1313-
goto_programt payload;
1314-
instrument_spec_assigns.invalidate_stack_allocated(symbol, payload);
1315-
insert_before_swap_and_advance(body, instruction_it, payload);
1316-
}
1317-
else
1318-
{
1319-
// For loops, the loop_head appears after the DECL of counters,
1320-
// and any other temporaries introduced during "initialization".
1321-
// However, they go `DEAD` (possible conditionally) inside the loop,
1322-
// in presence of return statements.
1323-
// Notice that for them those variables be writable,
1324-
// they must appear as assigns targets anyway,
1325-
// but their DECL statements are outside of the loop.
1326-
log.warning() << "Found a `DEAD` variable " << symbol.get_identifier()
1327-
<< " without corresponding `DECL`, at: "
1328-
<< instruction_it->source_location() << messaget::eom;
1329-
}
1330-
}
1331-
else if(
1332-
instruction_it->is_other() &&
1333-
instruction_it->get_other().get_statement() == ID_havoc_object)
1334-
{
1335-
auto havoc_argument = instruction_it->get_other().operands().front();
1336-
auto havoc_object = pointer_object(havoc_argument);
1337-
havoc_object.add_source_location() = instruction_it->source_location();
1338-
goto_programt payload;
1339-
instrument_spec_assigns.check_inclusion_assignment(
1340-
havoc_object, cfg_info_opt, payload);
1341-
insert_before_swap_and_advance(body, instruction_it, payload);
1342-
}
1343-
1344-
// Move to the next instruction
1345-
instruction_it++;
1346-
if(cfg_info_opt.has_value())
1347-
cfg_info_opt.value().step();
1348-
}
1349-
}
1350-
13511118
bool code_contractst::enforce_contract(const irep_idt &function)
13521119
{
13531120
// Add statements to the source function

src/goto-instrument/contracts/contracts.h

Lines changed: 0 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,6 @@ class code_contractst
111111

112112
namespacet ns;
113113

114-
/// Tells wether to skip or not skip an action
115-
enum class skipt
116-
{
117-
DontSkip,
118-
Skip
119-
};
120-
121114
protected:
122115
symbol_tablet &symbol_table;
123116
goto_functionst &goto_functions;
@@ -133,52 +126,10 @@ class code_contractst
133126
/// Instrument functions to check frame conditions.
134127
bool check_frame_conditions_function(const irep_idt &function);
135128

136-
/// \brief Insert assertion statements into the goto program to ensure that
137-
/// assigned memory is within the assignable memory frame.
138-
///
139-
/// \param function Name of the function getting instrumented.
140-
/// \param body Body of the function getting instrumented.
141-
/// \param instruction_it Iterator to the instruction from which to start
142-
/// instrumentation (inclusive).
143-
/// \param instruction_end Iterator to the instruction at which to stop
144-
/// instrumentation (exclusive).
145-
/// \param instrument_spec_assigns Assigns clause instrumenter of the function
146-
/// \param skip_parameter_assigns If true, will cause assignments to symbol
147-
/// marked as is_parameter to not be instrumented.
148-
/// \param cfg_info_opt Control flow graph information can will be used
149-
/// for write set optimisation if available.
150-
void check_frame_conditions(
151-
const irep_idt &function,
152-
goto_programt &body,
153-
goto_programt::targett instruction_it,
154-
const goto_programt::targett &instruction_end,
155-
instrument_spec_assignst &instrument_spec_assigns,
156-
skipt skip_parameter_assigns,
157-
optionalt<cfg_infot> &cfg_info_opt);
158-
159129
/// Check if there are any malloc statements which may be repeated because of
160130
/// a goto statement that jumps back.
161131
bool check_for_looped_mallocs(const goto_programt &program);
162132

163-
/// Inserts an assertion into program immediately before the assignment
164-
/// instruction_it, to ensure that the left-hand-side of the assignment
165-
/// is "included" in the (conditional address ranges in the) write set.
166-
void instrument_assign_statement(
167-
goto_programt::targett &instruction_it,
168-
goto_programt &program,
169-
instrument_spec_assignst &instrument_spec_assigns,
170-
optionalt<cfg_infot> &cfg_info_opt);
171-
172-
/// Inserts an assertion into program immediately before the function call at
173-
/// instruction_it, to ensure that all memory locations written to by the
174-
// callee are "included" in the (conditional address ranges in the) write set.
175-
void instrument_call_statement(
176-
goto_programt::targett &instruction_it,
177-
const irep_idt &function,
178-
goto_programt &body,
179-
instrument_spec_assignst &instrument_spec_assigns,
180-
optionalt<cfg_infot> &cfg_info_opt);
181-
182133
/// Apply loop contracts, whenever available, to all loops in `function`.
183134
/// Loop invariants, loop variants, and loop assigns clauses.
184135
void apply_loop_contract(

0 commit comments

Comments
 (0)