-
Notifications
You must be signed in to change notification settings - Fork 277
Fix local static variables detection in assigns clauses #6447
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
Fix local static variables detection in assigns clauses #6447
Conversation
@@ -861,7 +861,7 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function) | |||
for(const auto &sym_pair : symbol_table) | |||
{ | |||
const auto &sym = sym_pair.second; | |||
if(sym.is_static_lifetime && sym.location.get_function() == function) | |||
if(sym.is_static_lifetime && !sym.location.get_function().empty()) |
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.
Wouldn't this create CARs for ALL static local variables, like beyond the current function on which contracts are checked and its subfunction calls?
It might have a significant perf impact if lots of unrelated functions also introduce static local variables, because we'll add their CARs unnecessarily to the write set and check every assignment against them.
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 might be worth fixing the inlining routine instead. Inlined static locals should just behave as static locals within the caller. It seems weird and inconsistent to have the function body inlined into a caller but be mutating a static local that's tagged with a different function name.
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.
Indeed.
Since all sub-function calls are inlined for contract checking, a better solution would be to detect static locals used in the body of the function under analysis rather than at the symbol table level.
Updated to a better solution is to detect static locals used in the body of the function under analysis rather than at the symbol table level. |
Codecov Report
@@ Coverage Diff @@
## develop #6447 +/- ##
===========================================
- Coverage 76.04% 76.01% -0.03%
===========================================
Files 1546 1527 -19
Lines 165543 164487 -1056
===========================================
- Hits 125885 125035 -850
+ Misses 39658 39452 -206
Continue to review full report at Codecov.
|
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 requests.
regression/contracts/assigns_enforce_statics_subfunctions/main.c
Outdated
Show resolved
Hide resolved
regression/contracts/assigns_enforce_statics_subfunctions/test.desc
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,14 @@ | |||
CORE | |||
main.c | |||
--enforce-contract bar _ --pointer-primitive-check |
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 we need --pointer-primitive-check
here?
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.
?
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 is there an underscore here (after bar
)?
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 underscore separates two groups of options , the first group is passed to goto-instrument to perform the contract instrumentation, and the second set is passed to cbmc to analyse the instrumented contracts.
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.
Almost there.
regression/contracts/assigns_enforce_statics_subfunctions/test1.c
Outdated
Show resolved
Hide resolved
regression/contracts/assigns_enforce_statics_subfunctions/test2.c
Outdated
Show resolved
Hide resolved
regression/contracts/assigns_enforce_statics_subfunctions/test2.desc
Outdated
Show resolved
Hide resolved
1b03a35
to
0531588
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.
LGTM modulo a few minor comments.
regression/contracts/assigns_enforce_statics_subfunctions/main.c
Outdated
Show resolved
Hide resolved
@@ -0,0 +1,14 @@ | |||
CORE | |||
main.c | |||
--enforce-contract bar _ --pointer-primitive-check |
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.
?
/// \brief Finds symbols declared with a static lifetime in the given | ||
/// `root_function` or one of the functions it calls, | ||
/// and adds them to the given `assigns`. | ||
/// | ||
/// @param functions all functions of the goto_model | ||
/// @param root_function the root function under which to search statics | ||
/// @param assigns assigns clause where search results are added | ||
/// | ||
/// A symbol is considered a static local symbol iff: | ||
/// - it has a static lifetime annotation | ||
/// - its source location has a non-empty function attribute | ||
/// - this function attribute is found in the call graph of the root_function |
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.
/// \brief Finds symbols declared with a static lifetime in the given | |
/// `root_function` or one of the functions it calls, | |
/// and adds them to the given `assigns`. | |
/// | |
/// @param functions all functions of the goto_model | |
/// @param root_function the root function under which to search statics | |
/// @param assigns assigns clause where search results are added | |
/// | |
/// A symbol is considered a static local symbol iff: | |
/// - it has a static lifetime annotation | |
/// - its source location has a non-empty function attribute | |
/// - this function attribute is found in the call graph of the root_function | |
/// \brief Finds symbols declared with a static lifetime in the given | |
/// `root_function` or one of the functions it calls, | |
/// and adds them to the given `assigns`. | |
/// | |
/// @param functions All functions of the `goto_model`. | |
/// @param root_function The root function under which to search statics. | |
/// @param assigns Assigns clause where search results are added. | |
/// | |
/// A symbol is considered a static local symbol iff: | |
/// - it has a static lifetime annotation; | |
/// - its source location has a non-empty function attribute; | |
/// - this function attribute is found in the call graph of the `root_function`. |
We are now collecting symbols from the symbol table that have a static lifetime and are declare in functions occurring in the call graph of the function under analysis |
@@ -0,0 +1,14 @@ | |||
CORE | |||
main.c | |||
--enforce-contract bar _ --pointer-primitive-check |
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 is there an underscore here (after bar
)?
#include <stdlib.h> | ||
|
||
static int x; | ||
static int x = 0; |
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.
Random place for comment: the commit message ...
- Could make clear that this is about code contracts;
- Should likely have the "A symbol is ..." all moved into the body of the message;
- Should likely talk about "called functions" instead of "subfunctions."
@@ -853,18 +856,16 @@ bool code_contractst::check_frame_conditions_function(const irep_idt &function) | |||
function, | |||
symbol_table); | |||
|
|||
// Adds formal parameters to write set | |||
// fetch local statics of all subfunctions in the symbol_table |
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.
Much like the commit message: I don't think "subfunctions" is the right terminology.
// Full inlining of the function body | ||
goto_function_inline(goto_functions, function, ns, log.get_message_handler()); |
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.
I think the comment could be more useful if it explained why full inlining was necessary. (Also, will this work with recursive functions?)
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.
the reason behind inlining is that in the function f
we are instrumenting , a same function g
can be called in several locations under different write sets.
Without inlining, for each call site we would have to create a copy of g and instrument for the write set specific to this call site.
Inlining allows us to avoid having to duplicate g and instrument it specifically.
As for inlining recursive functions, I'll implement a check to detect them and error out during inlining
// - this function appears in the call graph of the function | ||
assigns.add_to_write_set(sym.symbol_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.
What about function pointers?
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.
we suppose function pointers have been removed/restricted before this instrumentation phase so that all possible target functions would appear explicitly in the call graph
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, we should really include a PRECONDITION
in call_graph.cpp
's forall_callsites
to replace if(function_expr.id()==ID_symbol)
by PRECONDITION_WITH_DIAGNOSTICS(function_expr.id() == ID_symbol, "call graph computation requires function pointer removal");
.
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.
PRECONDITION
added in call_graph.cpp
.
Should we also modify goto_function_inline
to warn on unresolved function pointers ?
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.
Yes, we should either warn or fail with a PRECONDITION
.
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.
Some minor comments:
0531588
to
f89fcd4
Compare
f89fcd4
to
09e7bcb
Compare
b64d75b
to
3023ccb
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.
LGTM 🎉
int result = sum(10); | ||
__CPROVER_assert(result == 55, "result == 55"); | ||
return 0; | ||
} |
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.
[nit] missing newline
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.
+1
3023ccb
to
3dfdd56
Compare
@chrisr-diffblue @martin-cs @peterschrammel could you review this PR? It's missing code owner approval. |
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.
Couple of minor nitpicks but nothing that would block the merge.
@@ -15,6 +15,8 @@ Date: July 2021 | |||
|
|||
#include <langapi/language_util.h> | |||
|
|||
#include <analyses/call_graph.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.
[nit] this should go above langapi
include(s).
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
} | ||
} | ||
} | ||
return; |
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.
Redundant return
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
455425e
to
200246c
Compare
200246c
to
df40139
Compare
…call graph add INVARIANT for recursive functions and PRECONDITION for function pointers. - Static local variables declared in functions called by the function under analysis are now added the write set (same for loops) - `code_contractst::check_frame_conditions_function` now errors out on recursive functions - `call_grapht::forall_callsites` now errors out on function pointers
df40139
to
5fb8ee5
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.
Nothing blocking, just a few remarks for future work:
That's quite a lot in a single commit. It could have been split into a series of commits:
- introduce decorator
- add new tests
- add static local detection
- other changes 1
- other changes 2
- other changes 3
- activate/modify tests (separately or with the corresponding commit that makes the test pass)
each of which with a 'why' description in the commit body to make the intent of the PR clearer and easier and faster to review.
@@ -29,6 +29,7 @@ Date: February 2016 | |||
|
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 is file is getting way to large. Consider refactoring/splitting in future.
Function contracts instrumentation:
Other changes:
code_contractst::check_frame_conditions
tocode_contractst::check_frame_conditions_function
.