|
26 | 26 |
|
27 | 27 | import static com.oracle.svm.core.util.VMError.guarantee; |
28 | 28 | import static com.oracle.svm.jni.JNIObjectHandles.nullHandle; |
| 29 | +import static com.oracle.svm.jvmtiagentbase.Support.callObjectMethod; |
29 | 30 | import static com.oracle.svm.jvmtiagentbase.Support.check; |
30 | 31 | import static com.oracle.svm.jvmtiagentbase.Support.checkJni; |
31 | 32 | import static com.oracle.svm.jvmtiagentbase.Support.checkNoException; |
|
39 | 40 | import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOr; |
40 | 41 | import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOrNull; |
41 | 42 | import static com.oracle.svm.jvmtiagentbase.Support.getDirectCallerClass; |
| 43 | +import static com.oracle.svm.jvmtiagentbase.Support.getObjectField; |
42 | 44 | import static com.oracle.svm.jvmtiagentbase.Support.getMethodDeclaringClass; |
43 | 45 | import static com.oracle.svm.jvmtiagentbase.Support.getObjectArgument; |
44 | 46 | import static com.oracle.svm.jvmtiagentbase.Support.jniFunctions; |
45 | 47 | import static com.oracle.svm.jvmtiagentbase.Support.jvmtiEnv; |
46 | 48 | import static com.oracle.svm.jvmtiagentbase.Support.jvmtiFunctions; |
| 49 | +import static com.oracle.svm.jvmtiagentbase.Support.newObjectL; |
47 | 50 | import static com.oracle.svm.jvmtiagentbase.Support.testException; |
48 | 51 | import static com.oracle.svm.jvmtiagentbase.Support.toCString; |
49 | 52 | import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_BREAKPOINT; |
|
61 | 64 | import java.util.concurrent.ConcurrentMap; |
62 | 65 | import java.util.concurrent.locks.ReentrantLock; |
63 | 66 |
|
| 67 | +import com.oracle.svm.util.SerializationChecksumCalculator; |
| 68 | +import com.oracle.svm.jni.nativeapi.JNIFieldId; |
64 | 69 | import org.graalvm.compiler.core.common.NumUtil; |
65 | 70 | import org.graalvm.nativeimage.StackValue; |
66 | 71 | import org.graalvm.nativeimage.UnmanagedMemory; |
|
76 | 81 | import org.graalvm.nativeimage.c.type.CTypeConversion; |
77 | 82 | import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder; |
78 | 83 | import org.graalvm.nativeimage.c.type.WordPointer; |
| 84 | +import org.graalvm.word.WordBase; |
79 | 85 | import org.graalvm.word.WordFactory; |
80 | 86 |
|
81 | 87 | import com.oracle.svm.core.c.function.CEntryPointOptions; |
@@ -831,6 +837,121 @@ private static boolean resolveMemberName(JNIEnvironment jni, Breakpoint bp) { |
831 | 837 | return true; |
832 | 838 | } |
833 | 839 |
|
| 840 | + static class CheckSumCalculator extends SerializationChecksumCalculator.JVMCIAgentCalculator { |
| 841 | + private JNIEnvironment jni; |
| 842 | + private Breakpoint bp; |
| 843 | + |
| 844 | + CheckSumCalculator(JNIEnvironment jni, Breakpoint bp) { |
| 845 | + this.jni = jni; |
| 846 | + this.bp = bp; |
| 847 | + } |
| 848 | + |
| 849 | + @Override |
| 850 | + protected WordBase getSuperClass(WordBase clazz) { |
| 851 | + return jniFunctions().getGetSuperclass().invoke(jni, (JNIObjectHandle) clazz); |
| 852 | + } |
| 853 | + |
| 854 | + @Override |
| 855 | + public Long calculateFromComputeDefaultSUID(WordBase clazz) { |
| 856 | + JNIMethodId computeDefaultSUIDMId = agent.handles().getJavaIoObjectStreamClassComputeDefaultSUID(jni, bp.clazz); |
| 857 | + JNIValue args = StackValue.get(1, JNIValue.class); |
| 858 | + args.setObject((JNIObjectHandle) clazz); |
| 859 | + return jniFunctions().getCallStaticLongMethodA().invoke(jni, bp.clazz, computeDefaultSUIDMId, args); |
| 860 | + } |
| 861 | + |
| 862 | + @Override |
| 863 | + protected boolean isClassAbstract(WordBase clazz) { |
| 864 | + CIntPointer modifiers = StackValue.get(CIntPointer.class); |
| 865 | + if (jvmtiFunctions().GetClassModifiers().invoke(jvmtiEnv(), (JNIObjectHandle) clazz, modifiers) != JvmtiError.JVMTI_ERROR_NONE) { |
| 866 | + return false; |
| 867 | + } |
| 868 | + // Checkstyle: allow reflection |
| 869 | + return (modifiers.read() & java.lang.reflect.Modifier.ABSTRACT) != 0; |
| 870 | + } |
| 871 | + |
| 872 | + @Override |
| 873 | + public String getClassName(WordBase clazz) { |
| 874 | + return getClassNameOrNull(jni, (JNIObjectHandle) clazz); |
| 875 | + } |
| 876 | + } |
| 877 | + |
| 878 | + private static boolean objectStreamClassConstructor(JNIEnvironment jni, Breakpoint bp) { |
| 879 | + JNIObjectHandle serializeTargetClass = getObjectArgument(1); |
| 880 | + String serializeTargetClassName = getClassNameOrNull(jni, serializeTargetClass); |
| 881 | + long checksum = 0; |
| 882 | + List<SerializationInfo> traceCandidates = new ArrayList<>(); |
| 883 | + CheckSumCalculator checkSumCalculator = new CheckSumCalculator(jni, bp); |
| 884 | + JNIObjectHandle objectStreamClassInstance = newObjectL(jni, bp.clazz, bp.method, serializeTargetClass); |
| 885 | + Object result = nullHandle().notEqual(objectStreamClassInstance); |
| 886 | + if (clearException(jni)) { |
| 887 | + result = false; |
| 888 | + } |
| 889 | + // Skip Lambda class serialization |
| 890 | + if (serializeTargetClassName.contains("$$Lambda$")) { |
| 891 | + return true; |
| 892 | + } |
| 893 | + if (result.equals(true)) { |
| 894 | + checksum = checkSumCalculator.calculateChecksum(getConsClassName(jni, bp.clazz, objectStreamClassInstance), serializeTargetClassName, serializeTargetClass); |
| 895 | + } |
| 896 | + traceCandidates.add(new SerializationInfo(serializeTargetClassName, checksum)); |
| 897 | + |
| 898 | + /** |
| 899 | + * When the ObjectStreamClass instance is created for the given serializeTargetClass, some |
| 900 | + * additional ObjectStreamClass instances (usually the super classes) are created |
| 901 | + * recursively. Call ObjectStreamClass.getClassDataLayout0() can get all of them. |
| 902 | + */ |
| 903 | + JNIMethodId getClassDataLayout0MId = agent.handles().getJavaIoObjectStreamClassGetClassDataLayout0(jni, bp.clazz); |
| 904 | + JNIObjectHandle dataLayoutArray = callObjectMethod(jni, objectStreamClassInstance, getClassDataLayout0MId); |
| 905 | + if (!clearException(jni) && nullHandle().notEqual(dataLayoutArray)) { |
| 906 | + int length = jniFunctions().getGetArrayLength().invoke(jni, dataLayoutArray); |
| 907 | + // If only 1 element is got from getClassDataLayout0(). it is base ObjectStreamClass |
| 908 | + // instance itself. |
| 909 | + if (!clearException(jni) && length > 1) { |
| 910 | + JNIFieldId hasDataFId = agent.handles().getJavaIOObjectStreamClassClassDataSlotHasData(jni); |
| 911 | + JNIFieldId descFId = agent.handles().getJavaIOObjectStreamClassClassDataSlotDesc(jni); |
| 912 | + JNIMethodId javaIoObjectStreamClassForClassMId = agent.handles().getJavaIoObjectStreamClassForClass(jni, bp.clazz); |
| 913 | + for (int i = 0; i < length; i++) { |
| 914 | + JNIObjectHandle classDataSlot = jniFunctions().getGetObjectArrayElement().invoke(jni, dataLayoutArray, i); |
| 915 | + boolean hasData = jniFunctions().getGetBooleanField().invoke(jni, classDataSlot, hasDataFId); |
| 916 | + if (hasData) { |
| 917 | + JNIObjectHandle oscInstanceInSlot = jniFunctions().getGetObjectField().invoke(jni, classDataSlot, descFId); |
| 918 | + if (!jniFunctions().getIsSameObject().invoke(jni, oscInstanceInSlot, objectStreamClassInstance)) { |
| 919 | + JNIObjectHandle oscClazz = callObjectMethod(jni, oscInstanceInSlot, javaIoObjectStreamClassForClassMId); |
| 920 | + String oscClassName = getClassNameOrNull(jni, oscClazz); |
| 921 | + traceCandidates.add(new SerializationInfo(oscClassName, |
| 922 | + checkSumCalculator.calculateChecksum(getConsClassName(jni, |
| 923 | + bp.clazz, oscInstanceInSlot), oscClassName, oscClazz))); |
| 924 | + } |
| 925 | + } |
| 926 | + } |
| 927 | + } |
| 928 | + } |
| 929 | + for (SerializationInfo serializationInfo : traceCandidates) { |
| 930 | + if (traceWriter != null) { |
| 931 | + traceWriter.traceCall("serialization", |
| 932 | + "ObjectStreamClass.<init>", |
| 933 | + null, |
| 934 | + null, |
| 935 | + null, |
| 936 | + result, |
| 937 | + // serializeTargetClassName, checksum); |
| 938 | + serializationInfo.className, serializationInfo.checksum); |
| 939 | + guarantee(!testException(jni)); |
| 940 | + } |
| 941 | + } |
| 942 | + return true; |
| 943 | + } |
| 944 | + |
| 945 | + private static String getConsClassName(JNIEnvironment jni, JNIObjectHandle objectStreamClassClazz, JNIObjectHandle objectStreamClassInstance) { |
| 946 | + JNIObjectHandle cons = getObjectField(jni, objectStreamClassClazz, objectStreamClassInstance, "cons", "Ljava/lang/reflect/Constructor;"); |
| 947 | + String targetConstructorClassName = ""; |
| 948 | + if (nullHandle().notEqual(cons)) { |
| 949 | + // Compute hashcode from the first unserializable superclass |
| 950 | + targetConstructorClassName = getClassNameOrNull(jni, callObjectMethod(jni, cons, agent.handles().javaLangReflectMemberGetDeclaringClass)); |
| 951 | + } |
| 952 | + return targetConstructorClassName; |
| 953 | + } |
| 954 | + |
834 | 955 | @CEntryPoint |
835 | 956 | @CEntryPointOptions(prologue = AgentIsolate.Prologue.class) |
836 | 957 | private static void onBreakpoint(@SuppressWarnings("unused") JvmtiEnv jvmti, JNIEnvironment jni, |
@@ -1135,6 +1256,7 @@ private interface BreakpointHandler { |
1135 | 1256 | brk("java/lang/reflect/Proxy", "newProxyInstance", |
1136 | 1257 | "(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;", BreakpointInterceptor::newProxyInstance), |
1137 | 1258 |
|
| 1259 | + brk("java/io/ObjectStreamClass", "<init>", "(Ljava/lang/Class;)V", BreakpointInterceptor::objectStreamClassConstructor), |
1138 | 1260 | optionalBrk("java/util/ResourceBundle", |
1139 | 1261 | "getBundleImpl", |
1140 | 1262 | "(Ljava/lang/String;Ljava/util/Locale;Ljava/lang/ClassLoader;Ljava/util/ResourceBundle$Control;)Ljava/util/ResourceBundle;", |
@@ -1264,6 +1386,16 @@ public int hashCode() { |
1264 | 1386 | } |
1265 | 1387 | } |
1266 | 1388 |
|
| 1389 | + private static final class SerializationInfo { |
| 1390 | + private String className; |
| 1391 | + private long checksum; |
| 1392 | + |
| 1393 | + SerializationInfo(String serializeTargetClassName, long checksum) { |
| 1394 | + this.className = serializeTargetClassName; |
| 1395 | + this.checksum = checksum; |
| 1396 | + } |
| 1397 | + } |
| 1398 | + |
1267 | 1399 | private BreakpointInterceptor() { |
1268 | 1400 | } |
1269 | 1401 | } |
0 commit comments