Skip to content

Commit 02d5ad0

Browse files
Rewrite do_exception_handling
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
1 parent 3a116c3 commit 02d5ad0

File tree

2 files changed

+78
-93
lines changed

2 files changed

+78
-93
lines changed

jbmc/regression/jbmc/exceptions28/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
KNOWNBUG
1+
CORE
22
test.class
33
--unwind 10
44
^\[java::test.main:\(\[Ljava/lang/String;\)V\.1\] line 7 no uncaught exception: FAILURE$

jbmc/src/java_bytecode/java_bytecode_convert_method.cpp

Lines changed: 77 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1806,7 +1806,7 @@ code_blockt java_bytecode_convert_methodt::convert_instructions(
18061806
// (e.g. due to exceptional control flow)
18071807
if(!start_new_block)
18081808
start_new_block=address_pair.second.predecessors.size()>1;
1809-
// Start a new lexical block if we've just entered a try block
1809+
// Start a new lexical block if we've just entered a handler block
18101810
if(!start_new_block && has_seen_previous_address)
18111811
{
18121812
for(const auto &exception_row : method.exception_table)
@@ -2335,105 +2335,90 @@ codet &java_bytecode_convert_methodt::do_exception_handling(
23352335
const java_bytecode_convert_methodt::methodt &method,
23362336
const std::set<method_offsett> &working_set,
23372337
method_offsett cur_pc,
2338-
codet &c)
2338+
codet &code)
23392339
{
2340-
std::vector<irep_idt> exception_ids;
2341-
std::vector<irep_idt> handler_labels;
2342-
2343-
// for each try-catch add a CATCH-PUSH instruction
2344-
// each CATCH-PUSH records a list of all the handler labels
2345-
// together with a list of all the exception ids
2346-
2347-
// be aware of different try-catch blocks with the same starting pc
2348-
method_offsett pos = 0;
2349-
method_offsett end_pc = 0;
2350-
while(pos < method.exception_table.size())
2340+
// For each exception handler range that starts here add a CATCH-PUSH marker
2341+
// Each CATCH-PUSH records a list of all the exception id and handler label
2342+
// pairs handled for its exact block
2343+
2344+
std::vector<codet> result; // The instructions to replace code with
2345+
2346+
// Gather all try-catch blocks that have cur_pc as the starting pc
2347+
typedef std::vector<std::reference_wrapper<
2348+
const java_bytecode_convert_methodt::methodt::exceptiont>> exceptionst;
2349+
std::map<std::size_t, exceptionst> exceptions_by_end;
2350+
for(const java_bytecode_convert_methodt::methodt::exceptiont &exception
2351+
: method.exception_table)
23512352
{
2352-
// check if this is the beginning of a try block
2353-
for(; pos < method.exception_table.size(); ++pos)
2354-
{
2355-
// unexplored try-catch?
2356-
if(cur_pc == method.exception_table[pos].start_pc && end_pc == 0)
2357-
{
2358-
end_pc = method.exception_table[pos].end_pc;
2359-
}
2360-
2361-
// currently explored try-catch?
2362-
if(
2363-
cur_pc == method.exception_table[pos].start_pc &&
2364-
method.exception_table[pos].end_pc == end_pc)
2365-
{
2366-
exception_ids.push_back(
2367-
method.exception_table[pos].catch_type.get_identifier());
2368-
// record the exception handler in the CATCH-PUSH
2369-
// instruction by generating a label corresponding to
2370-
// the handler's pc
2371-
handler_labels.push_back(
2372-
label(std::to_string(method.exception_table[pos].handler_pc)));
2373-
}
2374-
else
2375-
break;
2376-
}
2377-
2378-
// add the actual PUSH-CATCH instruction
2379-
if(!exception_ids.empty())
2380-
{
2381-
code_push_catcht catch_push;
2382-
INVARIANT(
2383-
exception_ids.size() == handler_labels.size(),
2384-
"Exception tags and handler labels should be 1-to-1");
2385-
code_push_catcht::exception_listt &exception_list =
2386-
catch_push.exception_list();
2387-
for(size_t i = 0; i < exception_ids.size(); ++i)
2388-
{
2389-
exception_list.push_back(
2390-
code_push_catcht::exception_list_entryt(
2391-
exception_ids[i], handler_labels[i]));
2392-
}
2393-
2394-
c = code_blockt({catch_push, c});
2395-
}
2396-
else
2397-
{
2398-
// advance
2399-
++pos;
2400-
}
2401-
2402-
// reset
2403-
end_pc = 0;
2404-
exception_ids.clear();
2405-
handler_labels.clear();
2353+
if(exception.start_pc == cur_pc)
2354+
exceptions_by_end[exception.end_pc].push_back(exception);
2355+
}
2356+
for(const auto &exceptions : exceptions_by_end)
2357+
{
2358+
// For each block with a distinct end position create one CATCH-PUSH
2359+
code_push_catcht catch_push;
2360+
// Fill in its exception_list
2361+
code_push_catcht::exception_listt &exception_list =
2362+
catch_push.exception_list();
2363+
for(const java_bytecode_convert_methodt::methodt::exceptiont &exception
2364+
: exceptions.second)
2365+
{
2366+
exception_list.emplace_back(
2367+
exception.catch_type.get_identifier(),
2368+
// Record the exception handler in the CATCH-PUSH instruction by
2369+
// generating a label corresponding to the handler's pc
2370+
label(std::to_string(exception.handler_pc)));
2371+
}
2372+
// Prepend the CATCH-PUSH instruction
2373+
result.push_back(std::move(catch_push));
24062374
}
24072375

2408-
// next add the CATCH-POP instructions
2409-
size_t start_pc = 0;
2410-
// as the first try block may have start_pc 0, we
2411-
// must track it separately
2412-
bool first_handler = false;
2413-
// check if this is the end of a try block
2414-
for(const auto &exception_row : method.exception_table)
2376+
// Move code to result - we can move it back later if we don't need to create
2377+
// a new block
2378+
result.push_back(std::move(code));
2379+
2380+
// Next add the CATCH-POP instructions
2381+
// exception_row.end_pc is exclusive so append a CATCH-POP instruction if
2382+
// this is the instruction before it.
2383+
// To do this, attempt to find all exception handlers that end at the
2384+
// earliest known instruction after this one.
2385+
2386+
// Dangerously, we assume that the next opcode in the bytecode after the end
2387+
// of the exception handler block (whose address matches the exclusive end
2388+
// position of the block) will be a successor of some code investigated
2389+
// before the instruction at the end of that handler and therefore in the
2390+
// working set.
2391+
// As an example of where this may fail, for non-obfuscated bytecode
2392+
// generated by most compilers the next opcode after the block ending at the
2393+
// end of the try block is the lexically next bit of code after the try
2394+
// block, i.e. the catch block. When there aren't any throwing statements in
2395+
// the try block this block will not be the successor of any instruction.
2396+
2397+
auto next_opcode_it = std::find_if(
2398+
working_set.begin(),
2399+
working_set.end(),
2400+
[cur_pc](method_offsett offset) { return offset > cur_pc; });
2401+
if(next_opcode_it != working_set.end())
24152402
{
2416-
// add the CATCH-POP before the end of the try block
2417-
if(
2418-
cur_pc < exception_row.end_pc && !working_set.empty() &&
2419-
*working_set.begin() == exception_row.end_pc)
2403+
// Count the distinct start positions of handlers that end at this location
2404+
std::set<std::size_t> start_positions; // Use a set to deduplicate
2405+
for(const auto &exception_row : method.exception_table)
24202406
{
2421-
// have we already added a CATCH-POP for the current try-catch?
2422-
// (each row corresponds to a handler)
2423-
if(exception_row.start_pc != start_pc || !first_handler)
2424-
{
2425-
if(!first_handler)
2426-
first_handler = true;
2427-
// remember the beginning of the try-catch so that we don't add
2428-
// another CATCH-POP for the same try-catch
2429-
start_pc = exception_row.start_pc;
2430-
// add CATCH_POP instruction
2431-
code_pop_catcht catch_pop;
2432-
c = code_blockt({c, catch_pop});
2433-
}
2407+
// Check if the next instruction found is the (exclusive) end of a block
2408+
if(*next_opcode_it == exception_row.end_pc)
2409+
start_positions.insert(exception_row.start_pc);
24342410
}
2411+
for(std::size_t handler = 0; handler < start_positions.size(); ++handler)
2412+
// Append a CATCH-POP instruction before the end of the block
2413+
result.push_back(code_pop_catcht());
24352414
}
2436-
return c;
2415+
2416+
if(result.size() != 1)
2417+
code = code_blockt(std::move(result));
2418+
else
2419+
// Move original codet back to code
2420+
code = std::move(result[0]);
2421+
return code;
24372422
}
24382423

24392424
code_blockt java_bytecode_convert_methodt::convert_multianewarray(

0 commit comments

Comments
 (0)