Skip to content

Commit ccc3550

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 ccc3550

File tree

2 files changed

+78
-92
lines changed

2 files changed

+78
-92
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 & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2335,105 +2335,91 @@ 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+
// Loop over each block with a distinct end position
2357+
for(const auto &exceptions : exceptions_by_end)
2358+
{
2359+
// For each block create a CATCH-PUSH
2360+
code_push_catcht catch_push;
2361+
// Fill in its exception_list
2362+
code_push_catcht::exception_listt &exception_list =
2363+
catch_push.exception_list();
2364+
for(const java_bytecode_convert_methodt::methodt::exceptiont &exception
2365+
: exceptions.second)
2366+
{
2367+
exception_list.emplace_back(
2368+
exception.catch_type.get_identifier(),
2369+
// Record the exception handler in the CATCH-PUSH instruction by
2370+
// generating a label corresponding to the handler's pc
2371+
label(std::to_string(exception.handler_pc)));
2372+
}
2373+
// Prepend the CATCH-PUSH instruction
2374+
result.push_back(std::move(catch_push));
24062375
}
24072376

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)
2377+
// Move code to result - we'll move it back later if we don't need to create
2378+
// a new block
2379+
result.push_back(std::move(code));
2380+
2381+
// Next add the CATCH-POP instructions
2382+
// exception_row.end_pc is exclusive so append a CATCH-POP instruction if
2383+
// this is the instruction before it.
2384+
// To do this, attempt to find all exception handlers that end at the
2385+
// earliest known instruction after this one.
2386+
2387+
// Dangerously, we assume that the next opcode in the bytecode after the end
2388+
// of the exception handler block (whose address matches the exclusive end
2389+
// position of the block) will be a successor of some code investigated
2390+
// before the instruction at the end of that handler and therefore in the
2391+
// working set.
2392+
// As an example of where this may fail, for non-obfuscated bytecode
2393+
// generated by most compilers the next opcode after the block ending at the
2394+
// end of the try block is the lexically next bit of code after the try
2395+
// block, i.e. the catch block. When there aren't any throwing statements in
2396+
// the try block this block will not be the successor of any instruction.
2397+
2398+
auto next_opcode_it = std::find_if(
2399+
working_set.begin(),
2400+
working_set.end(),
2401+
[cur_pc](method_offsett offset) { return offset > cur_pc; });
2402+
if(next_opcode_it != working_set.end())
24152403
{
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)
2404+
// Count the distinct handler start positions that end at this location
2405+
std::set<std::size_t> start_positions; // Use a set to deduplicate
2406+
for(const auto &exception_row : method.exception_table)
24202407
{
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-
}
2408+
// Check if the next instruction found is the (exclusive) end of a try block
2409+
if(*next_opcode_it == exception_row.end_pc)
2410+
start_positions.insert(exception_row.start_pc);
24342411
}
2412+
for(std::size_t handler = 0; handler < start_positions.size(); ++handler)
2413+
// Append a CATCH-POP instruction before the end of the block
2414+
result.push_back(code_pop_catcht());
24352415
}
2436-
return c;
2416+
2417+
if (result.size() != 1)
2418+
code = code_blockt(std::move(result));
2419+
else
2420+
// Move original codet back to code
2421+
code = std::move(result[0]);
2422+
return code;
24372423
}
24382424

24392425
code_blockt java_bytecode_convert_methodt::convert_multianewarray(

0 commit comments

Comments
 (0)