From ba9714d44ceabdb98078a4338fb8e8a3e22adcbe Mon Sep 17 00:00:00 2001 From: Maurizio Cimadamore Date: Thu, 11 May 2023 09:45:45 +0000 Subject: [PATCH] 8307610: Linker::nativeLinker should not be restricted (mainline) Reviewed-by: jvernee --- .../classes/java/lang/foreign/Linker.java | 34 ++++++++++++------- .../internal/foreign/abi/AbstractLinker.java | 23 +++++++++++-- .../foreigntest/PanamaMainUnnamedModule.java | 22 +++++------- .../org/openjdk/foreigntest/PanamaMain.java | 6 ++-- .../openjdk/foreigntest/PanamaMainDirect.java | 6 ++-- .../openjdk/foreigntest/PanamaMainInvoke.java | 11 +++--- .../openjdk/foreigntest/PanamaMainJNI.java | 11 +++--- .../foreigntest/PanamaMainReflection.java | 10 +++--- .../foreigntest/libLinkerInvokerModule.cpp | 31 +++++++++++------ .../handle/invoker/MethodHandleInvoker.java | 11 +++--- .../handle/lookup/MethodHandleLookup.java | 9 +++-- 11 files changed, 111 insertions(+), 63 deletions(-) diff --git a/src/java.base/share/classes/java/lang/foreign/Linker.java b/src/java.base/share/classes/java/lang/foreign/Linker.java index 71ef4200eeb..ed416cf1eb1 100644 --- a/src/java.base/share/classes/java/lang/foreign/Linker.java +++ b/src/java.base/share/classes/java/lang/foreign/Linker.java @@ -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. - *

- * This method is restricted. - * 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); * } + *

+ * This method is restricted. + * 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}. + *

+ * This method is restricted. + * 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. + *

+ * This method is restricted. + * 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); /** diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index f7a2b9114c1..1c6ee128c2b 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -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 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); diff --git a/test/jdk/java/foreign/enablenativeaccess/org/openjdk/foreigntest/PanamaMainUnnamedModule.java b/test/jdk/java/foreign/enablenativeaccess/org/openjdk/foreigntest/PanamaMainUnnamedModule.java index a6667d1240f..a9bcb929a25 100644 --- a/test/jdk/java/foreign/enablenativeaccess/org/openjdk/foreigntest/PanamaMainUnnamedModule.java +++ b/test/jdk/java/foreign/enablenativeaccess/org/openjdk/foreigntest/PanamaMainUnnamedModule.java @@ -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() { diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMain.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMain.java index f6792133a91..484341b2094 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMain.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMain.java @@ -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"); } } diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java index 71a8a6bb58e..9e63af039d6 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainDirect.java @@ -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() { diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java index ef75fa822c0..991d4118afc 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainInvoke.java @@ -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 { diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainJNI.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainJNI.java index cc408e2d432..164ee5852cc 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainJNI.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainJNI.java @@ -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); } diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java index a32f3edca06..1db42d42be2 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/PanamaMainReflection.java @@ -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 { diff --git a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/libLinkerInvokerModule.cpp b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/libLinkerInvokerModule.cpp index 3358c5e3a22..474738a584d 100644 --- a/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/libLinkerInvokerModule.cpp +++ b/test/jdk/java/foreign/enablenativeaccess/panama_module/org/openjdk/foreigntest/libLinkerInvokerModule.cpp @@ -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); } } diff --git a/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java b/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java index dc212d51d09..9b696caa82d 100644 --- a/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java +++ b/test/jdk/java/foreign/handles/invoker_module/handle/invoker/MethodHandleInvoker.java @@ -59,7 +59,7 @@ public class MethodHandleInvoker { static final Map, Object> DEFAULT_VALUES = new HashMap<>(); - static void addDefaultMapping(Class 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 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); } } diff --git a/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java b/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java index 4042e93e771..ed916c1fe24 100644 --- a/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java +++ b/test/jdk/java/foreign/handles/lookup_module/handle/lookup/MethodHandleLookup.java @@ -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" },