-
Notifications
You must be signed in to change notification settings - Fork 274
Allow checks to be performed inside quantifier statements. #6399
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.
I can't immediately see anything wrong with this BUT the whole thing makes me uneasy. The comments have been there since 2013 which makes me think that they were probably there for a reason. My immediate thought is about scoping and whether the quantified variable is actually live when the variable is checked. I am also unsure about how nested quantifiers are going to work and to what degree the guards are applied to the checks as well.
Can you please say a little more about how you are expecting things to work?
Codecov Report
@@ Coverage Diff @@
## develop #6399 +/- ##
========================================
Coverage 75.98% 75.98%
========================================
Files 1578 1578
Lines 180910 180931 +21
========================================
+ Hits 137467 137487 +20
- Misses 43443 43444 +1
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.
A few more concrete requests for other functionality.
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 happens to work for the specific case of positive forall, since this is doing Skolemisation implicitly.
- This will fail (generating VCCs that are too strong) for exists.
- I would much prefer that the Skolemisation is done explicitly, i.e., by introducing a Skolem constant and instantiating the quantifier with it.
201bb39
to
5be5efc
Compare
Hi Daniel @kroening. With regards to your first query, we have pushed some changes that show that existentially quantified statements appear to be working as they should (or at least, how we expect them to). Could you review these and let us know if these much your expectations, or if there's a counterexample case you could think of that would help us test our change in a way that exposes a weakness of our current implementation? |
regression/cbmc-primitives/forall_6231_3/test_malloc_less_than_bound.c
Outdated
Show resolved
Hide resolved
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 have examples of quantifiers in assumptions?
- Can you briefly outline how this works? What logical expression is generated? How does this interact with scoping?
regression/cbmc-primitives/exists_memory_checks/invalid_index_range.desc
Outdated
Show resolved
Hide resolved
regression/cbmc-primitives/exists_memory_checks/smt_missing_range_check.desc
Outdated
Show resolved
Hide resolved
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.
Thanks for fixing this! This would help us catch issues in code contracts. Earlier, invariants and function contracts that used implications but performed incorrect/unsafe operations were not caught.
All test cases in this PR seem to check that the consequent is safe. May be the fix already checks the antecedent as well, but can we also add a regression test for that? Something like:
__CPROVER_forall {
int i;
(0 <= i && i <= (n / 0)) /* (n/0) should be caught by `--div-by-zero` */
==> ...
}
@SaswatPadhi makes a good point. It is not clear to me whether this treats quantified expressions as guard/statement pairs and if so, whether it checks in both, does it use the guard for the checks in the statement? |
bbdcd3f
to
ff47efb
Compare
Hi Martin, yes, it does treat quantified expressions as guard/statement pairs and yes it uses the guard for the checks in the statement. I have factored the change that does that in its own commit, with an explanation of how it works there. As a shorter explanation here, Does that answer your question? |
Hi @kroening , I'm trying to better understand the requirement of the second part of your comment, which I quote:
My understanding is that Skolemization is being done the same way it was being done before this patch, i.e. in Is that not adequate in some capacity? We have performed extended testing and it seems like this bug fix is working as intended - but if you believe that there's a chance some input might fail we're happy to investigate further and tighten the coverage of the tests for this. Or is your request that we lift this all into a separate module? If yes, where do you think this would be better plugged in - the simplification modules, a completely different pass over the goto-program that hooks into the transformation pipeline, or somewhere else entirely? |
@NlightNFotis Thanks for your response, the explanation does indeed help and yes, I think you are correct about this being a latent bug. Would it be possible to write a test case that doesn't use quantifiers that triggers this? Would something like Please could you have a look at, check and if necessary update: cbmc/src/analyses/goto_check.cpp Line 111 in ce2680b
cbmc/src/analyses/goto_check.cpp Line 1623 in ce2680b
( The real cherry on top would be adding a |
The problem is that the identifiers used in quantifiers are no longer guaranteed to be unique, i.e., you could have something like You can't test that in the C front-end, since the C front-end's treatment of quantifiers pre-dates the introduction of scoping in the solver backends. Please don't even think about reintroducing this restriction, not even as a temporary hack. The way to do this properly is to introduce a Skolem symbol for the bound identifier. You'll find that the classes for exists and forall have convenient methods for making that easy! |
@NlightNFotis while reviewing the changes you had kindly described I ran into some concerns. I have documented them here: #6423 I don't consider them a precondition for this PR, but, obviously, they are linked. |
aec0b8b
to
63f6830
Compare
@kroening Hi Daniel, the last commit is performing scolemisation of the formula in @martin-cs Given that the bug-fix has now been merged on its own PR, could you please re-review this and let me know if there's anything more that needs be done? |
112cf8f
to
e675709
Compare
Hi @kroening, can you take a look at the PR now and let us know if it is more or less what you envisioned for skolemization as a pass? Specifically, it's the last commit that implements this. |
src/analyses/goto_check.cpp
Outdated
CPROVER_PREFIX, | ||
id2string(ns.lookup(quantifier_sym.get_identifier()).base_name), | ||
quantified_expr.source_location(), | ||
ID_C, |
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 still hard coded in your new rename_and_substitute
function and still needs fixing.
f28976d
to
c813374
Compare
Hello @martin-cs and @kroening , would it be possible to have a look at this again? It's past our internal reviews and blocked on your reviews right now. I want to take this forward, either in terms of performing improvements and revisions if deemed necessary, or getting this merged. |
@NlightNFotis My apologies for the delay in reviewing. I will look ASAP. |
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 am removing my block on this but I still have some concerns:
- There are no tests using
assume
; that matters from a user point of view because of code contracts and also from a technical point of view as these are "flipped" during symex so what works with the SAT engine is different. - A lot of the issues with quantifiers are simplified when there is only one level of quantifier alternation. So I feel that alternating tests would be useful.
- The renaming code is not, IMHO, Skolemization and to me, doesn't match what @kroening requested. However I believe that it fixes the immediate issue he raised and I will leave it to him to pursue that one.
- I'm still struggling a bit to see what is generated; I /think/ it is something like
A && __CPROVER_forall { int i; B ==> C}
will create a VC:
assert(A && __CPROVER_forall { int i; B ==> CHECK_FROM_C } );
it would be good to get that confirmed.
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^VERIFICATION FAILED$ | ||
line 9 dereference failure: pointer outside object bounds in a\[\(signed (long|long long) int\)i\]: FAILURE |
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.
Would it be possible to check that the assertion passes?
^EXIT=0$ | ||
^SIGNAL=0$ | ||
^VERIFICATION SUCCESSFUL$ | ||
line 9 dereference failure:.*SUCCESS |
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 more complete check line as you have in other tests would be good.
@@ -0,0 +1,15 @@ | |||
CORE | |||
smt_missing_range_check.c | |||
--pointer-check -z3 |
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 we still don't have a CI run that doesn't have Z3 set up, please can this be tagged so that the regression tests still work on machines that don't have Z3 installed?
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, in the last commit I have added a check for the z3
binary in CMakeLists.txt
^EXIT=0$ | ||
^SIGNAL=0$ | ||
^VERIFICATION SUCCESSFUL$ | ||
line 9 dereference failure:.*SUCCESS |
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.
More complete matching against results would be good.
|
||
assert(*a == *a); | ||
|
||
// BUG: no errors even with `--pointer-check` enabled -- now fixed. |
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 am not quite sure what this comment is trying to tell me. Whatever it is, it feels like there might be a better place and way or saying it.
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.
Apologies, I added this comment in a haste, and now realise that my phrasing was not the best 😞
I have rephrased that in commit 604d12c. Can you let me know if that's clearer?
|
||
assert(*a == *a); | ||
|
||
// BUG: no errors even with `--pointer-check` enabled -- now fixed. |
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 above.
src/goto-programs/skolemization.cpp
Outdated
exprt &condition, | ||
symbol_table_baset &symbol_table) | ||
{ | ||
condition.visit_post([&symbol_table](exprt &sub_expression) { |
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.
Thank you!
src/goto-programs/skolemization.cpp
Outdated
@@ -0,0 +1,57 @@ | |||
/// \file skolemization.h | |||
/// Rename variables in existentially quantified statements. |
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 not what I would think of as Skolemization; this is renaming. For me, Skolemization has to remove the existential so:
assert(__CPROVER_exists { int i; (0 <= i && i < 10) && a[i] == i *i });
becomes:
int skolem_23_i; // Note that if it is nested in a forall then it will need to be an uninterpreted function.
assert( (0 <= skolem_23_i && skolem_23_i < 10) && a[skolem_23_i] == skolem_23_i * skolem_23_i );
The general use-case for this is so that provers only have to deal with one kind of quantifier.
Note that the symex engine performs on-the-fly Skolemization where it can; that's how the SAT engine can handle quantifiers at all. However it dynamically handles polarity, so depending on whether the quantifiers are in assume or assert, negated, etc. it will do the right thing.
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.
Hi Martin,
Apologies for that - I'm aware of how skolemization works (in contrast to the code here), but when implemented in its proper form, it resulted in elimination of the existential quantifier far too early in the pipeline. There's code in goto-check
or goto-symex
(can't remember which of the two) - which depended on the quantifiers being there during VCC generation, and we were left with vccs that didn't semantically match the intended meaning, getting results that were all over the place.
The intended effect of the code above was to do a partial-skolemization, if we can admit that as a thing: perform the renaming, and let the existential quantifier stay, with the result being that the correct VCCs where generated, and it was being eliminated later in the pipeline in a way that felt safer (given that the variables were renamed before hand).
Definitely wasn't ideal, but there was a lot of uncertainty around the implementation of that feature in our pipeline.
My aim now is to drop this and substitute for Daniel's implementation (assuming it passes all tests).
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.
Annoying -- github won't let me just remove my "Request changes" and return to a neutral state. So, I am "approving" this but please don't treat this approved and merge.
Please look at https://github.com/diffblue/cbmc/tree/goto_check_guard for an alternative approach, which relegates the problem of introducing the Skolem functions to the solver. A key benefit of the alternative approach is that this means that there won't be two different kinds of goto-programs, one where bound symbols are unique and one where they aren't. |
06cc456
to
38c80f2
Compare
Hi @martin-cs and @kroening , as requested, I've made several enhancements to the tests (added the extra checks Martin requested, tests based on the behaviour of the quantifiers in assumes, and alternating quantifier tests - each of which are in their own commits) and changed the implementation to be using Daniel's code (I lost the commit information because I couldn't cleanly cherry-pick after some changes to Could you please have a look again and let me know how it looks to you now? |
May I suggest squashing the 9 commits that add/change the tests? Otherwise looks fine. |
ae2e381
to
38c80f2
Compare
Hi @kroening, if all is fine are you happy to lift your blocking review? I will of course squash all the test commits before the final merge, I just want to give a chance to @martin-cs to review them on a commit by commit basis so that he can more easily check if his concerns have been addressed accordingly, and assuming he's also happy I will squash and merge. |
These are checked against `--pointer-check` and one of them is sourced from the code reported in diffblue#6231.
We are now producing more checks, which was messing with the expectations of the tests. Now made it a bit more general and all is good again.
Original code by @kroening in branch `goto_check_guard`.
Original code by @kroening in `goto_check_guard` branch.
e9585bd
to
bc772e4
Compare
This allows checks from CLI flags like
--pointer-check
to be performed insideexpressions in quantifier statements.
This should be addressing issue #6231 .