Skip to content
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

introduce 'fatal assertions' #8226

Merged
merged 1 commit into from
Apr 30, 2024
Merged

introduce 'fatal assertions' #8226

merged 1 commit into from
Apr 30, 2024

Conversation

kroening
Copy link
Member

@kroening kroening commented Mar 3, 2024

This introduces a variant of ASSERT instructions that are fatal when they are refuted. Execution paths through fatal assertions that are refuted are undefined. Assertions that (otherwise) pass and that are reachable from a refuted fatal assertion are now reported as UNKNOWN.

The motivating use-case for fatal assertions is undefined behavior in languages such as C/C++ or Rust.

  • 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).
  • n/a My commit message includes data points confirming performance improvements (if claimed).
  • My PR is restricted to a single feature or bugfix.
  • n/a White-space or formatting changes outside the feature-related changed lines are in commits of their own.

@kroening kroening force-pushed the fatal-assertions branch 3 times, most recently from fa9256f to 8f8e599 Compare March 3, 2024 23:32
Copy link

codecov bot commented Mar 4, 2024

Codecov Report

Attention: Patch coverage is 80.59701% with 13 lines in your changes are missing coverage. Please review.

Project coverage is 78.37%. Comparing base (41496b9) to head (3cdd832).

Files Patch % Lines
src/goto-checker/fatal_assertions.cpp 77.96% 13 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #8226      +/-   ##
===========================================
+ Coverage    77.96%   78.37%   +0.40%     
===========================================
  Files         1720     1721       +1     
  Lines       188982   188083     -899     
  Branches     18391    18452      +61     
===========================================
+ Hits        147347   147404      +57     
+ Misses       41635    40679     -956     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@kroening kroening force-pushed the fatal-assertions branch 5 times, most recently from 58bdc8c to 2a08484 Compare March 9, 2024 15:57
@peterschrammel
Copy link
Member

Is UNKNOWN the right status here? Or should there are rather be a new status UNDEFINED?

@kroening
Copy link
Member Author

kroening commented Mar 9, 2024

Is UNKNOWN the right status here? Or should there are rather be a new status UNDEFINED?

I did think about revising the status outputs to increase clarity. But that should happen independently, as a separate discussion.

An issue with "UNDEFINED" is that the word "undefined behavior" is very specific to C/C++, and we may wish our status codes to be more general. Also, it may mislead a reader to believe that there is undefined behavior at the given location. There may well be, but the point is that the undefined behavior is before that assertion in program order.

@zhassan-aws
Copy link
Collaborator

In Kani, we use UNDETERMINED.

@kroening kroening marked this pull request as ready for review March 9, 2024 19:55
@kroening
Copy link
Member Author

kroening commented Mar 9, 2024

I've pulled out the question of division-by-zero for floating-point numbers into a separate PR: #8233

@celinval
Copy link
Collaborator

Are fatal assertions encoded as assert + assume? How customizable will this new assertion be? Will users also be able to instrument their code with fatal assertions?

@kroening
Copy link
Member Author

Are fatal assertions encoded as assert + assume?

I am afraid that would yield the opposite result: you'd be more likely to get SUCCESS for the downstream properties, making the user believe that they are proven when really they are not.

How customizable will this new assertion be? Will users also be able to instrument their code with fatal assertions?

We could certainly consider a __CPROVER_fatal_assert, or the like.

@celinval
Copy link
Collaborator

Are fatal assertions encoded as assert + assume?

I am afraid that would yield the opposite result: you'd be more likely to get SUCCESS for the downstream properties, making the user believe that they are proven when really they are not.

In Kani, we over-approximate by marking all passing properties as undetermined.

tautschnig added a commit to tautschnig/cbmc that referenced this pull request Mar 15, 2024
With a new option --assert-then-assume each built-in check (assertion)
is followed by an assumption. For Kani, this will enable more consistent
behaviour, and it may give us an additional way to produce "fatal
assertions" as proposed in diffblue#8226.
@tautschnig
Copy link
Collaborator

Are fatal assertions encoded as assert + assume?

I am afraid that would yield the opposite result: you'd be more likely to get SUCCESS for the downstream properties, making the user believe that they are proven when really they are not.

In Kani, we over-approximate by marking all passing properties as undetermined.

#8242 now implements the assert + assume when users request this.

@kroening
Copy link
Member Author

This now marks refuted assertions after refuted fatal assertions as UNKNOWN.

@kroening kroening force-pushed the fatal-assertions branch 7 times, most recently from 4d88965 to f6e0d9f Compare March 24, 2024 21:19
@kroening
Copy link
Member Author

This now marks refuted assertions after refuted fatal assertions as UNKNOWN.

This comes at a price: to identify those assertions, the trace needs to be computed. That trace computation is broken in a number of cases, now marked as KNOWNBUG. Note that these aren't regressions caused by this PR, but just exposed by this PR.

