8307610: Linker::nativeLinker should not be restricted (mainline)

Reviewed-by: jvernee
This commit is contained in:
Maurizio Cimadamore 2023-05-11 09:45:45 +00:00
parent ecc1d85dbe
commit ba9714d44c
11 changed files with 111 additions and 63 deletions
src/java.base/share/classes
java/lang/foreign
jdk/internal/foreign/abi
test/jdk/java/foreign
enablenativeaccess
handles
invoker_module/handle/invoker
lookup_module/handle/lookup

@ -430,11 +430,6 @@ public sealed interface Linker permits AbstractLinker {
/**
* Returns a linker for the ABI associated with the underlying native platform. The underlying native platform
* is the combination of OS and processor where the Java runtime is currently executing.
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @apiNote It is not currently possible to obtain a linker for a different combination of OS and processor.
* @implNote The libraries exposed by the {@linkplain #defaultLookup() default lookup} associated with the returned
@ -443,11 +438,8 @@ public sealed interface Linker permits AbstractLinker {
*
* @return a linker for the ABI associated with the underlying native platform.
* @throws UnsupportedOperationException if the underlying native platform is not supported.
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
*/
@CallerSensitive
static Linker nativeLinker() {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "nativeLinker");
return SharedUtils.getSystemLinker();
}
@ -458,6 +450,11 @@ public sealed interface Linker permits AbstractLinker {
* {@snippet lang=java :
* linker.downcallHandle(function).bindTo(symbol);
* }
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param symbol the address of the target function.
* @param function the function descriptor of the target function.
@ -466,11 +463,10 @@ public sealed interface Linker permits AbstractLinker {
* @throws IllegalArgumentException if the provided function descriptor is not supported by this linker.
* or if the symbol is {@link MemorySegment#NULL}
* @throws IllegalArgumentException if an invalid combination of linker options is given.
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
*/
default MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) {
SharedUtils.checkSymbol(symbol);
return downcallHandle(function, options).bindTo(symbol);
}
@CallerSensitive
MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options);
/**
* Creates a method handle which is used to call a foreign function with the given signature.
@ -502,6 +498,11 @@ public sealed interface Linker permits AbstractLinker {
* The returned method handle will throw an {@link IllegalArgumentException} if the {@link MemorySegment}
* representing the target address of the foreign function is the {@link MemorySegment#NULL} address.
* The returned method handle will additionally throw {@link NullPointerException} if any argument passed to it is {@code null}.
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param function the function descriptor of the target function.
* @param options any linker options.
@ -509,7 +510,9 @@ public sealed interface Linker permits AbstractLinker {
* from the provided function descriptor.
* @throws IllegalArgumentException if the provided function descriptor is not supported by this linker.
* @throws IllegalArgumentException if an invalid combination of linker options is given.
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
*/
@CallerSensitive
MethodHandle downcallHandle(FunctionDescriptor function, Option... options);
/**
@ -533,6 +536,11 @@ public sealed interface Linker permits AbstractLinker {
* could wrap all code in the target method handle in a try/catch block that catches any {@link Throwable}, for
* instance by using the {@link java.lang.invoke.MethodHandles#catchException(MethodHandle, Class, MethodHandle)}
* method handle combinator, and handle exceptions as desired in the corresponding catch block.
* <p>
* This method is <a href="package-summary.html#restricted"><em>restricted</em></a>.
* Restricted methods are unsafe, and, if used incorrectly, their use might crash
* the JVM or, worse, silently result in memory corruption. Thus, clients should refrain from depending on
* restricted methods, and use safe and supported functionalities, where possible.
*
* @param target the target method handle.
* @param function the upcall stub function descriptor.
@ -545,7 +553,9 @@ public sealed interface Linker permits AbstractLinker {
* @throws IllegalStateException if {@code arena.scope().isAlive() == false}
* @throws WrongThreadException if {@code arena} is a confined arena, and this method is called from a
* thread {@code T}, other than the arena's owner thread.
* @throws IllegalCallerException If the caller is in a module that does not have native access enabled.
*/
@CallerSensitive
MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options);
/**

@ -34,6 +34,8 @@ import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
import jdk.internal.foreign.layout.AbstractLayout;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import java.lang.foreign.AddressLayout;
import java.lang.foreign.GroupLayout;
@ -67,7 +69,21 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch
private final SoftReferenceCache<LinkRequest, UpcallStubFactory> UPCALL_CACHE = new SoftReferenceCache<>();
@Override
public MethodHandle downcallHandle(FunctionDescriptor function, Option... options) {
@CallerSensitive
public final MethodHandle downcallHandle(MemorySegment symbol, FunctionDescriptor function, Option... options) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "downcallHandle");
SharedUtils.checkSymbol(symbol);
return downcallHandle0(function, options).bindTo(symbol);
}
@Override
@CallerSensitive
public final MethodHandle downcallHandle(FunctionDescriptor function, Option... options) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "downcallHandle");
return downcallHandle0(function, options);
}
private final MethodHandle downcallHandle0(FunctionDescriptor function, Option... options) {
Objects.requireNonNull(function);
Objects.requireNonNull(options);
checkLayouts(function);
@ -82,10 +98,13 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch
return handle;
});
}
protected abstract MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options);
@Override
public MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options) {
@CallerSensitive
public final MemorySegment upcallStub(MethodHandle target, FunctionDescriptor function, Arena arena, Linker.Option... options) {
Reflection.ensureNativeAccess(Reflection.getCallerClass(), Linker.class, "upcallStub");
Objects.requireNonNull(arena);
Objects.requireNonNull(target);
Objects.requireNonNull(function);

@ -24,6 +24,8 @@
package org.openjdk.foreigntest;
import java.lang.foreign.*;
import java.lang.foreign.Linker.Option;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
@ -36,31 +38,25 @@ public class PanamaMainUnnamedModule {
public static void main(String[] args) throws Throwable {
testReflection();
testSetAccessible();
testInvoke();
testDirectAccess();
testJNIAccess();
}
public static void testReflection() throws Throwable {
Method method = Linker.class.getDeclaredMethod("nativeLinker");
method.invoke(null);
}
public static void testSetAccessible() throws Throwable {
Method method = Linker.class.getDeclaredMethod("nativeLinker");
method.setAccessible(true);
method.invoke(null);
Linker linker = Linker.nativeLinker();
Method method = Linker.class.getDeclaredMethod("downcallHandle", FunctionDescriptor.class, Option[].class);
method.invoke(linker, FunctionDescriptor.ofVoid(), new Linker.Option[0]);
}
public static void testInvoke() throws Throwable {
var mh = MethodHandles.lookup().findStatic(Linker.class, "nativeLinker",
MethodType.methodType(Linker.class));
var linker = (Linker)mh.invokeExact();
var mh = MethodHandles.lookup().findVirtual(Linker.class, "downcallHandle",
MethodType.methodType(MethodHandle.class, FunctionDescriptor.class, Linker.Option[].class));
var downcall = (MethodHandle)mh.invokeExact(Linker.nativeLinker(), FunctionDescriptor.ofVoid(), new Linker.Option[0]);
}
public static void testDirectAccess() {
Linker.nativeLinker();
Linker.nativeLinker().downcallHandle(FunctionDescriptor.ofVoid());
}
public static void testJNIAccess() {

@ -27,8 +27,8 @@ import java.lang.foreign.*;
public class PanamaMain {
public static void main(String[] args) {
System.out.println("Trying to get Linker");
Linker.nativeLinker();
System.out.println("Got Linker");
System.out.println("Trying to obtain a downcall handle");
Linker.nativeLinker().downcallHandle(FunctionDescriptor.ofVoid());
System.out.println("Got downcall handle");
}
}

@ -33,9 +33,9 @@ public class PanamaMainDirect {
}
public static void testDirectAccessCLinker() {
System.out.println("Trying to get Linker");
Linker.nativeLinker();
System.out.println("Got Linker");
System.out.println("Trying to obtain a downcall handle");
Linker.nativeLinker().downcallHandle(FunctionDescriptor.ofVoid());
System.out.println("Got downcall handle");
}
public static void testDirectAccessMemorySegment() {

@ -34,11 +34,12 @@ public class PanamaMainInvoke {
}
public static void testInvokenativeLinker() throws Throwable {
System.out.println("Trying to get Linker");
var mh = MethodHandles.lookup().findStatic(Linker.class, "nativeLinker",
MethodType.methodType(Linker.class));
var linker = (Linker)mh.invokeExact();
System.out.println("Got Linker");
Linker linker = Linker.nativeLinker();
System.out.println("Trying to obtain a downcall handle");
var mh = MethodHandles.lookup().findVirtual(Linker.class, "downcallHandle",
MethodType.methodType(MethodHandle.class, FunctionDescriptor.class, Linker.Option[].class));
var handle = (MethodHandle)mh.invokeExact(linker, FunctionDescriptor.ofVoid(), new Linker.Option[0]);
System.out.println("Got downcall handle");
}
public static void testInvokeMemorySegment() throws Throwable {

@ -1,5 +1,8 @@
package org.openjdk.foreigntest;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
public class PanamaMainJNI {
static {
@ -11,10 +14,10 @@ public class PanamaMainJNI {
}
public static void testDirectAccessCLinker() {
System.out.println("Trying to get Linker");
nativeLinker0();
System.out.println("Got Linker");
System.out.println("Trying to get downcall handle");
nativeLinker0(Linker.nativeLinker(), FunctionDescriptor.ofVoid(), new Linker.Option[0]);
System.out.println("Got downcall handle");
}
static native void nativeLinker0();
static native void nativeLinker0(Linker linker, FunctionDescriptor desc, Linker.Option[] options);
}

@ -25,6 +25,7 @@ package org.openjdk.foreigntest;
import java.lang.foreign.*;
import java.lang.foreign.Arena;
import java.lang.foreign.Linker.Option;
import java.lang.reflect.Method;
public class PanamaMainReflection {
@ -34,10 +35,11 @@ public class PanamaMainReflection {
}
public static void testReflectionnativeLinker() throws Throwable {
System.out.println("Trying to get Linker");
Method method = Linker.class.getDeclaredMethod("nativeLinker");
method.invoke(null);
System.out.println("Got Linker");
Linker linker = Linker.nativeLinker();
System.out.println("Trying to get downcall handle");
Method method = Linker.class.getDeclaredMethod("downcallHandle", FunctionDescriptor.class, Option[].class);
method.invoke(linker, FunctionDescriptor.ofVoid(), new Linker.Option[0]);
System.out.println("Got downcall handle");
}
public static void testReflectionMemorySegment() throws Throwable {

@ -25,21 +25,32 @@
#include "jni.h"
#include "testlib_threads.h"
void call(void* ctxt) {
JavaVM *jvm = (JavaVM*) ctxt;
typedef struct {
JavaVM* jvm;
jobject linker;
jobject desc;
jarray opts;
} Context;
void call(void* arg) {
Context* context = (Context*)arg;
JNIEnv* env;
jvm->AttachCurrentThread((void**)&env, NULL);
context->jvm->AttachCurrentThread((void**)&env, NULL);
jclass linkerClass = env->FindClass("java/lang/foreign/Linker");
jmethodID nativeLinkerMethod = env->GetStaticMethodID(linkerClass, "nativeLinker", "()Ljava/lang/foreign/Linker;");
env->CallStaticVoidMethod(linkerClass, nativeLinkerMethod);
jvm->DetachCurrentThread();
jmethodID nativeLinkerMethod = env->GetMethodID(linkerClass, "downcallHandle",
"(Ljava/lang/foreign/FunctionDescriptor;[Ljava/lang/foreign/Linker$Option;)Ljava/lang/invoke/MethodHandle;");
env->CallVoidMethod(context->linker, nativeLinkerMethod, context->desc, context->opts);
context->jvm->DetachCurrentThread();
}
extern "C" {
JNIEXPORT void JNICALL
Java_org_openjdk_foreigntest_PanamaMainJNI_nativeLinker0(JNIEnv *env, jclass cls) {
JavaVM* jvm;
env->GetJavaVM(&jvm);
run_in_new_thread_and_join(call, jvm);
Java_org_openjdk_foreigntest_PanamaMainJNI_nativeLinker0(JNIEnv *env, jclass cls, jobject linker, jobject desc, jarray opts) {
Context context;
env->GetJavaVM(&context.jvm);
context.linker = linker;
context.desc = desc;
context.opts = opts;
run_in_new_thread_and_join(call, &context);
}
}

@ -59,7 +59,7 @@ public class MethodHandleInvoker {
static final Map<Class<?>, Object> DEFAULT_VALUES = new HashMap<>();
static <Z> void addDefaultMapping(Class<Z> carrier, Z value) {
static void addDefaultMapping(Class<?> carrier, Object value) {
DEFAULT_VALUES.put(carrier, value);
}
@ -67,7 +67,7 @@ public class MethodHandleInvoker {
addDefaultMapping(Linker.class, Linker.nativeLinker());
addDefaultMapping(Path.class, Path.of("nonExistent"));
addDefaultMapping(String.class, "Hello!");
addDefaultMapping(Runnable.class, () -> {});
addDefaultMapping(Runnable.class, (Runnable)() -> {});
addDefaultMapping(MethodHandle.class, MethodHandles.identity(int.class));
addDefaultMapping(Charset.class, Charset.defaultCharset());
addDefaultMapping(MethodType.class, MethodType.methodType(void.class));
@ -88,6 +88,8 @@ public class MethodHandleInvoker {
addDefaultMapping(AddressLayout.class, ValueLayout.ADDRESS);
addDefaultMapping(SymbolLookup.class, SymbolLookup.loaderLookup());
addDefaultMapping(Consumer.class, (Consumer<Object>)(Object o) -> {});
addDefaultMapping(FunctionDescriptor.class, FunctionDescriptor.ofVoid());
addDefaultMapping(Linker.Option[].class, null);
addDefaultMapping(byte.class, (byte)0);
addDefaultMapping(boolean.class, true);
addDefaultMapping(char.class, (char)0);
@ -105,10 +107,9 @@ public class MethodHandleInvoker {
}
static Object makeArg(Class<?> clazz) {
Object value = DEFAULT_VALUES.get(clazz);
if (value == null) {
if (!DEFAULT_VALUES.containsKey(clazz)) {
throw new UnsupportedOperationException(clazz.getName());
}
return value;
return DEFAULT_VALUES.get(clazz);
}
}

@ -30,6 +30,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.SymbolLookup;
@ -49,8 +50,12 @@ public class MethodHandleLookup {
static Object[][] restrictedMethods() {
try {
return new Object[][]{
{ MethodHandles.lookup().findStatic(Linker.class, "nativeLinker",
MethodType.methodType(Linker.class)), "Linker::nativeLinker" },
{ MethodHandles.lookup().findVirtual(Linker.class, "downcallHandle",
MethodType.methodType(MethodHandle.class, FunctionDescriptor.class, Linker.Option[].class)), "Linker::downcallHandle/1" },
{ MethodHandles.lookup().findVirtual(Linker.class, "downcallHandle",
MethodType.methodType(MethodHandle.class, MemorySegment.class, FunctionDescriptor.class, Linker.Option[].class)), "Linker::downcallHandle/2" },
{ MethodHandles.lookup().findVirtual(Linker.class, "upcallStub",
MethodType.methodType(MemorySegment.class, MethodHandle.class, FunctionDescriptor.class, Arena.class, Linker.Option[].class)), "Linker::upcallStub" },
{ MethodHandles.lookup().findVirtual(MemorySegment.class, "reinterpret",
MethodType.methodType(MemorySegment.class, long.class)),
"MemorySegment::reinterpret/1" },