mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-02 07:10:23 +00:00
8378796: java.lang.runtime bootstrap methods missing lookup validation
Reviewed-by: jvernee
This commit is contained in:
parent
2b20a1391b
commit
5a19e49dc4
@ -479,12 +479,7 @@ public final class ObjectMethods {
|
||||
* {@link java.lang.Record#toString()}.
|
||||
*
|
||||
*
|
||||
* @param lookup Every bootstrap method is expected to have a {@code lookup}
|
||||
* which usually represents a lookup context with the
|
||||
* accessibility privileges of the caller. This is because
|
||||
* {@code invokedynamic} call sites always provide a {@code lookup}
|
||||
* to the corresponding bootstrap method, but this method just
|
||||
* ignores the {@code lookup} parameter
|
||||
* @param lookup the full-privilege lookup context of the caller
|
||||
* @param methodName the name of the method to generate, which must be one of
|
||||
* {@code "equals"}, {@code "hashCode"}, or {@code "toString"}
|
||||
* @param type a {@link MethodType} corresponding the descriptor type
|
||||
@ -503,8 +498,6 @@ public final class ObjectMethods {
|
||||
* if invoked by a condy
|
||||
* @throws IllegalArgumentException if the bootstrap arguments are invalid
|
||||
* or inconsistent
|
||||
* @throws NullPointerException if any argument is {@code null} or if any element
|
||||
* in the {@code getters} array is {@code null}
|
||||
* @throws Throwable if any exception is thrown during call site construction
|
||||
*/
|
||||
public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type,
|
||||
@ -518,6 +511,9 @@ public final class ObjectMethods {
|
||||
requireNonNull(names);
|
||||
List<MethodHandle> getterList = List.of(getters); // deep null check
|
||||
|
||||
if (!lookup.hasFullPrivilegeAccess())
|
||||
throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString()));
|
||||
|
||||
MethodType methodType;
|
||||
if (type instanceof MethodType mt)
|
||||
methodType = mt;
|
||||
|
||||
@ -169,16 +169,13 @@ public final class SwitchBootstraps {
|
||||
* the length of the {@code labels} array (inclusive),
|
||||
* both or an {@link IndexOutOfBoundsException} is thrown.
|
||||
*
|
||||
* @param lookup Represents a lookup context with the accessibility
|
||||
* privileges of the caller. When used with {@code invokedynamic},
|
||||
* this is stacked automatically by the VM.
|
||||
* @param lookup the full-privilege lookup context of the caller
|
||||
* @param invocationName unused, {@code null} is permitted
|
||||
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
|
||||
* a target type, an {@code int}, and {@code int} as a return type.
|
||||
* @param labels case labels as described above
|
||||
* @return a {@code CallSite} returning the first matching element as described above
|
||||
*
|
||||
* @throws NullPointerException if any argument is {@code null}, unless noted otherwise
|
||||
* @throws IllegalArgumentException if any element in the labels array is null
|
||||
* @throws IllegalArgumentException if the invocation type is not a method type of first parameter of a target type,
|
||||
* second parameter of type {@code int} and with {@code int} as its return type
|
||||
@ -198,6 +195,9 @@ public final class SwitchBootstraps {
|
||||
requireNonNull(invocationType);
|
||||
requireNonNull(labels);
|
||||
|
||||
if (!lookup.hasFullPrivilegeAccess())
|
||||
throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString()));
|
||||
|
||||
Class<?> selectorType = invocationType.parameterType(0);
|
||||
if (invocationType.parameterCount() != 2
|
||||
|| (!invocationType.returnType().equals(int.class))
|
||||
@ -275,9 +275,7 @@ public final class SwitchBootstraps {
|
||||
* @apiNote It is permissible for the {@code labels} array to contain {@code String}
|
||||
* values that do not represent any enum constants at runtime.
|
||||
*
|
||||
* @param lookup Represents a lookup context with the accessibility
|
||||
* privileges of the caller. When used with {@code invokedynamic},
|
||||
* this is stacked automatically by the VM.
|
||||
* @param lookup the full-privilege lookup context of the caller
|
||||
* @param invocationName unused, {@code null} is permitted
|
||||
* @param invocationType The invocation type of the {@code CallSite} with two parameters,
|
||||
* an enum type, an {@code int}, and {@code int} as a return type.
|
||||
@ -285,7 +283,6 @@ public final class SwitchBootstraps {
|
||||
* in any combination
|
||||
* @return a {@code CallSite} returning the first matching element as described above
|
||||
*
|
||||
* @throws NullPointerException if any argument is {@code null}, unless noted otherwise
|
||||
* @throws IllegalArgumentException if any element in the labels array is null
|
||||
* @throws IllegalArgumentException if any element in the labels array is an empty {@code String}
|
||||
* @throws IllegalArgumentException if the invocation type is not a method type
|
||||
@ -305,6 +302,9 @@ public final class SwitchBootstraps {
|
||||
requireNonNull(invocationType);
|
||||
requireNonNull(labels);
|
||||
|
||||
if (!lookup.hasFullPrivilegeAccess())
|
||||
throw new IllegalArgumentException("Unprivileged lookup ".concat(lookup.toString()));
|
||||
|
||||
if (invocationType.parameterCount() != 2
|
||||
|| (!invocationType.returnType().equals(int.class))
|
||||
|| invocationType.parameterType(0).isPrimitive()
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 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
|
||||
@ -26,8 +26,20 @@
|
||||
/**
|
||||
* The {@code java.lang.runtime} package provides low-level runtime support
|
||||
* for the Java language.
|
||||
* <p>
|
||||
* Unless otherwise specified: <ul>
|
||||
* <li>Methods and constructors in this package throw a {@link
|
||||
* NullPointerException} when they are called with {@code null} or an array
|
||||
* that contains {@code null} as an argument.
|
||||
* <li>{@linkplain java.lang.invoke##bsm Bootstrap methods} in this package
|
||||
* throw an {@link IllegalArgumentException} when they are called with a
|
||||
* {@link Lookup Lookup} that does not have {@linkplain
|
||||
* Lookup#hasFullPrivilegeAccess() full privilege access}.
|
||||
* </ul>
|
||||
*
|
||||
* @since 14
|
||||
*/
|
||||
|
||||
package java.lang.runtime;
|
||||
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
|
||||
@ -79,6 +79,7 @@ public class ObjectMethodsTest {
|
||||
}
|
||||
|
||||
static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
static final MethodHandles.Lookup UNPRIVILEGED_LOOKUP = LOOKUP.dropLookupMode(MethodHandles.Lookup.PRIVATE);
|
||||
|
||||
@Test
|
||||
public void testEqualsC() throws Throwable {
|
||||
@ -184,6 +185,9 @@ public class ObjectMethodsTest {
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(LOOKUP, null, type, C.class, "x;y", C.ACCESSORS));
|
||||
assertThrows(NPE, () -> ObjectMethods.bootstrap(null, name, type, C.class, "x;y", C.ACCESSORS));
|
||||
|
||||
// Unprivileged lookup
|
||||
assertThrows(IAE, () -> ObjectMethods.bootstrap(UNPRIVILEGED_LOOKUP, name, type, C.class, "x;y", C.ACCESSORS));
|
||||
|
||||
// Bad indy call receiver type - change C to this test class
|
||||
assertThrows(IAE, () -> ObjectMethods.bootstrap(LOOKUP, name, type.changeParameterType(0, this.getClass()), C.class, "x;y", C.ACCESSORS));
|
||||
|
||||
|
||||
@ -42,40 +42,25 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
/*
|
||||
* @test
|
||||
* @bug 8318144
|
||||
* @enablePreview
|
||||
* @compile SwitchBootstrapsTest.java
|
||||
* @run junit/othervm SwitchBootstrapsTest
|
||||
* @run junit SwitchBootstrapsTest
|
||||
*/
|
||||
public class SwitchBootstrapsTest {
|
||||
|
||||
public static final MethodHandle BSM_TYPE_SWITCH;
|
||||
public static final MethodHandle BSM_ENUM_SWITCH;
|
||||
|
||||
static {
|
||||
try {
|
||||
BSM_TYPE_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "typeSwitch",
|
||||
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class));
|
||||
BSM_ENUM_SWITCH = MethodHandles.lookup().findStatic(SwitchBootstraps.class, "enumSwitch",
|
||||
MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class));
|
||||
}
|
||||
catch (ReflectiveOperationException e) {
|
||||
throw new AssertionError("Should not happen", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void testType(Object target, int start, int result, Object... labels) throws Throwable {
|
||||
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
|
||||
MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
|
||||
MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker();
|
||||
assertEquals(result, (int) indy.invoke(target, start));
|
||||
assertEquals(-1, (int) indy.invoke(null, start));
|
||||
}
|
||||
|
||||
private void testPrimitiveType(Object target, Class<?> targetType, int start, int result, Object... labels) throws Throwable {
|
||||
MethodType switchType = MethodType.methodType(int.class, targetType, int.class);
|
||||
MethodHandle indy = ((CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
|
||||
MethodHandle indy = SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker();
|
||||
assertEquals(result, (int) indy.invoke(target, start));
|
||||
}
|
||||
|
||||
@ -85,7 +70,7 @@ public class SwitchBootstrapsTest {
|
||||
|
||||
private void testEnum(Class<?> targetClass, Enum<?> target, int start, int result, Object... labels) throws Throwable {
|
||||
MethodType switchType = MethodType.methodType(int.class, targetClass, int.class);
|
||||
MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels)).dynamicInvoker();
|
||||
MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", switchType, labels).dynamicInvoker();
|
||||
assertEquals(result, (int) indy.invoke(target, start));
|
||||
assertEquals(-1, (int) indy.invoke(null, start));
|
||||
}
|
||||
@ -188,7 +173,7 @@ public class SwitchBootstrapsTest {
|
||||
|
||||
//null invocation name:
|
||||
MethodType switchType = MethodType.methodType(int.class, E1.class, int.class);
|
||||
MethodHandle indy = ((CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), null, switchType)).dynamicInvoker();
|
||||
MethodHandle indy = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), null, switchType).dynamicInvoker();
|
||||
assertEquals(0, (int) indy.invoke(E1.A, 0));
|
||||
}
|
||||
|
||||
@ -229,7 +214,7 @@ public class SwitchBootstrapsTest {
|
||||
};
|
||||
for (MethodType switchType : switchTypes) {
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType)
|
||||
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType)
|
||||
);
|
||||
}
|
||||
MethodType[] enumSwitchTypes = new MethodType[] {
|
||||
@ -240,7 +225,7 @@ public class SwitchBootstrapsTest {
|
||||
};
|
||||
for (MethodType enumSwitchType : enumSwitchTypes) {
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType)
|
||||
SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -270,23 +255,23 @@ public class SwitchBootstrapsTest {
|
||||
public void testNullLabels() throws Throwable {
|
||||
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
|
||||
assertThrows(NullPointerException.class, () ->
|
||||
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, (Object[]) null)
|
||||
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, (Object[]) null)
|
||||
);
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType,
|
||||
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType,
|
||||
new Object[] {1, null, String.class})
|
||||
);
|
||||
MethodType enumSwitchType = MethodType.methodType(int.class, E1.class, int.class);
|
||||
assertThrows(NullPointerException.class, () ->
|
||||
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null)
|
||||
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType, (Object[]) null)
|
||||
);
|
||||
assertThrows(IllegalArgumentException.class, () ->
|
||||
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType,
|
||||
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", enumSwitchType,
|
||||
new Object[] {1, null, String.class})
|
||||
);
|
||||
//null invocationName is OK:
|
||||
BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), null, switchType,
|
||||
new Object[] {Object.class});
|
||||
SwitchBootstraps.typeSwitch(MethodHandles.lookup(), null, switchType,
|
||||
Object.class);
|
||||
}
|
||||
|
||||
private static AtomicBoolean enumInitialized = new AtomicBoolean();
|
||||
@ -304,7 +289,7 @@ public class SwitchBootstrapsTest {
|
||||
|
||||
MethodType enumSwitchType = MethodType.methodType(int.class, E.class, int.class);
|
||||
|
||||
CallSite invocation = (CallSite) BSM_ENUM_SWITCH.invoke(MethodHandles.lookup(), "", enumSwitchType, new Object[] {"A"});
|
||||
CallSite invocation = SwitchBootstraps.enumSwitch(MethodHandles.lookup(), "", enumSwitchType, "A");
|
||||
assertFalse(enumInitialized.get());
|
||||
assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0));
|
||||
assertFalse(enumInitialized.get());
|
||||
@ -330,7 +315,7 @@ public class SwitchBootstrapsTest {
|
||||
EnumDesc.of(ClassDesc.of(E.class.getName()), "A"),
|
||||
"test"
|
||||
};
|
||||
CallSite invocation = (CallSite) BSM_TYPE_SWITCH.invoke(MethodHandles.lookup(), "", switchType, labels);
|
||||
CallSite invocation = (CallSite) SwitchBootstraps.typeSwitch(MethodHandles.lookup(), "", switchType, labels);
|
||||
assertFalse(enumInitialized.get());
|
||||
assertEquals(-1, invocation.dynamicInvoker().invoke(null, 0));
|
||||
assertFalse(enumInitialized.get());
|
||||
@ -402,21 +387,37 @@ public class SwitchBootstrapsTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullLookup() throws Throwable {
|
||||
public void testNullLookup() {
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
|
||||
BSM_TYPE_SWITCH.invoke(null, "", switchType, Object.class);
|
||||
SwitchBootstraps.typeSwitch(null, "", switchType, Object.class);
|
||||
});
|
||||
enum E {}
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
MethodType switchType = MethodType.methodType(int.class, E.class, int.class);
|
||||
BSM_ENUM_SWITCH.invoke(null, "", switchType,
|
||||
new Object[] {});
|
||||
SwitchBootstraps.enumSwitch(null, "", switchType);
|
||||
});
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
MethodType switchType = MethodType.methodType(int.class, E.class, int.class);
|
||||
BSM_ENUM_SWITCH.invoke(null, "", switchType,
|
||||
new Object[] {"A"});
|
||||
SwitchBootstraps.enumSwitch(null, "", switchType, "A");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnprivilegedLookup() {
|
||||
var lookup = MethodHandles.lookup().dropLookupMode(MethodHandles.Lookup.PRIVATE);
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
MethodType switchType = MethodType.methodType(int.class, Object.class, int.class);
|
||||
SwitchBootstraps.typeSwitch(lookup, "", switchType, Object.class);
|
||||
});
|
||||
enum E {}
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
MethodType switchType = MethodType.methodType(int.class, E.class, int.class);
|
||||
SwitchBootstraps.enumSwitch(lookup, "", switchType);
|
||||
});
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
MethodType switchType = MethodType.methodType(int.class, E.class, int.class);
|
||||
SwitchBootstraps.enumSwitch(lookup, "", switchType, "A");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user