Skip to content

Fix Java exception handlers #4499

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

Conversation

NathanJPhillips
Copy link
Contributor

@NathanJPhillips NathanJPhillips commented Apr 8, 2019

Fix Java exception handlers when multiple exception table rows begin at the same offset. Previously this could lead to throws within the catch block (e.g. a straightforward log-and-rethrow) being directed to the same catch block, introducing a spurious loop.

This is not a perfect solution, as documented in the comments, but is an improvement on the previous version, which was actually broken as demonstrated by the added tests. If it wasn't for the unwind limit the test case shown would cause Symex never to complete. An ideal solution would probably be to get rid of push-catch and pop-catch altogether and carry a GOTO exception table around with the GOTO program until exceptions are removed.

  • Each commit message has a non-empty body, explaining why the change was made.
  • N/A Methods or procedures I have added are documented, following the guidelines provided in CODING_STANDARD.md.
  • N/A 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.

@smowton
Copy link
Contributor

smowton commented Apr 8, 2019

@NathanJPhillips Added the first paragraph to the description

Copy link
Contributor

@smowton smowton left a comment

Choose a reason for hiding this comment

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

Some cleanups otherwise lgtm

@NathanJPhillips NathanJPhillips force-pushed the bugfix/exception-handlers branch 2 times, most recently from f51f997 to b76857c Compare April 8, 2019 17:41
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: b76857c).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/107511332
Status will be re-evaluated on next push.
Common spurious failures include: 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); compatibility was already broken by an earlier merge.

@NathanJPhillips
Copy link
Contributor Author

This PR failed Diffblue compatibility checks (cbmc commit: b76857c).

This was confirmed to be an erroneous warning.

Copy link
Contributor

@thk123 thk123 left a comment

Choose a reason for hiding this comment

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

I have some questions, I'm also gonna try it on TG just to be safe about the limitations

}
for(const auto &exceptions : exceptions_by_end)
{
code_push_catcht catch_push;
Copy link
Contributor

Choose a reason for hiding this comment

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

⛏️ Seems a bit back to front to make the code before making the list, would be nicer to make the execption list, and construct the code_push_catcht from it, but I see it has no constructor from exception list so out of scope of this PR to fix it

{
exception_list.emplace_back(
exception.catch_type.get_identifier(),
// Record the exception handler in the CATCH-PUSH instruction by
Copy link
Contributor

Choose a reason for hiding this comment

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

⛏️ Initially found the comment inside the brackets a little disorientating. You could introduce a local with the result of label with the comment above, if you agree.

method_offsett pos = 0;
method_offsett end_pc = 0;
while(pos < method.exception_table.size())
// For each try-catch add a CATCH-PUSH instruction
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Big block of code with a comment, perhaps pull out into a function?

if(working_set.empty())
{
// There was no throw in the try block
// TODO: This will result in non-balanced PUSH and POPs
Copy link
Contributor

Choose a reason for hiding this comment

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

❓ This seems a little scary... is this really not a regression? A knownbug test showing this limitation would be greatly appreciated.

Copy link
Contributor

Choose a reason for hiding this comment

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

❓ can we print a warning here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the current behaviour already. I suggested putting an assertion in but @smowton thought that this actually happens quite a lot and is relatively harmless. A warning seems like a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also it turns out my thinking was slightly wrong. We can't tell at this point whether balancing POPs will be introduced. I've updated the code and clarified the comments to make it clearer what's going on.

{
// This just fails if we have a back-edge here
// TODO: This will result in non-balanced PUSH and POPs
return code;
Copy link
Contributor

Choose a reason for hiding this comment

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

❓ Ditto here. Also might be a good idea to print some warning in this eventuallity?

@NathanJPhillips NathanJPhillips force-pushed the bugfix/exception-handlers branch from b76857c to ccc3550 Compare April 9, 2019 17:21
Copy link
Contributor

@thk123 thk123 left a comment

Choose a reason for hiding this comment

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

Thanks 👍 I ran the tests locally (and they passed) but messed up running on CI, feel free to merge without waiting if you want, but I'll kick of CI again if you don't mind waiting till tomorrow

}
return c;

if (result.size() != 1)
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems a little over the top just to avoid one redundant block, but as you like

if(exception.start_pc == cur_pc)
exceptions_by_end[exception.end_pc].push_back(exception);
}
// Loop over each block with a distinct end position
Copy link
Contributor

Choose a reason for hiding this comment

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

personally think this comment doesn't add clarity.

@NathanJPhillips NathanJPhillips force-pushed the bugfix/exception-handlers branch from ccc3550 to 02d5ad0 Compare April 10, 2019 08:06
@@ -1806,7 +1806,7 @@ code_blockt java_bytecode_convert_methodt::convert_instructions(
// (e.g. due to exceptional control flow)
if(!start_new_block)
start_new_block=address_pair.second.predecessors.size()>1;
// Start a new lexical block if we've just entered a try block
// Start a new lexical block if we've just entered a handler block
Copy link
Contributor

Choose a reason for hiding this comment

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

try was accurate, I'd interpret handler to mean a catch block, and the exception table's start_pc entry refers to the try

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought using the word "try" would imply the whole of a Java try block to someone who didn't know how exception tables worked.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, true enough -- in that case I think you should say it in full, "if we've just entered a block in which exceptions are handled. This is usually the start of a try block, but a single try can be represented as multiple non-contiguous blocks in the exception table."

7 15 15 Class MyException
0 6 18 Class java/lang/Exception
7 15 18 Class java/lang/Exception
This is not ordered in the sense that the two handlers for the block 0-6 are not next to each other.
Copy link
Member

Choose a reason for hiding this comment

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

Missing newline

Copy link
Contributor Author

@NathanJPhillips NathanJPhillips Apr 10, 2019

Choose a reason for hiding this comment

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

👍

cur_pc < exception_row.end_pc && !working_set.empty() &&
*working_set.begin() == exception_row.end_pc)
// Count the distinct start positions of handlers that end at this location
std::set<std::size_t> start_positions; // Use a set to deduplicate
Copy link
Member

Choose a reason for hiding this comment

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

Is the ordered set needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

unordered_set wouldn't perform well when storing a small set of integers without good spread like these.

@thk123
Copy link
Contributor

thk123 commented Apr 10, 2019

TG has revealed a potential issue with either synchronized blocks or nested exception catch blocks and this PR. Nathan is investigating at the moment.

@NathanJPhillips NathanJPhillips force-pushed the bugfix/exception-handlers branch from 02d5ad0 to 21bfdba Compare April 10, 2019 11:50
@NathanJPhillips
Copy link
Contributor Author

A change I made in response to a review comment introduced a bug that wasn't caught by current CBMC tests. I have added a test to catch this in a separate commit and revised the change to overcome the problem.

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: 21bfdba).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/107800935

@NathanJPhillips NathanJPhillips force-pushed the bugfix/exception-handlers branch from 21bfdba to 2cf31a9 Compare April 10, 2019 14:49
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: 2cf31a9).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/107823835

Make the control-flow in the code understandable
Fix bugs caused by handlers in the exception table not being sorted so that ones that start and end at the same positions are all next to each other
@NathanJPhillips NathanJPhillips force-pushed the bugfix/exception-handlers branch from 2cf31a9 to 79d5d61 Compare April 10, 2019 15:57
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: 79d5d61).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/107834277

@NathanJPhillips NathanJPhillips merged commit 097a58d into diffblue:develop Apr 11, 2019
@NathanJPhillips NathanJPhillips deleted the bugfix/exception-handlers branch April 11, 2019 08:21
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.

5 participants