mirror of
https://github.com/openjdk/jdk.git
synced 2026-05-08 20:49:44 +00:00
8364660: ClassVerifier::ends_in_athrow() should be removed
Reviewed-by: liach, dlong
This commit is contained in:
parent
784af438ef
commit
1bd814c3b2
@ -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).
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user