-
Notifications
You must be signed in to change notification settings - Fork 284
Add new builtin __CPROVER_havoc_slice #6430
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
c1b6b23 to
2698970
Compare
2698970 to
4589c6d
Compare
|
|
||
| This function requires a valid pointer and updates all the bytes of the underlying object with nondeterministic values. | ||
| ```C | ||
| void __CPROVER_havoc_object(void *p); |
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.
When you say "object", what do you mean?
For example, should this assertion pass or fail?
struct foo {
int x;
int y;
};
struct foo thefoo = {.x = 1; .y = 12};
int* p = &thefoo.y;
__CPROVER_havoc_object(p);
assert(thefoo.x == 1);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 object in that case is the blob of memory that holds the whole struct thefoo so this assertion should fail.
on the other it passes when using __CPROVER_havoc_object_slice(p, sizeof(*p))
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 seems like it could lead to a foot gun:
int foo_stub(int* p) {
__CPROVER_havoc_object(p);
return *p;
}
struct foo { int x; int y; };
int main() [
struct foo thefoo = {.x = 1; .y = 12};
int v = foo_stub(&thefoo.y);
assert(thefoo.x == 1);
assert(thefoo.y == v);
}I think the user would expect that foo_stub wouldn't modify memory which it doesn't have a valid pointer to. Maybe we need a different name:
__CPROVER_havoc_entire_allocation()
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.
Agreed, we could name __CPROVER_havoc_object differently but it was introduced some time ago so this could be a breaking change for existing users of the primitive.
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.
Can we just add a more explicit warning to the document about havoc_object that it will havoc enclosing structs.
| throw 0; | ||
| } | ||
|
|
||
| if(arguments[1].type().id() != ID_unsignedbv) |
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.
To improve readability, could we rewrite this as:
| if(arguments[1].type().id() != ID_unsignedbv) | |
| if(arguments[1].type().id() != size_type().id()) |
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 idea with this check was to allow users that use the primitive directly from GOTO to use any unsignedbv type and not just size_t to specify sizes. This makes it more general than its C interface.
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, thinking more, I think it could actually be:
| if(arguments[1].type().id() != ID_unsignedbv) | |
| if(arguments[1].type() != size_type()) |
We define the signature of __CPROVER_havoc_slice as:
void __CPROVER_havoc_slice(void *p, __CPROVER_size_t size);for the frontend.
So, shouldn't the check here match the this signature?
Yes it should be an unsignedbv type, but I think it shouldn't be any unsignedbv type. It should match the size_t semantics on the machine, which is computed by size_type():
Lines 58 to 72 in 4889328
| unsignedbv_typet size_type() | |
| { | |
| // The size type varies. This is unsigned int on some systems, | |
| // and unsigned long int on others, | |
| // and unsigned long long on say Windows 64. | |
| if(config.ansi_c.pointer_width==config.ansi_c.int_width) | |
| return unsigned_int_type(); | |
| else if(config.ansi_c.pointer_width==config.ansi_c.long_int_width) | |
| return unsigned_long_int_type(); | |
| else if(config.ansi_c.pointer_width==config.ansi_c.long_long_int_width) | |
| return unsigned_long_long_int_type(); | |
| else | |
| INVARIANT(false, "width of size type"); // aaah! | |
| } |
Or am I missing something here?
341baa6 to
5671a81
Compare
Codecov Report
@@ Coverage Diff @@
## develop #6430 +/- ##
===========================================
+ Coverage 76.01% 76.02% +0.01%
===========================================
Files 1527 1527
Lines 164465 164510 +45
===========================================
+ Hits 125013 125068 +55
+ Misses 39452 39442 -10
Continue to review full report at Codecov.
|
feliperodri
left a comment
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 changes. Almost ready to go 😄
5671a81 to
a16bdb2
Compare
tautschnig
left a comment
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.
Approving, but please address the comments before merging.
| t->source_location_nonconst().set("user-provided", false); | ||
| t->source_location_nonconst().set_property_class(ID_assertion); | ||
| t->source_location_nonconst().set_comment( | ||
| "assertion havoc_slice " + from_expr(ns, identifier, ok_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.
Rather than fixing up the location after the fact, make these changes to a source location beforehand, like:
source_locationt annotated_location = source_location;
annotated_location.set("user-provided", ...); ...and then use annotated_location when constructing the goto-program instruction.
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.
done
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.
Did you perhaps end up going back and forth on this? It doesn't seem to have been addressed.
| symbol_exprt havoc_slice_function = | ||
| symbol_exprt{CPROVER_PREFIX "havoc_slice", empty_typet()}; |
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 needs to be of a code_typet type, and then makes me wonder whether you should rather fetch this symbol from the symbol table. And that, in turn, makes me wonder whether such a function should make sure it is present 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.
The symbol's signature is only declared in src/ansi-c/cprover_builtin_headers.h.
If someone were to build a goto model programatically without using the C front-end, is there a guarantee that this signature will be present 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.
No, there is no such guarantee. Hence, it would be necessary to check, and add the symbol if it isn't present just yet.
99e757d to
ab9c505
Compare
feliperodri
left a comment
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 module a few more tests updates and Michael's comments.
| CORE | ||
| test_whole_array_with_offset.c | ||
|
|
||
| ^EXIT=10$ | ||
| ^SIGNAL=0$ | ||
| ^VERIFICATION FAILED$ | ||
| -- | ||
| ^.*expecting FAILURE: SUCCESS$ | ||
| ^.*expecting SUCCESS: FAILURE$ | ||
| ^.*dereference .*: 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.
I see that you have a different name for each test case (i.e., a .c file and a test description). The usual pattern across the regression tests is to create a folder with a descriptive name (like the ones you're using for each test case) and add a main.c file and a test.desc file inside that folder. At the end of each test description, we add a small explanation of what behavior the test is covering. Not a blocker, but I suggest you to update this test to follow the same pattern.
| throw 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.
We probably don't have any cases where we try to pass more than 2 parameters, thus, we don't hit this case.
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.
Since tests are expressed in C and uses of havoc_slice are checked by the front end these would be impossible to reach using a test expressed in C.
Covering them would require build and load a goto model directly or create them in C++ directly.
| throw 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.
Same as above for non-pointer parameters.
| throw 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.
Same as above. We can create one test to cover these error cases.
| // check nil lhs | ||
| if(lhs.is_not_nil()) | ||
| { | ||
| error().source_location = source_location; | ||
| error() << "'" << identifier << "' not expected to have a LHS" << eom; | ||
| throw 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.
Same as above.
413c583 to
b9b984f
Compare
b9b984f to
8ee6305
Compare
| optional size; when the size argument is not given, the size of the subtype | ||
| (which must not be **void**) of the pointer type is used. | ||
|
|
||
| #### \_\_CPROVER\_havoc\_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.
Random place for comment: it would be great if the commit message also answered the "Why?" question. See https://chris.beams.io/posts/git-commit/ as linked from the pull-request template. On this occasion, it would be good to answer the "why did __CPROVER_havoc_object not suffice" question.
| t->source_location_nonconst().set("user-provided", false); | ||
| t->source_location_nonconst().set_property_class(ID_assertion); | ||
| t->source_location_nonconst().set_comment( | ||
| "assertion havoc_slice " + from_expr(ns, identifier, ok_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.
Did you perhaps end up going back and forth on this? It doesn't seem to have been addressed.
| vm, code.operands().size() >= 2, "output must have at least two operands"); | ||
| } | ||
|
|
||
| inline code_function_callt |
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.
Remove inline
| /// | ||
| /// \return A \ref code_function_callt expression | ||
| /// `nil_exprt() := __CPROVER_havoc_slice(p, size)`. | ||
| inline code_function_callt |
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.
Remove inline
| #ifndef CPROVER_UTIL_GOTO_INSTRUCTION_CODE_H | ||
| #define CPROVER_UTIL_GOTO_INSTRUCTION_CODE_H | ||
|
|
||
| #include <util/cprover_prefix.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.
You don't actually need this in the header file anymore.
Resolves #6351.
This PR adds a new builtin function :
Which lets you update
sizeconsecutive bytes to nondet values in the object underlying a given pointerp. It requires that__CPROVER_w_ok(p, size)holds.It is rewritten at goto level to an
array_replace(p, q)operation whereqis declared a nondet array of sizesize.