-
Notifications
You must be signed in to change notification settings - Fork 263
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
Aliasing pre-condition not processed correctly if is_fresh
clause it not standalone
#8492
Comments
This harness gets into a state where void dummy0(int *r, int *a)
__CPROVER_requires(r != NULL && __CPROVER_is_fresh(r, sizeof(int)) && r == a)
__CPROVER_assigns(*r)
__CPROVER_ensures(*a == 42) {
int *dummy_r_before = r;
int *dummy_a_before = a;
int dummy_star_r_before = *r;
int dummy_star_a_before = *a;
__CPROVER_assert(r == a, " r == a before");
__CPROVER_assert(*r == *a, "*r == *a before");
*r = 42;
int *dummy_r_after = r;
int *dummy_a_after = a;
int dummy_star_r_after = *r;
int dummy_start_a_after = *a;
__CPROVER_assert(r == a, " r == a after");
__CPROVER_assert(*r == *a, "*r == *a after");
}
with trace for
and trace for
Interestingly, the counter examples disappear if i break down the conjunction into separate clauses: void dummy0(int *r, int *a)
__CPROVER_requires(r != NULL)
__CPROVER_requires(__CPROVER_is_fresh(r, sizeof(int)))
__CPROVER_requires(r == a)
__CPROVER_assigns(*r)
__CPROVER_ensures(*a == 42) {
int *dummy_r_before = r;
int *dummy_a_before = a;
int dummy_star_r_before = *r;
int dummy_start_a_before = *a;
__CPROVER_assert(r == a, " r == a before");
__CPROVER_assert(*r == *a, "*r == *a before");
*r = 42;
int *dummy_r_after = r;
int *dummy_a_after = a;
int dummy_star_r_after = *r;
int dummy_start_a_after = *a;
__CPROVER_assert(r == a, " r == a after");
__CPROVER_assert(*r == *a, "*r == *a after");
}
|
Minimum reproducer /* cbmc main.c */
void main(void) {
int *r;
int *a;
bool tmp_if_expr;
if(r==NULL) {
goto L1;
}
r = malloc(4);
__CPROVER_assume(r);
tmp_if_expr = true;
goto L2;
L1:
tmp_if_expr = false;
L2:
__CPROVER_assume(tmp_if_expr);
// if `r == NULL` held in the initial state, `malloc` was skipped, `tmp_if_expr` set to `false` and `__CPROVER_assume(false)` was executed. Since progress stops on an assume false, none of the downstream assertions will be reached with `r == NULL`.
// if `r != NULL` held in the initial state, `malloc` was executed, `r` was updated to a point to a fresh object, and `tmp_if_expr` set to `true` and `__CPROVER_assume(true)` was executed. Execution continue with `r` pointing to a fresh object.
__CPROVER_assume(r == a);
__CPROVER_assert(a == r, "a == r before");
__CPROVER_assert(*a == *r, "*a == *r before");
*r = 42;
__CPROVER_assert(a == r, "a == r after");
__CPROVER_assert(*a == *r, "*a == *r after");
} |
The problem is that the value set that we use for dereferencing during symbolic execution does not have any value for |
Value sets and constant propagation are our only sources of points-to information when resolving dereferences in goto-symex. Therefore, assuming or branching on equalities of pointer-typed variables must have us consider both of their value sets to be equal. Else we may end up with spurious counterexamples as seen with the enclosed regression test (where we previously did not have any known value for `a`). Fixes: diffblue#8492
Previously, when a function under proof would assume a pointer to a structure, the verification harness would construct an instance of that structure on the stack and pass its address. This approach is unsound, because it adds to the contractual assumptions of the function the specifics of the function invocation in the test harness. For example, a missing bounds constraint in the spec might go undetected just because the stack-allocated variable happens to satisfy it. The most robust way to deal with this, so it seems, is to minimize the harnesses to merely pass uninitialized variables of the right type to the function under proof. In particular, where a pointer is expected, pass an uninitialized pointer, rather than the address of a stack allocated structure. Also, remove redundant `x != NULL` preconditions when `IS_FRESH(x,...)` is assumed. This is not only redundant, but also error prone because of diffblue/cbmc#8492. This commit implements those changes for all harnesses implemented so far. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Previously, when a function under proof would assume a pointer to a structure, the verification harness would construct an instance of that structure on the stack and pass its address. This approach is unsound, because it adds to the contractual assumptions of the function the specifics of the function invocation in the test harness. For example, a missing bounds constraint in the spec might go undetected just because the stack-allocated variable happens to satisfy it. The most robust way to deal with this, so it seems, is to minimize the harnesses to merely pass uninitialized variables of the right type to the function under proof. In particular, where a pointer is expected, pass an uninitialized pointer, rather than the address of a stack allocated structure. Also, remove redundant `x != NULL` preconditions when `IS_FRESH(x,...)` is assumed. This is not only redundant, but also error prone because of diffblue/cbmc#8492. This commit implements those changes for all harnesses implemented so far. As it turns out, a few specs were incorrect because of missing IS_FRESH annotations, hidden by the correct allocation in the harness. Signed-off-by: Hanno Becker <beckphan@amazon.co.uk>
Value sets and constant propagation are our only sources of points-to information when resolving dereferences in goto-symex. Therefore, assuming or branching on equalities of pointer-typed variables must have us consider both of their value sets to be equal. Else we may end up with spurious counterexamples as seen with the enclosed regression test (where we previously did not have any known value for `a`). Fixes: diffblue#8492
CBMC Version: 6.3.1
The following proves successfully:
The following does not:
Even if the
r != NULL
clause is redundant, this is surprising -- is there dedicated logic for detectingis_fresh
clauses which only works whenis_fresh
is used at the top-level in arequires
clause?The text was updated successfully, but these errors were encountered: