From 72853c670c97aae4eab64a5e9edb3c7176beaf6a Mon Sep 17 00:00:00 2001 Date: Fri, 22 Jan 2021 16:36:41 +0800 Subject: 8168926: C2: Bytecode escape analyzer crashes due to stack overflow Summary: :8168926: C2: Bytecode escape analyzer crashes due to stack overflow LLT: N/A Bug url: https://bugs.openjdk.java.net/browse/JDK-8168926 --- hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp | 30 ++++++++++++++++++-- hotspot/src/share/vm/ci/ciMethod.hpp | 12 +++++--- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp index 2b9e0e514..34bdbe94d 100644 --- a/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp +++ b/hotspot/src/share/vm/ci/bcEscapeAnalyzer.cpp @@ -894,9 +894,33 @@ void BCEscapeAnalyzer::iterate_one_block(ciBlock *blk, StateInfo &state, Growabl ciMethod* target = s.get_method(ignored_will_link, &declared_signature); ciKlass* holder = s.get_declared_method_holder(); assert(declared_signature != NULL, "cannot be null"); - // Push appendix argument, if one. - if (s.has_appendix()) { - state.apush(unknown_obj); + // If the current bytecode has an attached appendix argument, + // push an unknown object to represent that argument. (Analysis + // of dynamic call sites, especially invokehandle calls, needs + // the appendix argument on the stack, in addition to "regular" arguments + // pushed onto the stack by bytecode instructions preceding the call.) + // + // The escape analyzer does _not_ use the ciBytecodeStream::has_appendix(s) + // method to determine whether the current bytecode has an appendix argument. + // The has_appendix() method obtains the appendix from the + // ConstantPoolCacheEntry::_f1 field, which can happen concurrently with + // resolution of dynamic call sites. Callees in the + // ciBytecodeStream::get_method() call above also access the _f1 field; + // interleaving the get_method() and has_appendix() calls in the current + // method with call site resolution can lead to an inconsistent view of + // the current method's argument count. In particular, some interleaving(s) + // can cause the method's argument count to not include the appendix, which + // then leads to stack over-/underflow in the escape analyzer. + // + // Instead of pushing the argument if has_appendix() is true, the escape analyzer + // pushes an appendix for all call sites targeted by invokedynamic and invokehandle + // instructions, except if the call site is the _invokeBasic intrinsic + // (that intrinsic is always targeted by an invokehandle instruction but does + // not have an appendix argument). + if (target->is_loaded() && + Bytecodes::has_optional_appendix(s.cur_bc_raw()) && + target->intrinsic_id() != vmIntrinsics::_invokeBasic) { + state.apush(unknown_obj); } // Pass in raw bytecode because we need to see invokehandle instructions. invoke(state, s.cur_bc_raw(), target, holder); diff --git a/hotspot/src/share/vm/ci/ciMethod.hpp b/hotspot/src/share/vm/ci/ciMethod.hpp index 307452422..99d8dbe67 100644 --- a/hotspot/src/share/vm/ci/ciMethod.hpp +++ b/hotspot/src/share/vm/ci/ciMethod.hpp @@ -133,15 +133,19 @@ class ciMethod : public ciMetadata { check_is_loaded(); return _signature->size() + (_flags.is_static() ? 0 : 1); } - // Report the number of elements on stack when invoking this method. - // This is different than the regular arg_size because invokedynamic - // has an implicit receiver. + // Report the number of elements on stack when invoking the current method. + // If the method is loaded, arg_size() gives precise information about the + // number of stack elements (using the method's signature and its flags). + // However, if the method is not loaded, the number of stack elements must + // be determined differently, as the method's flags are not yet available. + // The invoke_arg_size() method assumes in that case that all bytecodes except + // invokestatic and invokedynamic have a receiver that is also pushed onto the + // stack by the caller of the current method. int invoke_arg_size(Bytecodes::Code code) const { if (is_loaded()) { return arg_size(); } else { int arg_size = _signature->size(); - // Add a receiver argument, maybe: if (code != Bytecodes::_invokestatic && code != Bytecodes::_invokedynamic) { arg_size++; -- 2.19.0