-
Notifications
You must be signed in to change notification settings - Fork 277
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
Fix Java exception handlers #4499
Conversation
@NathanJPhillips Added the first paragraph to the description |
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.
Some cleanups otherwise lgtm
f51f997
to
b76857c
Compare
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 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.
This was confirmed to be an erroneous warning. |
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 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; |
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.
⛏️ 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 |
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.
⛏️ 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 |
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.
💡 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 |
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 a little scary... is this really not a regression? A knownbug test showing this limitation would be greatly appreciated.
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 print a warning here
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 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.
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.
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; |
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.
❓ Ditto here. Also might be a good idea to print some warning in this eventuallity?
b76857c
to
ccc3550
Compare
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.
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) |
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.
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 |
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.
personally think this comment doesn't add clarity.
ccc3550
to
02d5ad0
Compare
@@ -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 |
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.
try
was accurate, I'd interpret handler
to mean a catch block, and the exception table's start_pc
entry refers to the try
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 thought using the word "try" would imply the whole of a Java try
block to someone who didn't know how exception tables worked.
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.
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. |
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.
Missing newline
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.
👍
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 |
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.
Is the ordered set needed here?
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.
unordered_set
wouldn't perform well when storing a small set of integers without good spread like these.
TG has revealed a potential issue with either synchronized blocks or nested exception catch blocks and this PR. Nathan is investigating at the moment. |
02d5ad0
to
21bfdba
Compare
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. |
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.
✔️
Passed Diffblue compatibility checks (cbmc commit: 21bfdba).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/107800935
21bfdba
to
2cf31a9
Compare
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.
✔️
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
2cf31a9
to
79d5d61
Compare
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.
✔️
Passed Diffblue compatibility checks (cbmc commit: 79d5d61).
Build URL: https://travis-ci.com/diffblue/test-gen/builds/107834277
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.