Skip to content

Commit 20ea218

Browse files
dean-longreinrich
andcommitted
8336042: Caller/callee param size mismatch in deoptimization causes crash
Co-authored-by: Richard Reingruber <[email protected]> Reviewed-by: pchilanomate, rrich, vlivanov, never
1 parent 38b4d46 commit 20ea218

File tree

11 files changed

+158
-14
lines changed

11 files changed

+158
-14
lines changed

src/hotspot/cpu/aarch64/abstractInterpreter_aarch64.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ void AbstractInterpreter::layout_activation(Method* method,
149149

150150
#ifdef ASSERT
151151
if (caller->is_interpreted_frame()) {
152-
assert(locals < caller->fp() + frame::interpreter_frame_initial_sp_offset, "bad placement");
152+
assert(locals <= caller->interpreter_frame_expression_stack(), "bad placement");
153+
assert(locals >= interpreter_frame->sender_sp() + max_locals - 1, "bad placement");
153154
}
154155
#endif
155156

src/hotspot/cpu/arm/abstractInterpreter_arm.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,15 @@ void AbstractInterpreter::layout_activation(Method* method,
131131

132132
intptr_t* locals = interpreter_frame->sender_sp() + max_locals - 1;
133133

134+
#ifdef ASSERT
135+
if (caller->is_interpreted_frame()) {
136+
// Test exact placement on top of caller args
137+
intptr_t* l2 = caller->interpreter_frame_last_sp() + caller_actual_parameters - 1;
138+
assert(l2 <= caller->interpreter_frame_expression_stack(), "bad placement");
139+
assert(l2 >= locals, "bad placement");
140+
}
141+
#endif
142+
134143
interpreter_frame->interpreter_frame_set_locals(locals);
135144
BasicObjectLock* montop = interpreter_frame->interpreter_frame_monitor_begin();
136145
BasicObjectLock* monbot = montop - moncount;

src/hotspot/cpu/ppc/abstractInterpreter_ppc.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,15 @@ void AbstractInterpreter::layout_activation(Method* method,
128128
caller->interpreter_frame_esp() + caller_actual_parameters :
129129
caller->sp() + method->max_locals() - 1 + (frame::java_abi_size / Interpreter::stackElementSize);
130130

131+
#ifdef ASSERT
132+
if (caller->is_interpreted_frame()) {
133+
assert(locals_base <= caller->interpreter_frame_expression_stack(), "bad placement");
134+
const int caller_abi_bytesize = (is_bottom_frame ? frame::top_ijava_frame_abi_size : frame::parent_ijava_frame_abi_size);
135+
intptr_t* l2 = caller->sp() + method->max_locals() - 1 + (caller_abi_bytesize / Interpreter::stackElementSize);
136+
assert(locals_base >= l2, "bad placement");
137+
}
138+
#endif
139+
131140
intptr_t* monitor_base = caller->sp() - frame::ijava_state_size / Interpreter::stackElementSize;
132141
intptr_t* monitor = monitor_base - (moncount * frame::interpreter_frame_monitor_size());
133142
intptr_t* esp_base = monitor - 1;

src/hotspot/cpu/riscv/abstractInterpreter_riscv.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ void AbstractInterpreter::layout_activation(Method* method,
141141

142142
#ifdef ASSERT
143143
if (caller->is_interpreted_frame()) {
144-
assert(locals < caller->fp() + frame::interpreter_frame_initial_sp_offset, "bad placement");
144+
assert(locals <= caller->interpreter_frame_expression_stack(), "bad placement");
145+
assert(locals >= interpreter_frame->sender_sp() + max_locals - 1, "bad placement");
145146
}
146147
#endif
147148

src/hotspot/cpu/s390/abstractInterpreter_s390.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,13 @@ void AbstractInterpreter::layout_activation(Method* method,
182182
intptr_t* sender_sp;
183183
if (caller->is_interpreted_frame()) {
184184
sender_sp = caller->interpreter_frame_top_frame_sp();
185+
#ifdef ASSERT
186+
assert(locals_base <= caller->interpreter_frame_expression_stack(), "bad placement");
187+
// Test caller-aligned placement vs callee-aligned
188+
intptr_t* l2 = (caller->sp() + method->max_locals() - 1 +
189+
frame::z_parent_ijava_frame_abi_size / Interpreter::stackElementSize);
190+
assert(locals_base >= l2, "bad placement");
191+
#endif
185192
} else if (caller->is_compiled_frame()) {
186193
sender_sp = caller->fp() - caller->cb()->frame_size();
187194
// The bottom frame's sender_sp is its caller's unextended_sp.

src/hotspot/cpu/x86/abstractInterpreter_x86.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ void AbstractInterpreter::layout_activation(Method* method,
8787

8888
#ifdef ASSERT
8989
if (caller->is_interpreted_frame()) {
90-
assert(locals < caller->fp() + frame::interpreter_frame_initial_sp_offset, "bad placement");
90+
// Test exact placement on top of caller args
91+
intptr_t* l2 = caller->interpreter_frame_last_sp() + caller_actual_parameters - 1;
92+
assert(l2 <= caller->interpreter_frame_expression_stack(), "bad placement");
93+
assert(l2 >= locals, "bad placement");
9194
}
9295
#endif
9396

src/hotspot/share/interpreter/bytecode.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ class Bytecode_invoke: public Bytecode_member_ref {
226226

227227
bool has_appendix();
228228

229+
bool has_member_arg() const;
230+
229231
int size_of_parameters() const;
230232

231233
private:

src/hotspot/share/interpreter/bytecode.inline.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "interpreter/bytecode.hpp"
2929

3030
#include "oops/cpCache.inline.hpp"
31+
#include "prims/methodHandles.hpp"
3132

3233
inline bool Bytecode_invoke::has_appendix() {
3334
if (invoke_code() == Bytecodes::_invokedynamic) {
@@ -37,4 +38,13 @@ inline bool Bytecode_invoke::has_appendix() {
3738
}
3839
}
3940

41+
inline bool Bytecode_invoke::has_member_arg() const {
42+
// NOTE: We could resolve the call and use the resolved adapter method here, but this function
43+
// is used by deoptimization, where resolving could lead to problems, so we avoid that here
44+
// by doing things symbolically.
45+
//
46+
// invokedynamic instructions don't have a class but obviously don't have a MemberName appendix.
47+
return !is_invokedynamic() && MethodHandles::has_member_arg(klass(), name());
48+
}
49+
4050
#endif // SHARE_INTERPRETER_BYTECODE_INLINE_HPP

src/hotspot/share/runtime/deoptimization.cpp

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "gc/shared/collectedHeap.hpp"
3737
#include "gc/shared/memAllocator.hpp"
3838
#include "interpreter/bytecode.hpp"
39+
#include "interpreter/bytecode.inline.hpp"
3940
#include "interpreter/bytecodeStream.hpp"
4041
#include "interpreter/interpreter.hpp"
4142
#include "interpreter/oopMapCache.hpp"
@@ -641,11 +642,12 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
641642
bool caller_was_method_handle = false;
642643
if (deopt_sender.is_interpreted_frame()) {
643644
methodHandle method(current, deopt_sender.interpreter_frame_method());
644-
Bytecode_invoke cur = Bytecode_invoke_check(method, deopt_sender.interpreter_frame_bci());
645-
if (cur.is_invokedynamic() || cur.is_invokehandle()) {
646-
// Method handle invokes may involve fairly arbitrary chains of
647-
// calls so it's impossible to know how much actual space the
648-
// caller has for locals.
645+
Bytecode_invoke cur(method, deopt_sender.interpreter_frame_bci());
646+
if (cur.has_member_arg()) {
647+
// This should cover all real-world cases. One exception is a pathological chain of
648+
// MH.linkToXXX() linker calls, which only trusted code could do anyway. To handle that case, we
649+
// would need to get the size from the resolved method entry. Another exception would
650+
// be an invokedynamic with an adapter that is really a MethodHandle linker.
649651
caller_was_method_handle = true;
650652
}
651653
}
@@ -748,9 +750,14 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
748750
}
749751
#endif
750752

753+
int caller_actual_parameters = -1; // value not used except for interpreted frames, see below
754+
if (deopt_sender.is_interpreted_frame()) {
755+
caller_actual_parameters = callee_parameters + (caller_was_method_handle ? 1 : 0);
756+
}
757+
751758
UnrollBlock* info = new UnrollBlock(array->frame_size() * BytesPerWord,
752759
caller_adjustment * BytesPerWord,
753-
caller_was_method_handle ? 0 : callee_parameters,
760+
caller_actual_parameters,
754761
number_of_frames,
755762
frame_sizes,
756763
frame_pcs,
@@ -939,7 +946,7 @@ JRT_LEAF(BasicType, Deoptimization::unpack_frames(JavaThread* thread, int exec_m
939946
if (Bytecodes::is_invoke(cur_code)) {
940947
Bytecode_invoke invoke(mh, iframe->interpreter_frame_bci());
941948
cur_invoke_parameter_size = invoke.size_of_parameters();
942-
if (i != 0 && !invoke.is_invokedynamic() && MethodHandles::has_member_arg(invoke.klass(), invoke.name())) {
949+
if (i != 0 && invoke.has_member_arg()) {
943950
callee_size_of_parameters++;
944951
}
945952
}

src/hotspot/share/runtime/vframeArray.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "classfile/vmSymbols.hpp"
2626
#include "code/vmreg.inline.hpp"
2727
#include "interpreter/bytecode.hpp"
28+
#include "interpreter/bytecode.inline.hpp"
2829
#include "interpreter/interpreter.hpp"
2930
#include "memory/allocation.inline.hpp"
3031
#include "memory/resourceArea.hpp"
@@ -610,10 +611,7 @@ void vframeArray::unpack_to_stack(frame &unpack_frame, int exec_mode, int caller
610611
methodHandle caller(current, elem->method());
611612
methodHandle callee(current, element(index - 1)->method());
612613
Bytecode_invoke inv(caller, elem->bci());
613-
// invokedynamic instructions don't have a class but obviously don't have a MemberName appendix.
614-
// NOTE: Use machinery here that avoids resolving of any kind.
615-
const bool has_member_arg =
616-
!inv.is_invokedynamic() && MethodHandles::has_member_arg(inv.klass(), inv.name());
614+
const bool has_member_arg = inv.has_member_arg();
617615
callee_parameters = callee->size_of_parameters() + (has_member_arg ? 1 : 0);
618616
callee_locals = callee->max_locals();
619617
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
package compiler.jsr292;
25+
26+
import java.lang.invoke.MethodHandle;
27+
import java.lang.invoke.MethodHandles;
28+
import java.lang.invoke.MethodHandles.Lookup;
29+
import java.lang.invoke.MethodType;
30+
import java.lang.reflect.Method;
31+
32+
/*
33+
* @test
34+
* @bug 8336042
35+
* @library /test/lib /
36+
*
37+
* @run main/bootclasspath/othervm -Xbatch -XX:-TieredCompilation compiler.jsr292.MHDeoptTest
38+
*
39+
*/
40+
public class MHDeoptTest {
41+
42+
static int xx = 0;
43+
44+
public static void main(String[] args) throws Throwable {
45+
MethodHandle mh1 = MethodHandles.lookup().findStatic(MHDeoptTest.class, "body1", MethodType.methodType(int.class));
46+
MethodHandle mh2 = MethodHandles.lookup().findStatic(MHDeoptTest.class, "body2", MethodType.methodType(int.class));
47+
MethodHandle[] arr = new MethodHandle[] {mh2, mh1};
48+
49+
for (MethodHandle mh : arr) {
50+
for (int i = 1; i < 50_000; i++) {
51+
xx = i;
52+
mainLink(mh);
53+
}
54+
}
55+
56+
}
57+
58+
static int mainLink(MethodHandle mh) throws Throwable {
59+
return (int)mh.invokeExact();
60+
}
61+
62+
static int cnt = 1000;
63+
64+
static int body1() {
65+
int limit = 0x7fff;
66+
// uncommon trap
67+
if (xx == limit) {
68+
// OSR
69+
for (int i = 0; i < 50_000; i++) {
70+
}
71+
++cnt;
72+
++xx;
73+
}
74+
if (xx == limit + 1) {
75+
return cnt + 1;
76+
}
77+
return cnt;
78+
}
79+
80+
static int body2() {
81+
int limit = 0x7fff;
82+
int dummy = 0;
83+
// uncommon trap
84+
if (xx == limit) {
85+
// OSR
86+
for (int i = 0; i < 50_000; i++) {
87+
}
88+
++cnt;
89+
++xx;
90+
}
91+
if (xx == limit + 1) {
92+
return cnt + 1;
93+
}
94+
return cnt;
95+
}
96+
97+
}

0 commit comments

Comments
 (0)