-
Notifications
You must be signed in to change notification settings - Fork 274
Function contracts: check loop freeness before instrumentation, skip assignments to locals and prune write set using CFG info. #6533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this relate to #6527?
@@ -20,6 +20,7 @@ Date: July 2021 | |||
|
|||
#include <util/arith_tools.h> | |||
#include <util/c_types.h> | |||
#include <util/format_expr.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debugging leftover?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
"Loops remain in function '" + id2string(function) + | ||
"', assigns clause checking instrumentation cannot be applied."); | ||
|
||
// Create a deep copy of the iriginal goto_function object |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/iriginal/original/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
if(can_cast_expr<symbol_exprt>(lhs)) | ||
return !cfg_info_opt.value().is_local( | ||
to_symbol_expr(lhs).get_identifier()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With can_cast_expr
you can use
if(auto lhs_symbol = can_cast_expr<symbol_exprt>(lhs))
return !cfg_info_opt->is_local(lhs_symbol->get_identifier());
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lhs_symbol would be a boolean in that case, not the result of the cast ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
target(goto_function.body.instructions.begin()), | ||
bitvector_analysis(goto_function, ns), | ||
dirty_analysis(goto_function), | ||
dirty_idents(dirty_analysis.get_dirty_ids()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you copy these instead of using dirty_analysis
when needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
Codecov Report
@@ Coverage Diff @@
## develop #6533 +/- ##
===========================================
- Coverage 75.98% 75.98% -0.01%
===========================================
Files 1578 1578
Lines 180919 181032 +113
===========================================
+ Hits 137476 137551 +75
- Misses 43443 43481 +38
Continue to review full report at Codecov.
|
// skip checks on function parameter assignments | ||
true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should really be an enum.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An enum with TRUE and FALSE values ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A 2-value enum, where the "true" and "false" values carry meaningful names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
Following that logic we should change all bool
return types to SUCCESS/FAILURE
for
replace_calls
,
-enforce_contracts
,enforce_contract
,check_frame_conditions_function
,apply_function_contract
,
// Unless proved non-dirty by the CFG analysis we assume it is dirty. | ||
if(!cfg_info_opt.has_value()) | ||
return true; | ||
|
||
return cfg_info_opt.value().is_not_local_or_dirty_local( | ||
target->dead_symbol().get_identifier()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Unless proved non-dirty by the CFG analysis we assume it is dirty. | |
if(!cfg_info_opt.has_value()) | |
return true; | |
return cfg_info_opt.value().is_not_local_or_dirty_local( | |
target->dead_symbol().get_identifier()); | |
// Unless proved non-dirty by the CFG analysis we assume it is dirty. | |
if(cfg_info_opt.has_value()) | |
return cfg_info_opt.value().is_not_local_or_dirty_local( | |
target->dead_symbol().get_identifier()); | |
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
{ | ||
const auto &pragmas = instruction_it->source_location().get_pragmas(); | ||
if(pragmas.find(CONTRACT_PRAGMA_DISABLE_ASSIGNS_CHECK) != pragmas.end()) | ||
// skip instructions marked as disabled for assigns clause checking |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// skip instructions marked as disabled for assigns clause checking | |
// Skip instructions marked as disabled for assigns clause checking. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
The dependency on |
40409fa
to
d2ba49f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only minor comments left.
void simplify_gotos(goto_programt &goto_program, namespacet &ns) | ||
{ | ||
for(auto &instruction : goto_program.instructions) | ||
{ | ||
if(instruction.is_goto()) | ||
{ | ||
const auto &condition = instruction.get_condition(); | ||
const auto &simplified = simplify_expr(condition, ns); | ||
if(simplified.is_false()) | ||
instruction.turn_into_skip(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void simplify_gotos(goto_programt &goto_program, namespacet &ns) | |
{ | |
for(auto &instruction : goto_program.instructions) | |
{ | |
if(instruction.is_goto()) | |
{ | |
const auto &condition = instruction.get_condition(); | |
const auto &simplified = simplify_expr(condition, ns); | |
if(simplified.is_false()) | |
instruction.turn_into_skip(); | |
} | |
} | |
} | |
void simplify_gotos(goto_programt &goto_program, namespacet &ns) | |
{ | |
for(auto &instruction : goto_program.instructions) | |
if(instruction.is_goto() && simplify_expr(instruction.get_condition(), ns).is_false()) | |
instruction.turn_into_skip(); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
bool assigns_clauset::conditional_address_ranget::maybe_alive( | ||
cfg_infot &cfg_info) const | ||
{ | ||
if(can_cast_expr<symbol_exprt>(source_expr)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed earlier today: could be implemented using if(auto sym_expr = expr_try_dynamic_cast<symbol_exprt>(source_expr))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
auto &cfg_info = cfg_info_opt.value(); | ||
for(const auto &target : write_set) | ||
if(target.maybe_alive(cfg_info)) | ||
conditions.push_back(target.generate_unsafe_inclusion_check(lhs)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this actually introduce the same check in multiple places? Wouldn't it be sufficient to check this once?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it generates an inclusion check for each possible target of the write set, and each check is specify to the lhs of the instrumented assignment, so no doublons are created here.
// skip checks on function parameter assignments | ||
true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A 2-value enum, where the "true" and "false" values carry meaningful names.
|
||
return false; | ||
} | ||
|
||
/// Returns true iff the target instruction is tagged with a | ||
/// 'CONTRACT_PRAGMA_DISABLE_ASSIGNS_CHECK' pragma. | ||
bool has_disable_assigns_check_pragma(const goto_programt::targett &target) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really want an iterator that cannot change (const goto_programt::targett&
) or an iterator so that whatever it points to cannot change (goto_programt::const_targett
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both! Thanks for the tip
bool is_loop_free( | ||
const goto_programt &goto_program, | ||
namespacet &ns, | ||
messaget &log) | ||
{ | ||
// create cfg from instruction list | ||
cfg_baset<empty_cfg_nodet> cfg; | ||
cfg(goto_program); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment from the earlier PR: do you really need such an elaborate analysis, or would looking at is_backwards_goto()
for all instructions in goto_program
also do the trick?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, that indeed does not imply the existence of a loop. But then, would natural_loopst
do the trick here and relieve you of some of the heavy lifting?
d2ba49f
to
d05ed7c
Compare
…ing. - don't instrument assignments to locals and function parameters, - don't add function parameters and non-dirty locals to the write set, - remove from the local write set CARs that are provably not alive, - remove function parameters and locals from assigns clauses in tests. Sanity checks: - change GOTO instruction with false guards into SKIP instructions (removes artificial loops), - check loop-freeness before assigns clause instrumentation (required for soundness of assigns clause checking). The net effect of these changes is a better proof performance because of a reduction in the number of generated assertions and of their size, but otherwise the contract checking functionality remains unchanged. Rationale: Assigning to local variables or function parameter is always legal so we skip instrumenting these assignments. We avoid adding function parameters and local variables to the local write set, except when their address is taken at some point in the program and can later be assigned to indirectly via pointers. When generating inclusion checks for assignments, we remove from the local write set targets which are not possibly alive at the ASSIGN instruction that gets instrumented.
d05ed7c
to
f7752cf
Compare
The first two commits are from this draft PR #6525 which adds a
maybe_alive
bit tolocal_bitvector_analysist
and is needed by this PR.This PR adds:
In
check_frame_conditions_function
:For assigns clause checking:
The net effect of these changes is a better proof performance because of a reduction in the number of generated assertions and of their size, but otherwise the contract checking functionality remains unchanged.