8364660: ClassVerifier::ends_in_athrow() should be removed

Reviewed-by: liach, dlong
This commit is contained in:
Matias Saavedra Silva 2025-10-15 14:34:38 +00:00
parent 784af438ef
commit 1bd814c3b2
3 changed files with 1 additions and 251 deletions

View File

@ -2442,209 +2442,6 @@ void ClassVerifier::verify_field_instructions(RawBytecodeStream* bcs,
}
}
// Look at the method's handlers. If the bci is in the handler's try block
// then check if the handler_pc is already on the stack. If not, push it
// unless the handler has already been scanned.
void ClassVerifier::push_handlers(ExceptionTable* exhandlers,
GrowableArray<u4>* handler_list,
GrowableArray<u4>* handler_stack,
u4 bci) {
int exlength = exhandlers->length();
for(int x = 0; x < exlength; x++) {
if (bci >= exhandlers->start_pc(x) && bci < exhandlers->end_pc(x)) {
u4 exhandler_pc = exhandlers->handler_pc(x);
if (!handler_list->contains(exhandler_pc)) {
handler_stack->append_if_missing(exhandler_pc);
handler_list->append(exhandler_pc);
}
}
}
}
// Return TRUE if all code paths starting with start_bc_offset end in
// bytecode athrow or loop.
bool ClassVerifier::ends_in_athrow(u4 start_bc_offset) {
ResourceMark rm;
// Create bytecode stream.
RawBytecodeStream bcs(method());
int code_length = method()->code_size();
bcs.set_start(start_bc_offset);
// Create stack for storing bytecode start offsets for if* and *switch.
GrowableArray<u4>* bci_stack = new GrowableArray<u4>(30);
// Create stack for handlers for try blocks containing this handler.
GrowableArray<u4>* handler_stack = new GrowableArray<u4>(30);
// Create list of handlers that have been pushed onto the handler_stack
// so that handlers embedded inside of their own TRY blocks only get
// scanned once.
GrowableArray<u4>* handler_list = new GrowableArray<u4>(30);
// Create list of visited branch opcodes (goto* and if*).
GrowableArray<u4>* visited_branches = new GrowableArray<u4>(30);
ExceptionTable exhandlers(_method());
while (true) {
if (bcs.is_last_bytecode()) {
// if no more starting offsets to parse or if at the end of the
// method then return false.
if ((bci_stack->is_empty()) || (bcs.end_bci() == code_length))
return false;
// Pop a bytecode starting offset and scan from there.
bcs.set_start(bci_stack->pop());
}
Bytecodes::Code opcode = bcs.raw_next();
int bci = bcs.bci();
// If the bytecode is in a TRY block, push its handlers so they
// will get parsed.
push_handlers(&exhandlers, handler_list, handler_stack, bci);
switch (opcode) {
case Bytecodes::_if_icmpeq:
case Bytecodes::_if_icmpne:
case Bytecodes::_if_icmplt:
case Bytecodes::_if_icmpge:
case Bytecodes::_if_icmpgt:
case Bytecodes::_if_icmple:
case Bytecodes::_ifeq:
case Bytecodes::_ifne:
case Bytecodes::_iflt:
case Bytecodes::_ifge:
case Bytecodes::_ifgt:
case Bytecodes::_ifle:
case Bytecodes::_if_acmpeq:
case Bytecodes::_if_acmpne:
case Bytecodes::_ifnull:
case Bytecodes::_ifnonnull: {
int target = bcs.dest();
if (visited_branches->contains(bci)) {
if (bci_stack->is_empty()) {
if (handler_stack->is_empty()) {
return true;
} else {
// Parse the catch handlers for try blocks containing athrow.
bcs.set_start(handler_stack->pop());
}
} else {
// Pop a bytecode starting offset and scan from there.
bcs.set_start(bci_stack->pop());
}
} else {
if (target > bci) { // forward branch
if (target >= code_length) return false;
// Push the branch target onto the stack.
bci_stack->push(target);
// then, scan bytecodes starting with next.
bcs.set_start(bcs.next_bci());
} else { // backward branch
// Push bytecode offset following backward branch onto the stack.
bci_stack->push(bcs.next_bci());
// Check bytecodes starting with branch target.
bcs.set_start(target);
}
// Record target so we don't branch here again.
visited_branches->append(bci);
}
break;
}
case Bytecodes::_goto:
case Bytecodes::_goto_w: {
int target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w());
if (visited_branches->contains(bci)) {
if (bci_stack->is_empty()) {
if (handler_stack->is_empty()) {
return true;
} else {
// Parse the catch handlers for try blocks containing athrow.
bcs.set_start(handler_stack->pop());
}
} else {
// Been here before, pop new starting offset from stack.
bcs.set_start(bci_stack->pop());
}
} else {
if (target >= code_length) return false;
// Continue scanning from the target onward.
bcs.set_start(target);
// Record target so we don't branch here again.
visited_branches->append(bci);
}
break;
}
// Check that all switch alternatives end in 'athrow' bytecodes. Since it
// is difficult to determine where each switch alternative ends, parse
// each switch alternative until either hit a 'return', 'athrow', or reach
// the end of the method's bytecodes. This is gross but should be okay
// because:
// 1. tableswitch and lookupswitch byte codes in handlers for ctor explicit
// constructor invocations should be rare.
// 2. if each switch alternative ends in an athrow then the parsing should be
// short. If there is no athrow then it is bogus code, anyway.
case Bytecodes::_lookupswitch:
case Bytecodes::_tableswitch:
{
address aligned_bcp = align_up(bcs.bcp() + 1, jintSize);
int default_offset = Bytes::get_Java_u4(aligned_bcp) + bci;
int keys, delta;
if (opcode == Bytecodes::_tableswitch) {
jint low = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize);
jint high = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize);
// This is invalid, but let the regular bytecode verifier
// report this because the user will get a better error message.
if (low > high) return true;
keys = high - low + 1;
delta = 1;
} else {
keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize);
delta = 2;
}
// Invalid, let the regular bytecode verifier deal with it.
if (keys < 0) return true;
// Push the offset of the next bytecode onto the stack.
bci_stack->push(bcs.next_bci());
// Push the switch alternatives onto the stack.
for (int i = 0; i < keys; i++) {
int target = bci + (jint)Bytes::get_Java_u4(aligned_bcp+(3+i*delta)*jintSize);
if (target > code_length) return false;
bci_stack->push(target);
}
// Start bytecode parsing for the switch at the default alternative.
if (default_offset > code_length) return false;
bcs.set_start(default_offset);
break;
}
case Bytecodes::_return:
return false;
case Bytecodes::_athrow:
{
if (bci_stack->is_empty()) {
if (handler_stack->is_empty()) {
return true;
} else {
// Parse the catch handlers for try blocks containing athrow.
bcs.set_start(handler_stack->pop());
}
} else {
// Pop a bytecode offset and starting scanning from there.
bcs.set_start(bci_stack->pop());
}
}
break;
default:
;
} // end switch
} // end while loop
return false;
}
void ClassVerifier::verify_invoke_init(
RawBytecodeStream* bcs, u2 ref_class_index, VerificationType ref_class_type,
StackMapFrame* current_frame, u4 code_length, bool in_try_block,
@ -2669,25 +2466,6 @@ void ClassVerifier::verify_invoke_init(
// sure that all catch clause paths end in a throw. Otherwise, this can
// result in returning an incomplete object.
if (in_try_block) {
ExceptionTable exhandlers(_method());
int exlength = exhandlers.length();
for(int i = 0; i < exlength; i++) {
u2 start_pc = exhandlers.start_pc(i);
u2 end_pc = exhandlers.end_pc(i);
if (bci >= start_pc && bci < end_pc) {
if (!ends_in_athrow(exhandlers.handler_pc(i))) {
verify_error(ErrorContext::bad_code(bci),
"Bad <init> method call from after the start of a try block");
return;
} else if (log_is_enabled(Debug, verification)) {
ResourceMark rm(THREAD);
log_debug(verification)("Survived call to ends_in_athrow(): %s",
current_class()->name()->as_C_string());
}
}
}
// Check the exception handler target stackmaps with the locals from the
// incoming stackmap (before initialize_object() changes them to outgoing
// state).

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -334,17 +334,6 @@ class ClassVerifier : public StackObj {
bool* this_uninit, const constantPoolHandle& cp, StackMapTable* stackmap_table,
TRAPS);
// Used by ends_in_athrow() to push all handlers that contain bci onto the
// handler_stack, if the handler has not already been pushed on the stack.
void push_handlers(ExceptionTable* exhandlers,
GrowableArray<u4>* handler_list,
GrowableArray<u4>* handler_stack,
u4 bci);
// Returns true if all paths starting with start_bc_offset end in athrow
// bytecode or loop.
bool ends_in_athrow(u4 start_bc_offset);
void verify_invoke_instructions(
RawBytecodeStream* bcs, u4 code_length, StackMapFrame* current_frame,
bool in_try_block, bool* this_uninit, VerificationType return_type,

View File

@ -1513,13 +1513,6 @@ public final class VerifierImpl {
}
}
// Return TRUE if all code paths starting with start_bc_offset end in
// bytecode athrow or loop.
boolean ends_in_athrow(int start_bc_offset) {
log_info("unimplemented VerifierImpl.ends_in_athrow");
return true;
}
boolean verify_invoke_init(RawBytecodeHelper bcs, int ref_class_index, VerificationType ref_class_type,
VerificationFrame current_frame, int code_length, boolean in_try_block,
boolean this_uninit, ConstantPoolWrapper cp, VerificationTable stackmap_table) {
@ -1532,16 +1525,6 @@ public final class VerifierImpl {
verifyError("Bad <init> method call");
}
if (in_try_block) {
for(var exhandler : _method.exceptionTable()) {
int start_pc = exhandler[0];
int end_pc = exhandler[1];
if (bci >= start_pc && bci < end_pc) {
if (!ends_in_athrow(exhandler[2])) {
verifyError("Bad <init> method call from after the start of a try block");
}
}
}
verify_exception_handler_targets(bci, true, current_frame, stackmap_table);
}
current_frame.initialize_object(type, current_type());