We can try to fix these beforehand, and delay this PR. I believe that the step towards reporting sound results is very important, and that we should do it for 6.0.

@tautschnig
Copy link
Collaborator

This comes at a price: to identify those assertions, the trace needs to be computed. That trace computation is broken in a number of cases, now marked as KNOWNBUG. Note that these aren't regressions caused by this PR, but just exposed by this PR.

What kind of brokenness is this? I have previously only seen that computing traces can be very expensive (see #5714).

@kroening
Copy link
Member Author

What kind of brokenness is this? I have previously only seen that computing traces can be very expensive (see #5714).

It seems to be a miscellaneous set of issues that get triggered by calling get(...); some of them in the contract code, and also one in the interface to cprover_smt.

I didn't see a case of things getting very slow, but I'd say that's just as bad as throwing an error. This ought to be linear.

@kroening kroening force-pushed the fatal-assertions branch 2 times, most recently from 5cc78c4 to 0ad525c Compare April 3, 2024 20:01
@kroening kroening force-pushed the fatal-assertions branch 2 times, most recently from be78e8c to eb9ccc9 Compare April 19, 2024 15:16
@@ -7,4 +7,3 @@ user.json_symtab library.json_symtab
^\[2\] file library.adb line \d+ assertion: FAILURE$
^VERIFICATION FAILED$
--
error
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why did this line get removed?

Copy link
Member Author

Choose a reason for hiding this comment

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

The test triggered on "error trace", i.e., fails when --trace is added.

Copy link
Collaborator

Choose a reason for hiding this comment

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

But ... this PR no longer adds a need to compute traces?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but our future selves still want this. But could pull over into separate PR.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm sorry, I had to convince myself of the problem... Done and now PR'ed as #8270.

@@ -7,4 +7,3 @@ entry_point.json_symtab
^\[2\] file entry_point.adb line \d+ assertion: SUCCESS$
^VERIFICATION FAILED$
--
error
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here: Why did this line get removed?

Copy link
Collaborator

@remi-delmas-3000 remi-delmas-3000 left a comment

Choose a reason for hiding this comment

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

Maybe add tests with mutually recursive functions containing fatal assertions

if(!insertion_result.second)
continue; // seen already

if(loc.target->is_function_call())
Copy link
Collaborator

Choose a reason for hiding this comment

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

does this assume that function pointer calls have been eliminated ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes.


/// Proven assertions after refuted fatal assertions
/// are marked as UNKNOWN.
void propagate_fatal_to_proven(
Copy link
Collaborator

Choose a reason for hiding this comment

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

why keep refuted assertions after refuted fatal assertions intact ? The counter example could also be spurious or not worth investigating.

Copy link
Member Author

Choose a reason for hiding this comment

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

Note the discussion on the PR. The problem is that enabling the computation of counterexamples is expensive and doesn't work. Hence I've taken that out again.

{
// Iterate to find refuted fatal assertions. Anything reachalble
// from there is a 'fatal loc'.
loc_sett fatal_locs;
Copy link
Collaborator

@remi-delmas-3000 remi-delmas-3000 Apr 22, 2024

Choose a reason for hiding this comment

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

Is there a risk to flip the status of a fatal assertion from FAIL to UNKOWN if the goto model contains mutually recursive functions ? (a fatal assertion could appear to be after itself ?)

Copy link
Member Author

Choose a reason for hiding this comment

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

No, assertions that are failed simply stay failed.

Copy link
Collaborator

@remi-delmas-3000 remi-delmas-3000 left a comment

Choose a reason for hiding this comment

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

Is there a documentation update for fatal assertions ?

Are there ways to invoke CBMC which would result in the propagation not happening ?
i.e. is all_properties_verifier_with_trace_storaget the only verifier that users can invoke through the CLI ?

@kroening
Copy link
Member Author

Is there a documentation update for fatal assertions ?

This is a bigger job. It will need to be done eventually, but I'd do it as part of a bigger doc overhaul. It requires explaining what UB is, why it's bad, and why it's not locally contained.

Are there ways to invoke CBMC which would result in the propagation not happening ?

Not that I know of.

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.

Approving, but would appreciate if #8270 could be merged first to have a proper separation of concerns.

This introduces a variant of ASSERT instructions that are fatal when they
are refuted.  Execution paths through fatal assertions that are refuted are
undefined.  Assertions that (otherwise) pass and that are reachable from a
refuted fatal assertion are now reported as UNKNOWN.

The motivating use-case for fatal assertions is undefined behavior in
languages such as C/C++ or Rust.

Noi attempt is made to modify the outcome of ASSERT instructions that are
refuted owing to a trace through a refuted fatal assertion.  The computation
of the trace is not robust enough.
@kroening kroening enabled auto-merge April 30, 2024 11:49
@kroening kroening merged commit 69169d1 into develop Apr 30, 2024
41 checks passed
@tautschnig tautschnig deleted the fatal-assertions branch April 30, 2024 13:26
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.

6 participants