undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
339
test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java
Normal file
339
test/jdk/java/lang/invoke/condy/CondyRepeatFailedResolution.java
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 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 8186211
|
||||
* @summary Test basic invocation of multiple ldc's of the same dynamic constant that fail resolution
|
||||
* @library /java/lang/invoke/common
|
||||
* @run junit CondyRepeatFailedResolution
|
||||
* @run junit/othervm -XX:+UnlockDiagnosticVMOptions -XX:UseBootstrapCallInfo=3 CondyRepeatFailedResolution
|
||||
*/
|
||||
|
||||
import java.lang.classfile.ClassFile;
|
||||
import java.lang.constant.*;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class CondyRepeatFailedResolution {
|
||||
// Counter used to determine if a given BSM is invoked more than once
|
||||
static int bsm_called = 0;
|
||||
|
||||
// Generated class with methods containing condy ldc
|
||||
static Class<?> gc;
|
||||
|
||||
// Bootstrap method used to represent primitive values
|
||||
// that cannot be represented directly in the constant pool,
|
||||
// such as byte, and for completeness of testing primitive values
|
||||
// that can be represented directly, such as double or long that
|
||||
// take two slots
|
||||
public static Object intConversion(MethodHandles.Lookup l,
|
||||
String constantName,
|
||||
Class<?> constantType,
|
||||
int value) throws Throwable {
|
||||
++bsm_called;
|
||||
// replace constantName with a bogus value to trigger failed resolution
|
||||
constantName = "Foo";
|
||||
|
||||
switch (constantName) {
|
||||
case "B":
|
||||
return (byte) value;
|
||||
case "C":
|
||||
return (char) value;
|
||||
case "D":
|
||||
return (double) value;
|
||||
case "F":
|
||||
return (float) value;
|
||||
case "I":
|
||||
return value;
|
||||
case "J":
|
||||
return (long) value;
|
||||
case "S":
|
||||
return (short) value;
|
||||
case "Z":
|
||||
return value > 0;
|
||||
case "nullRef":
|
||||
return null;
|
||||
case "string":
|
||||
return "string";
|
||||
case "stringArray":
|
||||
return new String[]{"string", "string"};
|
||||
default:
|
||||
throw new BootstrapMethodError("Failure to generate a dynamic constant");
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void generateClass() throws Exception {
|
||||
String genClassName = CondyRepeatFailedResolution.class.getSimpleName() + "$Code";
|
||||
String bsmClassDesc = CondyRepeatFailedResolution.class.descriptorString();
|
||||
String bsmMethodName = "intConversion";
|
||||
String bsmDescriptor = MethodType.methodType(Object.class, MethodHandles.Lookup.class,
|
||||
String.class, Class.class, int.class).toMethodDescriptorString();
|
||||
DirectMethodHandleDesc bsmMhDesc = MethodHandleDesc.of(
|
||||
DirectMethodHandleDesc.Kind.STATIC,
|
||||
ClassDesc.ofDescriptor(bsmClassDesc),
|
||||
bsmMethodName,
|
||||
bsmDescriptor
|
||||
);
|
||||
byte[] byteArray = ClassFile.of().build(ClassDesc.of(genClassName), classBuilder -> classBuilder
|
||||
.withVersion(55, 0)
|
||||
.withSuperclass(ConstantDescs.CD_Object)
|
||||
.withMethod(ConstantDescs.INIT_NAME, ConstantDescs.MTD_void, ClassFile.ACC_PUBLIC,
|
||||
methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.aload(0)
|
||||
.invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME,
|
||||
ConstantDescs.MTD_void, false)
|
||||
.return_()
|
||||
)
|
||||
)
|
||||
.withMethod("B", MethodTypeDesc.of(ConstantDescs.CD_byte),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"B",
|
||||
ConstantDescs.CD_byte,
|
||||
(int) Byte.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("C", MethodTypeDesc.of(ConstantDescs.CD_char),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"C",
|
||||
ConstantDescs.CD_char,
|
||||
(int) Character.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("D", MethodTypeDesc.of(ConstantDescs.CD_double),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"D",
|
||||
ConstantDescs.CD_double,
|
||||
Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("D_AsType", MethodTypeDesc.of(ConstantDescs.CD_double),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_double,
|
||||
Integer.MAX_VALUE))
|
||||
.dreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("F", MethodTypeDesc.of(ConstantDescs.CD_float),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"F",
|
||||
ConstantDescs.CD_float,
|
||||
Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
)
|
||||
)
|
||||
.withMethod("F_AsType", MethodTypeDesc.of(ConstantDescs.CD_float),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_float,
|
||||
Integer.MAX_VALUE))
|
||||
.freturn()
|
||||
)
|
||||
)
|
||||
.withMethod("I", MethodTypeDesc.of(ConstantDescs.CD_int),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_int,
|
||||
Integer.MAX_VALUE))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("J", MethodTypeDesc.of(ConstantDescs.CD_long),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"J",
|
||||
ConstantDescs.CD_long,
|
||||
Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("J_AsType", MethodTypeDesc.of(ConstantDescs.CD_long),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"I",
|
||||
ConstantDescs.CD_long,
|
||||
Integer.MAX_VALUE))
|
||||
.lreturn()
|
||||
)
|
||||
)
|
||||
.withMethod("S", MethodTypeDesc.of(ConstantDescs.CD_short),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"S",
|
||||
ConstantDescs.CD_short,
|
||||
((int) Short.MAX_VALUE)))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("Z_F", MethodTypeDesc.of(ConstantDescs.CD_boolean),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"Z",
|
||||
ConstantDescs.CD_boolean,
|
||||
0))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("Z_T", MethodTypeDesc.of(ConstantDescs.CD_boolean),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"Z",
|
||||
ConstantDescs.CD_boolean,
|
||||
1))
|
||||
.ireturn()
|
||||
)
|
||||
)
|
||||
.withMethod("null", MethodTypeDesc.of(ConstantDescs.CD_Object),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"nullRef",
|
||||
ConstantDescs.CD_Object,
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
.withMethod("string", MethodTypeDesc.of(ConstantDescs.CD_String),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"string",
|
||||
ConstantDescs.CD_String,
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
.withMethod("stringArray", MethodTypeDesc.of(ConstantDescs.CD_String.arrayType()),
|
||||
ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC, methodBuilder -> methodBuilder
|
||||
.withCode(codeBuilder -> codeBuilder
|
||||
.ldc(DynamicConstantDesc.ofNamed(
|
||||
bsmMhDesc,
|
||||
"stringArray",
|
||||
ConstantDescs.CD_String.arrayType(),
|
||||
Integer.MAX_VALUE))
|
||||
.areturn()
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
gc = MethodHandles.lookup().defineClass(byteArray);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimitives() throws Exception {
|
||||
testConstants();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefs() throws Exception {
|
||||
testConstant("string", "string");
|
||||
testConstant("stringArray", new String[]{"string", "string"});
|
||||
}
|
||||
|
||||
void testConstants() throws Exception {
|
||||
// Note: for the _asType methods the BSM returns an int which is
|
||||
// then converted by an asType transformation
|
||||
|
||||
testConstant("B", Byte.MAX_VALUE);
|
||||
testConstant("C", Character.MAX_VALUE);
|
||||
testConstant("D", (double) Integer.MAX_VALUE);
|
||||
testConstant("D_AsType", (double) Integer.MAX_VALUE);
|
||||
testConstant("F", (float) Integer.MAX_VALUE);
|
||||
testConstant("F_AsType", (float) Integer.MAX_VALUE);
|
||||
testConstant("I", Integer.MAX_VALUE);
|
||||
testConstant("J", (long) Integer.MAX_VALUE);
|
||||
testConstant("J_AsType", (long) Integer.MAX_VALUE);
|
||||
testConstant("S", Short.MAX_VALUE);
|
||||
testConstant("Z_F", false);
|
||||
testConstant("Z_T", true);
|
||||
testConstant("null", null);
|
||||
}
|
||||
|
||||
void testConstant(String name, Object expected) throws Exception {
|
||||
Method m = gc.getDeclaredMethod(name);
|
||||
|
||||
bsm_called = 0;
|
||||
InvocationTargetException e1 = assertThrows(InvocationTargetException.class, () -> {
|
||||
Object r1 = m.invoke(null);
|
||||
});
|
||||
// bsm_called should have been incremented prior to the exception
|
||||
assertEquals(1, bsm_called);
|
||||
assertInstanceOf(BootstrapMethodError.class, e1.getCause());
|
||||
// Try invoking method again to ensure that the bootstrap
|
||||
// method is not invoked twice and a resolution failure
|
||||
// results.
|
||||
InvocationTargetException e2 = assertThrows(InvocationTargetException.class, () -> {
|
||||
Object r2 = m.invoke(null);
|
||||
});
|
||||
// bsm_called should remain at 1 since the bootstrap
|
||||
// method should not have been invoked.
|
||||
assertEquals(1, bsm_called);
|
||||
assertInstanceOf(BootstrapMethodError.class, e2.getCause());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue