diff --git a/.hgtags b/.hgtags index 38fe64868aaaf3167e2cc0d6c82a1af9c58d061a..b109a4ee922ab26244a2ce185afdcaebbcbbfa9d 100644 --- a/.hgtags +++ b/.hgtags @@ -515,4 +515,5 @@ f3f50c4f9ea5d3af40cb794b6f3f2a337c8873db jdk8u25-b08 9e2bb00a81910776d5b16c49a3f4c5264ceab522 jdk8u25-b11 2993491d47df8c4b096ea7fa534162bde8b53dcf jdk8u25-b12 ca6d25be853b5c428c6228871316671843264666 jdk8u25-b13 +c77d5db189422e2eef0443ee212644e497113b18 jdk8u25-b14 5bb683bbe2c74876d585b5c3232fc3aab7b23e97 jdk8u31-b00 diff --git a/src/share/vm/classfile/stackMapFrame.cpp b/src/share/vm/classfile/stackMapFrame.cpp index a06ef2c662cb2a74d496bb4562f1a5c319e2cf01..1332a081c76365383fe65a7f3f779179776b7141 100644 --- a/src/share/vm/classfile/stackMapFrame.cpp +++ b/src/share/vm/classfile/stackMapFrame.cpp @@ -54,21 +54,6 @@ StackMapFrame* StackMapFrame::frame_in_exception_handler(u1 flags) { return frame; } -bool StackMapFrame::has_new_object() const { - int32_t i; - for (i = 0; i < _max_locals; i++) { - if (_locals[i].is_uninitialized()) { - return true; - } - } - for (i = 0; i < _stack_size; i++) { - if (_stack[i].is_uninitialized()) { - return true; - } - } - return false; -} - void StackMapFrame::initialize_object( VerificationType old_object, VerificationType new_object) { int32_t i; diff --git a/src/share/vm/classfile/stackMapFrame.hpp b/src/share/vm/classfile/stackMapFrame.hpp index 52e1558b85599bae4a070497685a657c8d7dd4e4..24cbae330bbfcc6becc872d34be4106e74cc1194 100644 --- a/src/share/vm/classfile/stackMapFrame.hpp +++ b/src/share/vm/classfile/stackMapFrame.hpp @@ -154,10 +154,6 @@ class StackMapFrame : public ResourceObj { VerificationType set_locals_from_arg( const methodHandle m, VerificationType thisKlass, TRAPS); - // Search local variable type array and stack type array. - // Return true if an uninitialized object is found. - bool has_new_object() const; - // Search local variable type array and stack type array. // Set every element with type of old_object to new_object. void initialize_object( diff --git a/src/share/vm/classfile/stackMapTable.cpp b/src/share/vm/classfile/stackMapTable.cpp index de8dc9b00a5a43482be0507c4507ef220d50aa36..f74adbe31724e2f35b9f9400a8a39585ece905aa 100644 --- a/src/share/vm/classfile/stackMapTable.cpp +++ b/src/share/vm/classfile/stackMapTable.cpp @@ -130,19 +130,6 @@ void StackMapTable::check_jump_target( if (!match || (target < 0 || target >= _code_length)) { frame->verifier()->verify_error(ctx, "Inconsistent stackmap frames at branch target %d", target); - return; - } - // check if uninitialized objects exist on backward branches - check_new_object(frame, target, CHECK_VERIFY(frame->verifier())); -} - -void StackMapTable::check_new_object( - const StackMapFrame* frame, int32_t target, TRAPS) const { - if (frame->offset() > target && frame->has_new_object()) { - frame->verifier()->verify_error( - ErrorContext::bad_code(frame->offset()), - "Uninitialized object exists on backward branch %d", target); - return; } } diff --git a/src/share/vm/classfile/stackMapTable.hpp b/src/share/vm/classfile/stackMapTable.hpp index 184b1ebfa9ceb9cf2de090a7f6aed02f407ca327..5354a0b886731a4f3ab55da81eda27ce5a3271a8 100644 --- a/src/share/vm/classfile/stackMapTable.hpp +++ b/src/share/vm/classfile/stackMapTable.hpp @@ -90,10 +90,6 @@ class StackMapTable : public StackObj { // Returns the frame array index where the frame with offset is stored. int get_index_from_offset(int32_t offset) const; - // Make sure that there's no uninitialized object exist on backward branch. - void check_new_object( - const StackMapFrame* frame, int32_t target, TRAPS) const; - void print_on(outputStream* str) const; }; diff --git a/src/share/vm/classfile/verifier.cpp b/src/share/vm/classfile/verifier.cpp index 227768364226184ea4692bc7893ec3bda21656c3..bc8b7d0ae113bcb0078e5fe94f5442d14947d0f9 100644 --- a/src/share/vm/classfile/verifier.cpp +++ b/src/share/vm/classfile/verifier.cpp @@ -2231,6 +2231,181 @@ 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. +void ClassVerifier::push_handlers(ExceptionTable* exhandlers, + GrowableArray* 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)) { + handler_stack->append_if_missing(exhandlers->handler_pc(x)); + } + } +} + +// 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()); + u4 code_length = method()->code_size(); + bcs.set_start(start_bc_offset); + u4 target; + // Create stack for storing bytecode start offsets for if* and *switch. + GrowableArray* bci_stack = new GrowableArray(30); + // Create stack for handlers for try blocks containing this handler. + GrowableArray* handler_stack = new GrowableArray(30); + // Create list of visited branch opcodes (goto* and if*). + GrowableArray* visited_branches = new GrowableArray(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()) || ((u4)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(); + u4 bci = bcs.bci(); + + // If the bytecode is in a TRY block, push its handlers so they + // will get parsed. + push_handlers(&exhandlers, 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: + target = bcs.dest(); + if (visited_branches->contains(bci)) { + if (bci_stack->is_empty()) return true; + // 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: + target = (opcode == Bytecodes::_goto ? bcs.dest() : bcs.dest_w()); + if (visited_branches->contains(bci)) { + if (bci_stack->is_empty()) return true; + // 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 = (address) round_to((intptr_t)(bcs.bcp() + 1), jintSize); + u4 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++) { + u4 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 *this_uninit, @@ -2250,18 +2425,26 @@ void ClassVerifier::verify_invoke_init( return; } - // Make sure that this call is not done from within a TRY block because - // that can result in returning an incomplete object. Simply checking - // (bci >= start_pc) also ensures that this call is not done after a TRY - // block. That is also illegal because this call must be the first Java - // statement in the constructor. + // Check if this call is done from inside of a TRY block. If so, make + // sure that all catch clause paths end in a throw. Otherwise, this + // can result in returning an incomplete object. ExceptionTable exhandlers(_method()); int exlength = exhandlers.length(); for(int i = 0; i < exlength; i++) { - if (bci >= exhandlers.start_pc(i)) { - verify_error(ErrorContext::bad_code(bci), - "Bad method call from after the start of a try block"); - return; + 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 method call from after the start of a try block"); + return; + } else if (VerboseVerification) { + ResourceMark rm; + tty->print_cr( + "Survived call to ends_in_athrow(): %s", + current_class()->name()->as_C_string()); + } } } diff --git a/src/share/vm/classfile/verifier.hpp b/src/share/vm/classfile/verifier.hpp index 61844602e55ba5891db0eef68bc8b22428f6cda8..4c8b5895a217adf75471b0697c06798ea676918a 100644 --- a/src/share/vm/classfile/verifier.hpp +++ b/src/share/vm/classfile/verifier.hpp @@ -30,6 +30,7 @@ #include "oops/klass.hpp" #include "oops/method.hpp" #include "runtime/handles.hpp" +#include "utilities/growableArray.hpp" #include "utilities/exceptions.hpp" // The verifier class @@ -303,6 +304,16 @@ class ClassVerifier : public StackObj { StackMapFrame* current_frame, u4 code_length, bool* this_uninit, constantPoolHandle cp, TRAPS); + // Used by ends_in_athrow() to push all handlers that contain bci onto + // the handler_stack, if the handler is not already on the stack. + void push_handlers(ExceptionTable* exhandlers, + GrowableArray* 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* this_uninit, VerificationType return_type, diff --git a/test/runtime/7116786/Test7116786.java b/test/runtime/7116786/Test7116786.java index b914019258ed387787f82b199b65f46a5f07548a..48eaf440b86110f7214c86f21885c4f246e20e69 100644 --- a/test/runtime/7116786/Test7116786.java +++ b/test/runtime/7116786/Test7116786.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2014, 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 @@ -147,7 +147,8 @@ class VerifyErrorCases { "no stackmap frame at jump location or bad jump", "Inconsistent stackmap frames at branch target "), - new Case("case15", "stackMapTable.cpp", true, "check_new_object", + /* Backward jump with uninit is allowed starting with JDK 8 */ + new Case("case15", "stackMapTable.cpp", false, "check_new_object", "backward jump with uninit", "Uninitialized object exists on backward branch "),