|
37 | 37 | import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOr; |
38 | 38 | import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOrNull; |
39 | 39 | import static com.oracle.svm.jvmtiagentbase.Support.getDirectCallerClass; |
| 40 | +import static com.oracle.svm.jvmtiagentbase.Support.getIntArgument; |
40 | 41 | import static com.oracle.svm.jvmtiagentbase.Support.getMethodDeclaringClass; |
41 | 42 | import static com.oracle.svm.jvmtiagentbase.Support.getObjectArgument; |
42 | 43 | import static com.oracle.svm.jvmtiagentbase.Support.jniFunctions; |
|
49 | 50 | import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_NATIVE_METHOD_BIND; |
50 | 51 | import static org.graalvm.word.WordFactory.nullPointer; |
51 | 52 |
|
| 53 | +import java.lang.reflect.Modifier; |
52 | 54 | import java.nio.ByteBuffer; |
53 | 55 | import java.nio.ByteOrder; |
54 | 56 | import java.util.ArrayList; |
|
81 | 83 | import com.oracle.svm.agent.restrict.ProxyAccessVerifier; |
82 | 84 | import com.oracle.svm.agent.restrict.ReflectAccessVerifier; |
83 | 85 | import com.oracle.svm.agent.restrict.ResourceAccessVerifier; |
| 86 | +import com.oracle.svm.agent.restrict.SerializationAccessVerifier; |
84 | 87 | import com.oracle.svm.configure.config.ConfigurationMethod; |
85 | 88 | import com.oracle.svm.core.c.function.CEntryPointOptions; |
86 | 89 | import com.oracle.svm.core.util.VMError; |
87 | 90 | import com.oracle.svm.jni.nativeapi.JNIEnvironment; |
| 91 | +import com.oracle.svm.jni.nativeapi.JNIFunctionPointerTypes; |
88 | 92 | import com.oracle.svm.jni.nativeapi.JNIMethodId; |
89 | 93 | import com.oracle.svm.jni.nativeapi.JNINativeMethod; |
90 | 94 | import com.oracle.svm.jni.nativeapi.JNIObjectHandle; |
@@ -129,6 +133,7 @@ final class BreakpointInterceptor { |
129 | 133 | private static ReflectAccessVerifier accessVerifier; |
130 | 134 | private static ProxyAccessVerifier proxyVerifier; |
131 | 135 | private static ResourceAccessVerifier resourceVerifier; |
| 136 | + private static SerializationAccessVerifier serializationAccessVerifier; |
132 | 137 | private static NativeImageAgent agent; |
133 | 138 |
|
134 | 139 | private static Map<Long, Breakpoint> installedBreakpoints; |
@@ -913,6 +918,119 @@ private static String asInternalSignature(Object paramTypesArray) { |
913 | 918 | return null; |
914 | 919 | } |
915 | 920 |
|
| 921 | + @SuppressWarnings("unused") |
| 922 | + private static boolean generateSerializationConstructor(JNIEnvironment jni, Breakpoint bp) { |
| 923 | + JNIObjectHandle self = getObjectArgument(0); |
| 924 | + JNIObjectHandle serializeTargetClass = getObjectArgument(1); |
| 925 | + String serializeTargetClassName = getClassNameOrNull(jni, serializeTargetClass); |
| 926 | + JNIObjectHandle parameterTypes = getObjectArgument(2); |
| 927 | + Object parameterTypeNames = getClassArrayNames(jni, parameterTypes); |
| 928 | + JNIObjectHandle checkedExceptions = getObjectArgument(3); |
| 929 | + Object checkedExceptionNames = getClassArrayNames(jni, checkedExceptions); |
| 930 | + int modifiers = getIntArgument(4); |
| 931 | + JNIObjectHandle targetConstructorClass = getObjectArgument(5); |
| 932 | + String targetConstructorClassName = getClassNameOrNull(jni, targetConstructorClass); |
| 933 | + boolean allowed = (serializationAccessVerifier == null || |
| 934 | + serializationAccessVerifier.verifyGenerateSerializationConstructor(jni, serializeTargetClassName, parameterTypeNames, |
| 935 | + checkedExceptionNames, modifiers, targetConstructorClassName)); |
| 936 | + Object result = false; |
| 937 | + if (allowed) { |
| 938 | + JNIValue args = StackValue.get(6, JNIValue.class); |
| 939 | + args.addressOf(0).setObject(self); |
| 940 | + args.addressOf(1).setObject(serializeTargetClass); |
| 941 | + args.addressOf(2).setObject(parameterTypes); |
| 942 | + args.addressOf(3).setObject(checkedExceptions); |
| 943 | + args.addressOf(4).setInt(modifiers); |
| 944 | + args.addressOf(5).setObject(targetConstructorClass); |
| 945 | + result = nullHandle().notEqual(jniFunctions().getCallObjectMethodA().invoke(jni, bp.clazz, bp.method, args)); |
| 946 | + if (clearException(jni)) { |
| 947 | + result = false; |
| 948 | + } |
| 949 | + } |
| 950 | + JNIObjectHandle callerClass = getDirectCallerClass(); |
| 951 | + if (traceWriter != null) { |
| 952 | + traceWriter.traceCall("serialization", |
| 953 | + "generateSerializationConstructor", |
| 954 | + null, |
| 955 | + null, |
| 956 | + null, |
| 957 | + result, |
| 958 | + serializeTargetClassName, parameterTypeNames, |
| 959 | + checkedExceptionNames, modifiers, targetConstructorClassName); |
| 960 | + JNIFunctionPointerTypes.CallIntMethodFunctionPointer noArgRetIntCall = jniFunctions().getCallIntMethod(); |
| 961 | + int privateStaticFinalMask = Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL; |
| 962 | + int staticFinalMask = Modifier.STATIC | Modifier.FINAL; |
| 963 | + |
| 964 | + // call serializationTargetClass.getDeclaredFields(); |
| 965 | + JNIObjectHandle javaLangClass = agent.handles().findClass(jni, "java/lang/Class"); |
| 966 | + JNIMethodId getDeclaredFieldsMI = agent.handles().getMethodId(jni, javaLangClass, "getDeclaredFields", |
| 967 | + "()[Ljava/lang/reflect/Field;", false); |
| 968 | + JNIFunctionPointerTypes.CallObjectMethod0FunctionPointer noArgRetObjectCall = jniFunctions().getCallObjectMethod(); |
| 969 | + JNIObjectHandle fieldsJArray = noArgRetObjectCall.invoke(jni, serializeTargetClass, getDeclaredFieldsMI); |
| 970 | + |
| 971 | + // Prepare JNIMethodIds for later calls |
| 972 | + JNIObjectHandle javaLangReflectField = agent.handles().findClass(jni, "java/lang/reflect/Field"); |
| 973 | + JNIMethodId getFieldNameId = agent.handles().getMethodId(jni, javaLangReflectField, "getName", "()Ljava/lang/String;", false); |
| 974 | + JNIMethodId getFieldModifiersId = agent.handles().getMethodId(jni, javaLangReflectField, "getModifiers", "()I", false); |
| 975 | + JNIMethodId getFieldTypeId = agent.handles().getMethodId(jni, javaLangReflectField, "getType", "()Ljava/lang/Class;", false); |
| 976 | + // Add serialize and deserialize fields into reflection configs |
| 977 | + // Check each field |
| 978 | + int fieldArrayLength = jniFunctions().getGetArrayLength().invoke(jni, fieldsJArray); |
| 979 | + for (int i = 0; i < fieldArrayLength; i++) { |
| 980 | + // Get field object from array |
| 981 | + JNIObjectHandle field = jniFunctions().getGetObjectArrayElement().invoke(jni, fieldsJArray, i); |
| 982 | + // call field.getName() to get field's name |
| 983 | + String fieldName = fromJniString(jni, noArgRetObjectCall.invoke(jni, field, getFieldNameId)); |
| 984 | + // call field.getModifiers tp get field's modifiers |
| 985 | + int fieldModifiers = noArgRetIntCall.invoke(jni, field, getFieldModifiersId); |
| 986 | + if (fieldName.equals("serialPersistentFields") && |
| 987 | + (fieldModifiers & privateStaticFinalMask) == privateStaticFinalMask) { |
| 988 | + traceWriter.traceCall("reflect", |
| 989 | + "getDeclaredField", |
| 990 | + serializeTargetClassName, |
| 991 | + null, |
| 992 | + null, |
| 993 | + result, "serialPersistentFields"); |
| 994 | + } else if (fieldName.equals("serialVersionUID") && |
| 995 | + (fieldModifiers & staticFinalMask) == staticFinalMask) { |
| 996 | + traceWriter.traceCall("reflect", |
| 997 | + "getDeclaredField", |
| 998 | + serializeTargetClassName, |
| 999 | + null, |
| 1000 | + null, |
| 1001 | + result, "serialVersionUID"); |
| 1002 | + } else if ((fieldModifiers & staticFinalMask) != staticFinalMask) { |
| 1003 | + // Set the field's allowWrite and unsafeAccess properties |
| 1004 | + traceWriter.traceCall("reflect", |
| 1005 | + "getDeclaredField", |
| 1006 | + serializeTargetClassName, |
| 1007 | + null, |
| 1008 | + null, |
| 1009 | + result, (modifiers & Modifier.FINAL) == Modifier.FINAL, (modifiers & Modifier.STATIC) == 0, fieldName); |
| 1010 | + } |
| 1011 | + // Add field's class in config |
| 1012 | + // call field.getType() |
| 1013 | + JNIObjectHandle fieldClass = noArgRetObjectCall.invoke(jni, field, getFieldTypeId); |
| 1014 | + traceWriter.traceCall("reflect", |
| 1015 | + "forName", |
| 1016 | + serializeTargetClassName, |
| 1017 | + null, |
| 1018 | + null, |
| 1019 | + result, getClassNameOrNull(jni, fieldClass)); |
| 1020 | + } |
| 1021 | + guarantee(!testException(jni)); |
| 1022 | + } |
| 1023 | + |
| 1024 | + if (!allowed) { |
| 1025 | + try (CCharPointerHolder message = toCString( |
| 1026 | + NativeImageAgent.MESSAGE_PREFIX + "configuration does not permit SerializationConstructorAccessor class for class: " + serializeTargetClassName + |
| 1027 | + " with first unserializable super class " + targetConstructorClassName)) { |
| 1028 | + jniFunctions().getThrowNew().invoke(jni, agent.handles().javaLangSecurityException, message.get()); |
| 1029 | + } |
| 1030 | + } |
| 1031 | + return allowed; |
| 1032 | + } |
| 1033 | + |
916 | 1034 | @CEntryPoint |
917 | 1035 | @CEntryPointOptions(prologue = AgentIsolate.Prologue.class) |
918 | 1036 | private static void onBreakpoint(@SuppressWarnings("unused") JvmtiEnv jvmti, JNIEnvironment jni, |
@@ -991,12 +1109,14 @@ private static void installBreakpointIfClassLoader(JNIEnvironment jni, JNIObject |
991 | 1109 | JvmtiEnv.class, JNIEnvironment.class, JNIObjectHandle.class, JNIObjectHandle.class); |
992 | 1110 |
|
993 | 1111 | public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, TraceWriter writer, ReflectAccessVerifier verifier, |
994 | | - ProxyAccessVerifier prverifier, ResourceAccessVerifier resverifier, NativeImageAgent nativeImageTracingAgent, boolean exptlClassLoaderSupport) { |
| 1112 | + ProxyAccessVerifier prverifier, ResourceAccessVerifier resverifier, SerializationAccessVerifier serializationAccessVerifier, NativeImageAgent nativeImageTracingAgent, |
| 1113 | + boolean exptlClassLoaderSupport) { |
995 | 1114 |
|
996 | 1115 | BreakpointInterceptor.traceWriter = writer; |
997 | 1116 | BreakpointInterceptor.accessVerifier = verifier; |
998 | 1117 | BreakpointInterceptor.proxyVerifier = prverifier; |
999 | 1118 | BreakpointInterceptor.resourceVerifier = resverifier; |
| 1119 | + BreakpointInterceptor.serializationAccessVerifier = serializationAccessVerifier; |
1000 | 1120 | BreakpointInterceptor.agent = nativeImageTracingAgent; |
1001 | 1121 | BreakpointInterceptor.experimentalClassLoaderSupport = exptlClassLoaderSupport; |
1002 | 1122 |
|
@@ -1218,6 +1338,9 @@ private interface BreakpointHandler { |
1218 | 1338 | brk("java/lang/reflect/Proxy", "newProxyInstance", |
1219 | 1339 | "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;", BreakpointInterceptor::newProxyInstance), |
1220 | 1340 |
|
| 1341 | + brk("sun/reflect/MethodAccessorGenerator", "generateSerializationConstructor", |
| 1342 | + "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;ILjava/lang/Class;)Lsun/reflect/SerializationConstructorAccessorImpl;", |
| 1343 | + BreakpointInterceptor::generateSerializationConstructor), |
1221 | 1344 | optionalBrk("java/util/ResourceBundle", |
1222 | 1345 | "getBundleImpl", |
1223 | 1346 | "(Ljava/lang/String;Ljava/util/Locale;Ljava/lang/ClassLoader;Ljava/util/ResourceBundle$Control;)Ljava/util/ResourceBundle;", |
|
0 commit comments