Skip to content

Non-deterministic initialisation of structs, pointers and globals in function call harness #4181

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

Merged
merged 4 commits into from
Feb 18, 2019

Conversation

NlightNFotis
Copy link
Contributor

  • Each commit message has a non-empty body, explaining why the change was made.
  • Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
  • The feature or user visible behaviour I have added or modified has been documented in the User Guide in doc/cprover-manual/
  • Regression or unit tests are included, or existing tests cover the modified code (in this case I have detailed which ones those are in the commit message).
  • My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • White-space or formatting changes outside the feature-related changed lines are in commits of their own.

@hannes-steffenhagen-diffblue
Copy link
Contributor

hannes-steffenhagen-diffblue commented Feb 13, 2019

Basically, this PR is intended to re-implement (and ultimately replace) the changes made in #3251, except in an external tool rather than within CBMC

EDIT: The diff of the refactoring PR is kind of hopeless, mostly what's going on there is that we break up the generate function into multiple utility functions

{
const auto &symbol = symbol_table_entry.second;
if(
symbol.is_static_lifetime && symbol.is_lvalue &&

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We think this means "global", but we weren't entirely sure. This is basically reverse engineered from a --show-symbol-table.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

@NlightNFotis NlightNFotis force-pushed the non-det-struct-new branch 2 times, most recently from 0159b21 to 7eff3f5 Compare February 13, 2019 17:00
/// exist already.
symbol_exprt get_malloc_function();

void get_struct_tag_initializer(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these called get_* if they don't return?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, we'll change it

exprt choice;
if(
depth > initialization_config.min_null_tree_depth &&
depth < initialization_config.max_nondet_tree_depth)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about wrapping these conditions in something a bit more descriptive?, e.g.: should_null_initialize(depth), where
bool should_null_initialize(size_t depth) { return depth >= initialization_config.min_null_tree_depth; }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll consider it, I think a function might be a bit overkill though. A local variable might be an improvement here though!

{
auto const value = require_exactly_one_value(option, values);
p_impl->recursive_initialization_config.min_null_tree_depth =
std::stoul(value);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we have some facility to extract numbers from string safely via std::optional?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably do, we'll see how we can improve these

Copy link
Contributor

@hannes-steffenhagen-diffblue hannes-steffenhagen-diffblue Feb 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I still think this would be a good idea but we can’t find it. There are the safe_ conversion functions, but those just assert that the conversion succeeds rather than returning an optionalt. Writing new converters on the other hand seems a bit out of scope for this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well stoul is safe: throwing exceptions on failure. May want to catch those.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use safe_string2size_t here, unless you actually want to throw an exception.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tautschnig Considering this is user input I think throwing an exception here would be appropriate. I don't think asserting that user input is always correct is appropriate.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xbauch @tautschnig I added #4192, which adds a more convenient way to convert strings to integers and handling failures locally.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's great, indeed we should not be failing an assertion when reporting invalid user input.

recursive_initialization->initialize(lhs, 0, block);
}

void function_call_harness_generatort::validate_options()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this one called anywhere?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called by the harness generator factory

void function_call_harness_generatort::implt::
add_harness_function_to_goto_model(code_blockt function_body)
{
const auto &function_to_call = symbol_table->lookup_ref(function);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use lookup_function_to_call?

Copy link
Contributor

@xbauch xbauch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@NlightNFotis NlightNFotis force-pushed the non-det-struct-new branch 2 times, most recently from cac8026 to 365a914 Compare February 14, 2019 12:05
Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚫
This PR failed Diffblue compatibility checks (cbmc commit: cac8026).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/100932721
Status will be re-evaluated on next push.
Please contact @peterschrammel, @thk123, or @allredj for support.

Common spurious failures:

  • the cbmc commit has disappeared in the mean time (e.g. in a force-push)
  • the author is not in the list of contributors (e.g. first-time contributors).

The incompatibility may have been introduced by an earlier PR. In that case merging this
PR should be avoided unless it fixes the current incompatibility.

Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️
Passed Diffblue compatibility checks (cbmc commit: 365a914).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/100935342

Copy link
Collaborator

@tautschnig tautschnig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we actually get rid of goto-instrument/nondet_static.cpp with this code? I think that code might be a bit cleaner when it comes to choosing what (not) to initialise, but isn't very useful when it comes to objects with pointers and such.

{
const auto &symbol = symbol_table_entry.second;
if(
symbol.is_static_lifetime && symbol.is_lvalue &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

Copy link
Contributor

@chrisr-diffblue chrisr-diffblue left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, though you may want to check Michael's comment about using safe_string2size_t

allocate_objects.allocate_automatic_local_object(type.subtype(), "pointee");
allocate_objects.declare_created_symbols(body);
body.add(code_assignt{lhs, null_pointer_exprt{type}});
if(depth < initialization_config.max_nondet_tree_depth)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the behavior compared to the existing c_nondet_symbol_factory(). Previously we would only stop initializing when a recursion on a struct was reached (i.e., on a pointer chain the same struct appeared a second time) and the depth was greater or equal to max_nondet_tree_depth. This was done via recording the struct tags in the object recursion_set. All pointer dereferences would increase the depth but only when we see a recursion we would stop initializing. Would it be possible to replicate this behavior here? The intention was to be consistent with how the initialization is done in the Java object factory (to facilitate future refactorings and code sharing between the two).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, I figured there was something we forgot! Good point, we will implement this.


code_function_callt::operandst arguments{};
arguments.reserve(parameters.size());
void function_call_harness_generatort::implt::generate_nondet_globals(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also --nondet-static (using nondet_static()) in cbmc and goto-instrument. I'm not sure how exactly it compares to this functionality but maybe the two could be merged at some point.

NlightNFotis and others added 4 commits February 15, 2019 14:36
Important refactoring to make implementation
of later features easier.

Co-authored-by: Fotis Koutoulakis <[email protected]>
Co-authored-by: Hannes Steffenhagen <[email protected]>
Add ability to non-deterministically initialise
globals in the function call harness generator.

Co-authored-by: Fotis Koutoulakis <[email protected]>
Co-authored-by: Hannes Steffenhagen <[email protected]>
Recursively initialise non-deterministically
struct components and pointers.

Co-authored-by: Fotis Koutoulakis <[email protected]>
Co-authored-by: Hannes Steffenhagen <[email protected]>
Now the depth limit only applies to structs if we have seen this struct
before (i.e. the initialisation chain is recursive)
Copy link
Contributor

@allredj allredj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✔️
Passed Diffblue compatibility checks (cbmc commit: 550e6ab).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/101132607

@NlightNFotis NlightNFotis merged commit 79ea945 into diffblue:develop Feb 18, 2019
@NlightNFotis NlightNFotis deleted the non-det-struct-new branch February 18, 2019 10:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants