-
Notifications
You must be signed in to change notification settings - Fork 277
Adds the notion of a local write set to contracts #6331
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
Changes from all commits
fba232b
c06ccf1
52387ab
c255a15
e9cfb2c
6d86395
39c62dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#include <assert.h> | ||
#include <stdlib.h> | ||
|
||
int x = 0; | ||
int y = 0; | ||
|
||
void foo(int *xp, int *xq, int a) __CPROVER_assigns(*xp) | ||
{ | ||
a = 2; | ||
int *yp = malloc(sizeof(int)); | ||
free(yp); | ||
int z = 3; | ||
*xp = 1; | ||
y = -1; | ||
} | ||
|
||
void bar(int *a, int *b) __CPROVER_assigns(*a, *b) | ||
{ | ||
free(a); | ||
*b = 0; | ||
} | ||
|
||
void baz(int *a, int *c) __CPROVER_assigns(*a) | ||
{ | ||
free(c); | ||
*a = 0; | ||
} | ||
|
||
int main() | ||
{ | ||
int z = 0; | ||
foo(&x, &z, z); | ||
assert(x == 1); | ||
assert(y == -1); | ||
assert(z == 0); | ||
int *a = malloc(sizeof(*a)); | ||
int b = 1; | ||
bar(a, &b); | ||
assert(b == 0); | ||
int *c = malloc(sizeof(*c)); | ||
baz(&y, c); | ||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
CORE | ||
main.c | ||
--enforce-all-contracts _ --pointer-primitive-check | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^\[bar.\d+\] line \d+ Check that \*a is assignable: SUCCESS$ | ||
^\[bar.\d+\] line \d+ Check that \*b is assignable: SUCCESS$ | ||
^\[baz.\d+\] line \d+ Check that \*c is assignable: FAILURE$ | ||
^\[baz.\d+\] line \d+ Check that \*a is assignable: SUCCESS$ | ||
^\[foo.\d+\] line \d+ Check that a is assignable: SUCCESS$ | ||
^\[foo.\d+\] line \d+ Check that yp is assignable: SUCCESS$ | ||
^\[foo.\d+\] line \d+ Check that z is assignable: SUCCESS$ | ||
^\[foo.\d+\] line \d+ Check that \*xp is assignable: SUCCESS$ | ||
^\[foo.\d+\] line \d+ Check that y is assignable: FAILURE$ | ||
^.* 2 of \d+ failed \(\d+ iteration.*\)$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
^\[foo.\d+\] line \d+ Check that a is assignable: FAILURE$ | ||
^\[foo.pointer\_primitives.\d+\] line \d+ deallocated dynamic object in POINTER_OBJECT\(tmp\_cc\$\d+\): FAILURE$ | ||
^.* 3 of \d+ failed (\d+ iterations)$ | ||
-- | ||
Checks whether contract enforcement works with functions that deallocate memory. | ||
We had problems before when freeing a variable, but still keeping it on | ||
the writable set, which lead to deallocated variables issues. | ||
Now, if a memory is deallocated, we remove it from the our freely assignable set. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#include <assert.h> | ||
|
||
static int b = 0; | ||
static int c = 0; | ||
|
||
int f() __CPROVER_assigns() | ||
{ | ||
static int a = 0; | ||
a++; | ||
return a; | ||
} | ||
|
||
int g(int *points_to_b, int *points_to_c) | ||
__CPROVER_assigns(b) // Error: it should also mention c | ||
{ | ||
b = 1; | ||
c = 2; | ||
*points_to_b = 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice test case! We are now correctly looking at assignable (and freely assignable) "addresses" instead of symbols :) |
||
*points_to_c = 2; | ||
} | ||
|
||
int main() | ||
{ | ||
assert(f() == 1); | ||
assert(f() == 2); | ||
assert(b == 0); | ||
assert(c == 0); | ||
g(&b, &c); | ||
assert(b == 1); | ||
assert(c == 2); | ||
|
||
return 0; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
CORE | ||
main.c | ||
--enforce-all-contracts | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^\[f.\d+\] line \d+ Check that a is assignable: SUCCESS$ | ||
^\[g.\d+\] line \d+ Check that b is assignable: SUCCESS$ | ||
^\[g.\d+\] line \d+ Check that c is assignable: FAILURE$ | ||
^\[g.\d+\] line \d+ Check that \*points\_to\_b is assignable: SUCCESS$ | ||
^\[g.\d+\] line \d+ Check that \*points\_to\_c is assignable: FAILURE$ | ||
^VERIFICATION FAILED$ | ||
-- | ||
-- | ||
Checks whether contract enforcement works with (local and global) static variables. | ||
Local static variables should be part of the local write set. | ||
Global static variables should be included in the global write set, | ||
otherwise any assignment to it is invalid. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
KNOWNBUG | ||
CORE | ||
main.c | ||
--enforce-contract foo --replace-call-with-contract bar --replace-call-with-contract baz _ --pointer-primitive-check | ||
^EXIT=0$ | ||
^EXIT=10$ | ||
^SIGNAL=0$ | ||
^VERIFICATION SUCCESSFUL$ | ||
^\[foo.\d+\] line \d+ Check that z is assignable: FAILURE$ | ||
^.* 1 of \d+ failed \(\d+ iteration.*\)$ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment. Why are we checking There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for explaining offline. Resolving this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For this case, it's OK to track There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a little confused as to why only 1 assertion fails here though. The assigns clause for
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We discussed this offline. Two important points:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hmm, but
On digging a little bit deeper, I realized that the we don't actually need to separately check the assigns clause during replacement. We can go ahead and havoc directly, and those havocs would fail if something is not assignable in the calling context. |
||
^VERIFICATION FAILED$ | ||
// bar | ||
ASSERT \*foo::x > 0 | ||
IF ¬\(\*foo::x = 3\) THEN GOTO \d | ||
|
@@ -17,9 +19,3 @@ This test checks support for a malloced pointer that is assigned to by | |
a function (bar and baz). Both functions bar and baz are being replaced by | ||
their function contracts, while the calling function foo is being checked | ||
(by enforcing it's function contracts). | ||
|
||
BUG: `z` is being assigned to in `foo`, but is not in `foo`s assigns clause! | ||
This test is expected to pass but it should not. | ||
It somehow used to (and still does on *nix), but that seems buggy. | ||
Most likely the bug is related to `freely_assignable_symbols`, | ||
which would be properly fixed in a subsequent PR. |
Uh oh!
There was an error while loading. Please reload this page.