Skip to content

Commit b83c4e0

Browse files
committed
Support JDK serialization
1 parent 19a1421 commit b83c4e0

File tree

30 files changed

+976
-45
lines changed

30 files changed

+976
-45
lines changed

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOr;
3838
import static com.oracle.svm.jvmtiagentbase.Support.getClassNameOrNull;
3939
import static com.oracle.svm.jvmtiagentbase.Support.getDirectCallerClass;
40+
import static com.oracle.svm.jvmtiagentbase.Support.getIntArgument;
4041
import static com.oracle.svm.jvmtiagentbase.Support.getMethodDeclaringClass;
4142
import static com.oracle.svm.jvmtiagentbase.Support.getObjectArgument;
4243
import static com.oracle.svm.jvmtiagentbase.Support.jniFunctions;
@@ -49,6 +50,7 @@
4950
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_NATIVE_METHOD_BIND;
5051
import static org.graalvm.word.WordFactory.nullPointer;
5152

53+
import java.lang.reflect.Modifier;
5254
import java.nio.ByteBuffer;
5355
import java.nio.ByteOrder;
5456
import java.util.ArrayList;
@@ -81,10 +83,12 @@
8183
import com.oracle.svm.agent.restrict.ProxyAccessVerifier;
8284
import com.oracle.svm.agent.restrict.ReflectAccessVerifier;
8385
import com.oracle.svm.agent.restrict.ResourceAccessVerifier;
86+
import com.oracle.svm.agent.restrict.SerializationAccessVerifier;
8487
import com.oracle.svm.configure.config.ConfigurationMethod;
8588
import com.oracle.svm.core.c.function.CEntryPointOptions;
8689
import com.oracle.svm.core.util.VMError;
8790
import com.oracle.svm.jni.nativeapi.JNIEnvironment;
91+
import com.oracle.svm.jni.nativeapi.JNIFunctionPointerTypes;
8892
import com.oracle.svm.jni.nativeapi.JNIMethodId;
8993
import com.oracle.svm.jni.nativeapi.JNINativeMethod;
9094
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;
@@ -129,6 +133,7 @@ final class BreakpointInterceptor {
129133
private static ReflectAccessVerifier accessVerifier;
130134
private static ProxyAccessVerifier proxyVerifier;
131135
private static ResourceAccessVerifier resourceVerifier;
136+
private static SerializationAccessVerifier serializationAccessVerifier;
132137
private static NativeImageAgent agent;
133138

134139
private static Map<Long, Breakpoint> installedBreakpoints;
@@ -913,6 +918,59 @@ private static String asInternalSignature(Object paramTypesArray) {
913918
return null;
914919
}
915920

921+
@SuppressWarnings("unused")
922+
private static boolean generateSerializationConstructor(JNIEnvironment jni, Breakpoint bp) {
923+
JNIObjectHandle callerClass = getDirectCallerClass();
924+
JNIObjectHandle self = getObjectArgument(0);
925+
JNIObjectHandle serializeTargetClass = getObjectArgument(1);
926+
String serializeTargetClassName = getClassNameOrNull(jni, serializeTargetClass);
927+
JNIObjectHandle parameterTypes = getObjectArgument(2);
928+
Object parameterTypeNames = getClassArrayNames(jni, parameterTypes);
929+
JNIObjectHandle checkedExceptions = getObjectArgument(3);
930+
Object checkedExceptionNames = getClassArrayNames(jni, checkedExceptions);
931+
int modifiers = getIntArgument(4);
932+
JNIObjectHandle targetConstructorClass = getObjectArgument(5);
933+
String targetConstructorClassName = getClassNameOrNull(jni, targetConstructorClass);
934+
boolean allowed = (serializationAccessVerifier == null || (parameterTypeNames instanceof String[] && checkedExceptionNames instanceof String[] &&
935+
serializationAccessVerifier.verifyGenerateSerializationConstructor(jni, serializeTargetClassName, (String[]) parameterTypeNames,
936+
(String[]) checkedExceptionNames, modifiers, targetConstructorClassName, self, callerClass)));
937+
Object result = false;
938+
if (allowed) {
939+
JNIValue args = StackValue.get(6, JNIValue.class);
940+
args.addressOf(0).setObject(self);
941+
args.addressOf(1).setObject(serializeTargetClass);
942+
args.addressOf(2).setObject(parameterTypes);
943+
args.addressOf(3).setObject(checkedExceptions);
944+
args.addressOf(4).setInt(modifiers);
945+
args.addressOf(5).setObject(targetConstructorClass);
946+
result = nullHandle().notEqual(jniFunctions().getCallObjectMethodA().invoke(jni, bp.clazz, bp.method, args));
947+
if (clearException(jni)) {
948+
result = false;
949+
}
950+
}
951+
952+
if (traceWriter != null) {
953+
traceWriter.traceCall("serialization",
954+
"generateSerializationConstructor",
955+
null,
956+
null,
957+
null,
958+
result,
959+
serializeTargetClassName, parameterTypeNames,
960+
checkedExceptionNames, modifiers, targetConstructorClassName);
961+
guarantee(!testException(jni));
962+
}
963+
964+
if (!allowed) {
965+
try (CCharPointerHolder message = toCString(
966+
NativeImageAgent.MESSAGE_PREFIX + "configuration does not permit SerializationConstructorAccessor class for class: " + serializeTargetClassName +
967+
" with first unserializable super class " + targetConstructorClassName)) {
968+
jniFunctions().getThrowNew().invoke(jni, agent.handles().javaLangSecurityException, message.get());
969+
}
970+
}
971+
return allowed;
972+
}
973+
916974
@CEntryPoint
917975
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
918976
private static void onBreakpoint(@SuppressWarnings("unused") JvmtiEnv jvmti, JNIEnvironment jni,
@@ -991,12 +1049,14 @@ private static void installBreakpointIfClassLoader(JNIEnvironment jni, JNIObject
9911049
JvmtiEnv.class, JNIEnvironment.class, JNIObjectHandle.class, JNIObjectHandle.class);
9921050

9931051
public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, TraceWriter writer, ReflectAccessVerifier verifier,
994-
ProxyAccessVerifier prverifier, ResourceAccessVerifier resverifier, NativeImageAgent nativeImageTracingAgent, boolean exptlClassLoaderSupport) {
1052+
ProxyAccessVerifier prverifier, ResourceAccessVerifier resverifier, SerializationAccessVerifier serializationAccessVerifier, NativeImageAgent nativeImageTracingAgent,
1053+
boolean exptlClassLoaderSupport) {
9951054

9961055
BreakpointInterceptor.traceWriter = writer;
9971056
BreakpointInterceptor.accessVerifier = verifier;
9981057
BreakpointInterceptor.proxyVerifier = prverifier;
9991058
BreakpointInterceptor.resourceVerifier = resverifier;
1059+
BreakpointInterceptor.serializationAccessVerifier = serializationAccessVerifier;
10001060
BreakpointInterceptor.agent = nativeImageTracingAgent;
10011061
BreakpointInterceptor.experimentalClassLoaderSupport = exptlClassLoaderSupport;
10021062

@@ -1218,6 +1278,13 @@ private interface BreakpointHandler {
12181278
brk("java/lang/reflect/Proxy", "newProxyInstance",
12191279
"(Ljava/lang/ClassLoader;[Ljava/lang/Class;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;", BreakpointInterceptor::newProxyInstance),
12201280

1281+
optionalBrk("sun/reflect/MethodAccessorGenerator", "generateSerializationConstructor",
1282+
"(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;ILjava/lang/Class;)Lsun/reflect/SerializationConstructorAccessorImpl;",
1283+
BreakpointInterceptor::generateSerializationConstructor),
1284+
// MethodAccessorGenerator has been moved to jdk/internal/reflect since JDK9
1285+
optionalBrk("jdk/internal/reflect/MethodAccessorGenerator", "generateSerializationConstructor",
1286+
"(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;ILjava/lang/Class;)Ljdk/internal/reflect/SerializationConstructorAccessorImpl;",
1287+
BreakpointInterceptor::generateSerializationConstructor),
12211288
optionalBrk("java/util/ResourceBundle",
12221289
"getBundleImpl",
12231290
"(Ljava/lang/String;Ljava/util/Locale;Ljava/lang/ClassLoader;Ljava/util/ResourceBundle$Control;)Ljava/util/ResourceBundle;",

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgent.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import java.util.function.Function;
5454
import java.util.regex.Pattern;
5555

56+
import com.oracle.svm.agent.restrict.SerializationAccessVerifier;
5657
import org.graalvm.compiler.options.OptionKey;
5758
import org.graalvm.nativeimage.ProcessProperties;
5859

@@ -92,6 +93,7 @@ public final class NativeImageAgent extends JvmtiAgentBase<NativeImageAgentJNIHa
9293
private static final String oHReflectionConfigurationResources = oH(ConfigurationFiles.Options.ReflectionConfigurationResources);
9394
private static final String oHDynamicProxyConfigurationResources = oH(ConfigurationFiles.Options.DynamicProxyConfigurationResources);
9495
private static final String oHResourceConfigurationResources = oH(ConfigurationFiles.Options.ResourceConfigurationResources);
96+
private static final String oHSerializationConfigurationResources = oH(ConfigurationFiles.Options.SerializationConfigurationResources);
9597
private static final String oHConfigurationResourceRoots = oH(ConfigurationFiles.Options.ConfigurationResourceRoots);
9698

9799
private static <T> String oH(OptionKey<T> option) {
@@ -256,7 +258,7 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
256258
// They should use the same filter sets, however.
257259
AccessAdvisor advisor = createAccessAdvisor(builtinHeuristicFilter, callerFilter, accessFilter);
258260
TraceProcessor processor = new TraceProcessor(advisor, mergeConfigs.loadJniConfig(handler), mergeConfigs.loadReflectConfig(handler),
259-
mergeConfigs.loadProxyConfig(handler), mergeConfigs.loadResourceConfig(handler));
261+
mergeConfigs.loadProxyConfig(handler), mergeConfigs.loadResourceConfig(handler), mergeConfigs.loadSerializationConfig(handler));
260262
traceWriter = new TraceProcessorWriterAdapter(processor);
261263
} catch (Throwable t) {
262264
System.err.println(MESSAGE_PREFIX + t);
@@ -298,7 +300,11 @@ protected int onLoadCallback(JNIJavaVM vm, JvmtiEnv jvmti, JvmtiEventCallbacks c
298300
if (!restrictConfigs.getResourceConfigPaths().isEmpty()) {
299301
resourceVerifier = new ResourceAccessVerifier(restrictConfigs.loadResourceConfig(ConfigurationSet.FAIL_ON_EXCEPTION), accessAdvisor);
300302
}
301-
BreakpointInterceptor.onLoad(jvmti, callbacks, traceWriter, verifier, proxyVerifier, resourceVerifier, this, experimentalClassLoaderSupport);
303+
SerializationAccessVerifier serializationAccessVerifier = null;
304+
if (!restrictConfigs.getSerializationConfigPaths().isEmpty()) {
305+
serializationAccessVerifier = new SerializationAccessVerifier(restrictConfigs.loadSerializationConfig(ConfigurationSet.FAIL_ON_EXCEPTION), accessAdvisor);
306+
}
307+
BreakpointInterceptor.onLoad(jvmti, callbacks, traceWriter, verifier, proxyVerifier, resourceVerifier, serializationAccessVerifier, this, experimentalClassLoaderSupport);
302308
} catch (Throwable t) {
303309
System.err.println(MESSAGE_PREFIX + t);
304310
return 3;
@@ -439,12 +445,15 @@ private static boolean addRestrictConfigs(JvmtiEnv jvmti, ConfigurationSet restr
439445
addURI.add(restrictConfigs.getProxyConfigPaths(), cpEntry, optionParts[1]);
440446
} else if (oHResourceConfigurationResources.equals(argName)) {
441447
addURI.add(restrictConfigs.getResourceConfigPaths(), cpEntry, optionParts[1]);
448+
} else if (oHSerializationConfigurationResources.equals(argName)) {
449+
addURI.add(restrictConfigs.getSerializationConfigPaths(), cpEntry, optionParts[1]);
442450
} else if (oHConfigurationResourceRoots.equals(argName)) {
443451
String resourceLocation = optionParts[1];
444452
addURI.add(restrictConfigs.getJniConfigPaths(), cpEntry, resourceLocation + "/" + ConfigurationFiles.JNI_NAME);
445453
addURI.add(restrictConfigs.getReflectConfigPaths(), cpEntry, resourceLocation + "/" + ConfigurationFiles.REFLECTION_NAME);
446454
addURI.add(restrictConfigs.getProxyConfigPaths(), cpEntry, resourceLocation + "/" + ConfigurationFiles.DYNAMIC_PROXY_NAME);
447455
addURI.add(restrictConfigs.getResourceConfigPaths(), cpEntry, resourceLocation + "/" + ConfigurationFiles.RESOURCES_NAME);
456+
addURI.add(restrictConfigs.getSerializationConfigPaths(), cpEntry, resourceLocation + "/" + ConfigurationFiles.SERIALIZATION_NAME);
448457
}
449458
}
450459
});
@@ -557,6 +566,7 @@ private void writeConfigurationFiles() {
557566
allConfigFiles.put(ConfigurationFiles.JNI_NAME, p.getJniConfiguration());
558567
allConfigFiles.put(ConfigurationFiles.DYNAMIC_PROXY_NAME, p.getProxyConfiguration());
559568
allConfigFiles.put(ConfigurationFiles.RESOURCES_NAME, p.getResourceConfiguration());
569+
allConfigFiles.put(ConfigurationFiles.SERIALIZATION_NAME, p.getSerializationConfiguration());
560570

561571
for (Map.Entry<String, JsonPrintable> configFile : allConfigFiles.entrySet()) {
562572
Path tempPath = tempDirectory.resolve(configFile.getKey());
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
package com.oracle.svm.agent.restrict;
27+
28+
import com.oracle.svm.configure.config.SerializationConfiguration;
29+
import com.oracle.svm.configure.trace.AccessAdvisor;
30+
import com.oracle.svm.jni.nativeapi.JNIEnvironment;
31+
import com.oracle.svm.jni.nativeapi.JNIObjectHandle;
32+
33+
public class SerializationAccessVerifier extends AbstractAccessVerifier {
34+
private final SerializationConfiguration configuration;
35+
36+
public SerializationAccessVerifier(SerializationConfiguration configuration, AccessAdvisor advisor) {
37+
super(advisor);
38+
this.configuration = configuration;
39+
}
40+
41+
public boolean verifyGenerateSerializationConstructor(JNIEnvironment env, String serializationTargetClass, String[] parameterTypes, String[] checkedExceptions,
42+
int modifiers, String targetConstructorClass, JNIObjectHandle queriedClass, JNIObjectHandle callerClass) {
43+
if (shouldApproveWithoutChecks(env, queriedClass, callerClass)) {
44+
return true;
45+
}
46+
return configuration.contains(serializationTargetClass, parameterTypes, checkedExceptions, modifiers, targetConstructorClass);
47+
}
48+
}

substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/ConfigurationTool.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ private static void generate(Iterator<String> argsIter, boolean acceptTraceFileA
189189
set.getResourceConfigPaths().add(requirePathUri(current, value));
190190
break;
191191

192+
case "--serialization-input":
193+
set = inputSet; // fall through
194+
case "--serialization-output":
195+
set.getSerializationConfigPaths().add(requirePathUri(current, value));
196+
break;
197+
192198
case "--trace-input":
193199
traceInputs.add(requirePathUri(current, value));
194200
break;
@@ -249,7 +255,8 @@ private static void generate(Iterator<String> argsIter, boolean acceptTraceFileA
249255
TraceProcessor p;
250256
try {
251257
p = new TraceProcessor(advisor, inputSet.loadJniConfig(ConfigurationSet.FAIL_ON_EXCEPTION), inputSet.loadReflectConfig(ConfigurationSet.FAIL_ON_EXCEPTION),
252-
inputSet.loadProxyConfig(ConfigurationSet.FAIL_ON_EXCEPTION), inputSet.loadResourceConfig(ConfigurationSet.FAIL_ON_EXCEPTION));
258+
inputSet.loadProxyConfig(ConfigurationSet.FAIL_ON_EXCEPTION), inputSet.loadResourceConfig(ConfigurationSet.FAIL_ON_EXCEPTION),
259+
inputSet.loadSerializationConfig(ConfigurationSet.FAIL_ON_EXCEPTION));
253260
} catch (IOException e) {
254261
throw e;
255262
} catch (Throwable t) {
@@ -287,6 +294,11 @@ private static void generate(Iterator<String> argsIter, boolean acceptTraceFileA
287294
p.getResourceConfiguration().printJson(writer);
288295
}
289296
}
297+
for (URI uri : outputSet.getSerializationConfigPaths()) {
298+
try (JsonWriter writer = new JsonWriter(Paths.get(uri))) {
299+
p.getSerializationConfiguration().printJson(writer);
300+
}
301+
}
290302
}
291303

292304
private static void generateFilterRules(Iterator<String> argsIter) throws IOException {

0 commit comments

Comments
 (0)