mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-05 00:29:40 +00:00
161 lines
6.8 KiB
Java
161 lines
6.8 KiB
Java
/*
|
|
* Copyright (c) 2026, 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
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8216486
|
|
* @summary Verify BCEscapeAnalyzer handles methods where
|
|
* (numblocks+1)*(max_stack+max_locals) overflows a 32-bit int.
|
|
* On a UBSAN build the signed overflow would be caught as UB;
|
|
* on a normal build the test verifies no crash from the bogus
|
|
* allocation size that resulted from the overflow.
|
|
*
|
|
* @requires vm.compiler2.enabled
|
|
*
|
|
* @run main/othervm -Xcomp -XX:-TieredCompilation
|
|
* compiler.escapeAnalysis.TestBCEscapeAnalyzerOverflow
|
|
*/
|
|
|
|
package compiler.escapeAnalysis;
|
|
|
|
import java.lang.classfile.ClassFile;
|
|
import java.lang.classfile.Label;
|
|
import java.lang.constant.ClassDesc;
|
|
import java.lang.constant.ConstantDescs;
|
|
import java.lang.constant.MethodTypeDesc;
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.lang.invoke.MethodType;
|
|
|
|
public class TestBCEscapeAnalyzerOverflow {
|
|
|
|
// Number of goto instructions in the generated method.
|
|
// Creates NUM_GOTOS + 1 basic blocks. With max_stack = 0xFFFF and
|
|
// max_locals = 0xFFFF the product (numblocks+1)*(max_stack+max_locals)
|
|
// is 16386 * 131070 = 2,147,713,020 which exceeds Integer.MAX_VALUE.
|
|
static final int NUM_GOTOS = 16384;
|
|
static final int TARGET_MAX_STACK = 0xFFFF;
|
|
static final int TARGET_MAX_LOCALS = 0xFFFF;
|
|
|
|
static final ClassDesc CD_HELPER =
|
|
ClassDesc.of("compiler.escapeAnalysis.BCEscapeOverflowHelper");
|
|
|
|
public static void main(String[] args) throws Throwable {
|
|
byte[] classBytes = buildClass();
|
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
|
Class<?> cls = lookup.defineClass(classBytes);
|
|
|
|
// caller() allocates an Object and passes it to bigMethod() via
|
|
// invokestatic. Under -Xcomp -XX:-TieredCompilation, C2 compiles
|
|
// caller() and invokes BCEscapeAnalyzer on bigMethod to determine
|
|
// whether the argument escapes. Without the fix the 32-bit
|
|
// overflow in iterate_blocks leads to undefined behavior.
|
|
var mh = lookup.findStatic(cls, "caller",
|
|
MethodType.methodType(void.class));
|
|
mh.invoke();
|
|
}
|
|
|
|
/**
|
|
* Builds a minimal class (version 50, no StackMapTable needed) with:
|
|
* public static void bigMethod(Object o) -- pathological method
|
|
* public static void caller() -- calls bigMethod
|
|
*
|
|
* The ClassFile API generates the bytecode; max_stack and max_locals
|
|
* of bigMethod are then patched to the target overflow-triggering values.
|
|
*/
|
|
static byte[] buildClass() {
|
|
var mtd_Obj_void = MethodTypeDesc.of(ConstantDescs.CD_void,
|
|
ConstantDescs.CD_Object);
|
|
var mtd_void = MethodTypeDesc.of(ConstantDescs.CD_void);
|
|
|
|
byte[] bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS)
|
|
.build(CD_HELPER, cb -> {
|
|
cb.withVersion(50, 0);
|
|
cb.withFlags(ClassFile.ACC_PUBLIC | ClassFile.ACC_SUPER);
|
|
|
|
// bigMethod(Object o): aload_0, pop, <goto chain>, return
|
|
cb.withMethod("bigMethod", mtd_Obj_void,
|
|
ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
|
|
mb -> mb.withCode(code -> {
|
|
code.aload(0);
|
|
code.pop();
|
|
for (int i = 0; i < NUM_GOTOS; i++) {
|
|
Label next = code.newLabel();
|
|
code.goto_(next);
|
|
code.labelBinding(next);
|
|
}
|
|
code.return_();
|
|
}));
|
|
|
|
// caller(): new Object → dup → invokespecial <init> →
|
|
// invokestatic bigMethod → return
|
|
cb.withMethod("caller", mtd_void,
|
|
ClassFile.ACC_PUBLIC | ClassFile.ACC_STATIC,
|
|
mb -> mb.withCode(code -> {
|
|
code.new_(ConstantDescs.CD_Object);
|
|
code.dup();
|
|
code.invokespecial(ConstantDescs.CD_Object,
|
|
"<init>", mtd_void);
|
|
code.invokestatic(CD_HELPER,
|
|
"bigMethod", mtd_Obj_void);
|
|
code.return_();
|
|
}));
|
|
});
|
|
|
|
patchBigMethodMaxes(bytes);
|
|
return bytes;
|
|
}
|
|
|
|
/**
|
|
* Locates bigMethod's Code attribute and patches max_stack/max_locals
|
|
* to TARGET_MAX_STACK/TARGET_MAX_LOCALS. The ClassFile API computes
|
|
* small values (max_stack=1, max_locals=1); we inflate them to create
|
|
* the pathological overflow case.
|
|
*
|
|
* The Code attribute layout is:
|
|
* attribute_name_index(u2), attribute_length(u4),
|
|
* max_stack(u2), max_locals(u2), code_length(u4), code[...]...
|
|
*
|
|
* We search for bigMethod's unique code_length and patch the two u2
|
|
* fields immediately before it.
|
|
*/
|
|
static void patchBigMethodMaxes(byte[] b) {
|
|
int expectedCodeLen = NUM_GOTOS * 3 + 3;
|
|
for (int i = 4; i <= b.length - 4; i++) {
|
|
int codeLen = ((b[i] & 0xFF) << 24) | ((b[i + 1] & 0xFF) << 16)
|
|
| ((b[i + 2] & 0xFF) << 8) | (b[i + 3] & 0xFF);
|
|
if (codeLen == expectedCodeLen) {
|
|
int ms = ((b[i - 4] & 0xFF) << 8) | (b[i - 3] & 0xFF);
|
|
int ml = ((b[i - 2] & 0xFF) << 8) | (b[i - 1] & 0xFF);
|
|
if (ms <= 2 && ml <= 2) {
|
|
b[i - 4] = (byte)(TARGET_MAX_STACK >>> 8);
|
|
b[i - 3] = (byte)(TARGET_MAX_STACK);
|
|
b[i - 2] = (byte)(TARGET_MAX_LOCALS >>> 8);
|
|
b[i - 1] = (byte)(TARGET_MAX_LOCALS);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
throw new RuntimeException("Could not find bigMethod Code attribute");
|
|
}
|
|
}
|