-
Notifications
You must be signed in to change notification settings - Fork 112
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
Don't silently swallow exceptions when verifying #488
Conversation
When verifying an implementation, set the initial value of the outcome to `Uninitialized` (a new alternative) instead of `Correct` to avoid spurious verification success when exceptions occur during solver interaction.
An alternative (or additional?) approach could be to address what I think is perhaps a separate part of the problem: boogie/Source/VCGeneration/SplitAndVerifyWorker.cs Lines 97 to 109 in da30caa
This releases the checker silently if any exception occurs during checking, without indicating that the proof obligation being checked has not yet been established. |
I'm not sure this is ready to go yet, but I'm adding reviewers in the hope of identifying the correct solution. |
The current fix now seems pretty reasonable to me, and it's very small. It does cause two current tests to fail because the expected error gets caught in a different place, leading to different output. |
@@ -1244,6 +1244,13 @@ private static void CleanupCheckers(string requestId) | |||
verificationResult.Errors = null; | |||
verificationResult.Outcome = VCGen.Outcome.Inconclusive; | |||
} | |||
catch(AggregateException e) |
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 catch something more specific? I wouldn't want to swallow any bugs here but catching a bad solver argument seems good.
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.
There's nothing below AggregateException
in the class hierarchy, AFAIK, though we could pull out the wrapped exceptions and behave differently depending on what they are. This code will always lead to a message and an inconclusive result, so I wouldn't expect it to hide bugs. It could be that, after some experience with it, we realize that it would be best to handle different exceptions differently, but at the moment I'm not sure what the different behavior would be.
At the moment, this does catch bad solver arguments and paths, but also things like a broken pipe if a solver crashes, or exceptions thrown by internal code that relates to solver interaction (like failing to parse a solver result).
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.
Inspecting the internals of AggregateException
and rethrowing if it doesn't contain something we want to handle, seems fine to me.
This code will always lead to a message and an inconclusive result, so I wouldn't expect it to hide bugs.
That's true but I do think it is nicer to handle IO and input errors differently than bugs. As a user I'd also appreciate bugs being identified as such so I know immediately that I'm not to blame.
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.
That sounds good to me. I'll update it to look inside the AggregateException
.
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.
One possibility is to only handle IOException
here. Solver configuration issues result in a ProverException
which is then handled higher up, meaning the output expected by the current solver configuration error tests doesn't change.
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 latest commit does just this, and I think I like the way it behaves now (as detailed in the response below).
@@ -1244,6 +1244,13 @@ private static void CleanupCheckers(string requestId) | |||
verificationResult.Errors = null; | |||
verificationResult.Outcome = VCGen.Outcome.Inconclusive; | |||
} | |||
catch(AggregateException e) |
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 catch something more specific? I wouldn't want to swallow any bugs here but catching a bad solver argument seems good.
printer.AdvisoryWriteLine("Advisory: {0} SKIPPED because: {1}", | ||
impl.Name, e.Message); | ||
verificationResult.Errors = null; | ||
verificationResult.Outcome = VCGen.Outcome.Inconclusive; |
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.
Could we set the default of verificationResult.Outcome to Inconclusive? Right now the default is Outcome.Correct, which seems like asking for soundness issues.
printer.AdvisoryWriteLine("Advisory: {0} SKIPPED because: {1}", | ||
impl.Name, e.Message); | ||
verificationResult.Errors = null; | ||
verificationResult.Outcome = VCGen.Outcome.Inconclusive; |
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.
Inconclusive means that the solver ran but couldn't prove unsat or sat right? For the scenario of invalid solver arguments I wouldn't mind an additional enum value, like InvalidConfiguration
.
@@ -105,6 +105,7 @@ async Task DoWork(Split split) | |||
} | |||
catch { | |||
split.ReleaseChecker(); | |||
throw; // This can be better handled farther up the call stack. |
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.
Nitpick: I don't think the comment is necessary.
@@ -1784,6 +1818,10 @@ private static ErrorInformation CreateErrorInformation(Counterexample error, VC. | |||
{ | |||
cause = "Out of memory on"; | |||
} | |||
else if (outcome == VCGen.Outcome.SolverException) | |||
{ | |||
cause = "Solver exception on"; |
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.
From a user perspective, I think I like to know "is it my fault?" and "solver exception" seems a bit ambiguous is that regard. It sounds like it's the solver's fault, but this problem also occurs when you give incorrect solver arguments right?
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.
That's right. It'll be hard to distinguish between an invalid solver and a crashing solver without digging into the text of the error message accompanying the exception, though. I think both will be instances of IOException
. That text will be presented to the user, though, so they'll be able to tell the difference, even though it'll be toward the end of the message rather than the beginning.
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.
With the most recent change, this is better behaved. An invalid solver path or option will lead to the old behavior: quitting immediately with a fatal error. A solver that starts up and then crashes will lead to the new behavior: a warning and a result of "solver exception", allowing the user to still get results for any proof obligation on which the solver succeeds.
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.
Looks great, thanks :-)
(Edited to address my own comment below).
Re-throw exceptions that occur during the split and verify worker, rather than silently swallowing them. Avoids printing "valid" when exceptions occur during solver interaction.
Addresses #487.