Skip to content

Commit 448b0de

Browse files
committed
[GR-65850] [GR-68595] Fixing registration methods bypassing reachability handler creation and adding validation checks on registration methods.
PullRequest: graal/21804
2 parents df71e50 + a32ddb9 commit 448b0de

File tree

17 files changed

+191
-128
lines changed

17 files changed

+191
-128
lines changed

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/hosted/RuntimeProxyCreation.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -44,7 +44,7 @@
4444
import org.graalvm.nativeimage.Platform;
4545
import org.graalvm.nativeimage.Platforms;
4646
import org.graalvm.nativeimage.impl.ConfigurationCondition;
47-
import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport;
47+
import org.graalvm.nativeimage.impl.RuntimeProxyRegistrySupport;
4848

4949
/**
5050
* This class can be used to make creating dynamic proxy classes at run time valid.
@@ -62,7 +62,7 @@ public final class RuntimeProxyCreation {
6262
* @since 22.3
6363
*/
6464
public static void register(Class<?>... interfaces) {
65-
ImageSingletons.lookup(RuntimeProxyCreationSupport.class).addProxyClass(ConfigurationCondition.alwaysTrue(), interfaces);
65+
ImageSingletons.lookup(RuntimeProxyRegistrySupport.class).registerProxy(ConfigurationCondition.alwaysTrue(), interfaces);
6666
}
6767

6868
private RuntimeProxyCreation() {
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package org.graalvm.nativeimage.impl;
42+
43+
public interface RuntimeProxyRegistrySupport {
44+
Class<?> registerProxy(ConfigurationCondition condition, Class<?>... interfaces);
45+
}

substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,6 @@ public class ForeignFunctionsFeature implements InternalFeature {
157157
"jdk.internal.foreign.layout"});
158158

159159
/** Indicates if the registration of stubs is no longer allowed. */
160-
private boolean sealed;
161160
private RuntimeForeignAccessSupportImpl accessSupport;
162161

163162
/** Indicates if at least one stub was registered. */
@@ -175,10 +174,6 @@ public static ForeignFunctionsFeature singleton() {
175174
return ImageSingletons.lookup(ForeignFunctionsFeature.class);
176175
}
177176

178-
private void checkNotSealed() {
179-
UserError.guarantee(!sealed, "Registration of foreign functions was closed.");
180-
}
181-
182177
/**
183178
* Descriptor that represents both, up- and downcalls.
184179
*/
@@ -221,7 +216,7 @@ void duringSetup(AnalysisMetaAccess metaAccess, AnalysisUniverse analysisUnivers
221216

222217
@Override
223218
public void registerForDowncall(ConfigurationCondition condition, FunctionDescriptor desc, Linker.Option... options) {
224-
checkNotSealed();
219+
abortIfSealed();
225220
try {
226221
LinkerOptions linkerOptions = LinkerOptions.forDowncall(desc, options);
227222
SharedDesc sharedDesc = new SharedDesc(desc, linkerOptions);
@@ -233,7 +228,7 @@ public void registerForDowncall(ConfigurationCondition condition, FunctionDescri
233228

234229
@Override
235230
public void registerForUpcall(ConfigurationCondition condition, FunctionDescriptor desc, Linker.Option... options) {
236-
checkNotSealed();
231+
abortIfSealed();
237232
try {
238233
LinkerOptions linkerOptions = LinkerOptions.forUpcall(desc, options);
239234
SharedDesc sharedDesc = new SharedDesc(desc, linkerOptions);
@@ -245,7 +240,7 @@ public void registerForUpcall(ConfigurationCondition condition, FunctionDescript
245240

246241
@Override
247242
public void registerForDirectUpcall(ConfigurationCondition condition, MethodHandle target, FunctionDescriptor desc, Linker.Option... options) {
248-
checkNotSealed();
243+
abortIfSealed();
249244
DirectMethodHandleDesc directMethodHandleDesc = target.describeConstable()
250245
.filter(x -> x instanceof DirectMethodHandleDesc dmh && dmh.kind() == Kind.STATIC)
251246
.map(x -> ((DirectMethodHandleDesc) x))
@@ -719,7 +714,7 @@ public void beforeAnalysis(BeforeAnalysisAccess a) {
719714

720715
@Override
721716
public void afterAnalysis(AfterAnalysisAccess access) {
722-
sealed = true;
717+
accessSupport.sealed();
723718
if (!ForeignFunctionsRuntime.areFunctionCallsSupported() && stubsRegistered) {
724719
assert getCreatedDowncallStubsCount() == 0;
725720
assert getCreatedUpcallStubsCount() == 0;
@@ -853,17 +848,17 @@ public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, Va
853848
/* Testing and reporting interface */
854849

855850
public int getCreatedDowncallStubsCount() {
856-
assert sealed;
851+
assert accessSupport.isSealed();
857852
return foreignFunctionsRuntime.getDowncallStubsCount();
858853
}
859854

860855
public int getCreatedUpcallStubsCount() {
861-
assert sealed;
856+
assert accessSupport.isSealed();
862857
return foreignFunctionsRuntime.getUpcallStubsCount();
863858
}
864859

865860
public int getCreatedDirectUpcallStubsCount() {
866-
assert sealed;
861+
assert accessSupport.isSealed();
867862
return foreignFunctionsRuntime.getDirectUpcallStubsCount();
868863
}
869864
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ConditionalConfigurationRegistry.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.util.concurrent.ConcurrentLinkedQueue;
3333
import java.util.function.Consumer;
3434

35+
import com.oracle.svm.core.util.UserError;
3536
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
3637
import org.graalvm.nativeimage.hosted.Feature;
3738
import org.graalvm.nativeimage.impl.ConfigurationCondition;
@@ -42,6 +43,7 @@
4243
public abstract class ConditionalConfigurationRegistry {
4344
private Feature.BeforeAnalysisAccess beforeAnalysisAccess;
4445
private SVMHost hostVM;
46+
private boolean sealed = false;
4547
protected AnalysisUniverse universe;
4648
private final Map<Class<?>, Collection<Runnable>> pendingReachabilityHandlers = new ConcurrentHashMap<>();
4749
private final Set<ConditionalTask> pendingConditionalTasks = ConcurrentHashMap.newKeySet();
@@ -107,12 +109,39 @@ public void setAnalysisAccess(Feature.BeforeAnalysisAccess beforeAnalysisAccess)
107109
pendingReachabilityHandlers.clear();
108110
}
109111

112+
protected void requireNonNull(Object[] values, String kind, String accessKind) {
113+
for (Object value : values) {
114+
Objects.requireNonNull(value, () -> nullErrorMessage(kind, accessKind));
115+
}
116+
}
117+
118+
protected String nullErrorMessage(String elementKind, String accessKind) {
119+
return "Cannot register null value as " + elementKind + " for " + accessKind + ". Please ensure that all values you register are not null.";
120+
}
121+
122+
public void sealed() {
123+
sealed = true;
124+
}
125+
126+
public boolean isSealed() {
127+
return sealed;
128+
}
129+
130+
public void abortIfSealed() {
131+
/*
132+
* The UserError needs a cause argument to print out the stack trace, so users can see
133+
* exactly where the exception occurred.
134+
*/
135+
if (sealed) {
136+
throw UserError.abort(new IllegalStateException(), "All elements must be registered for runtime access before the analysis has completed.");
137+
}
138+
}
139+
110140
public void setHostVM(SVMHost hostVM) {
111141
this.hostVM = hostVM;
112142
}
113143

114144
public SVMHost getHostVM() {
115145
return hostVM;
116146
}
117-
118147
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ public static class Options {
160160
public static final HostedOptionKey<Boolean> GenerateEmbeddedResourcesFile = new HostedOptionKey<>(false);
161161
}
162162

163-
private boolean sealed = false;
163+
private ResourcesRegistryImpl resourcesRegistry;
164164

165165
private record ConditionalPattern(ConfigurationCondition condition, String pattern, Object origin) {
166166
}
@@ -208,6 +208,15 @@ public void addCondition(ConfigurationCondition condition, Module module, String
208208
}
209209
}
210210

211+
@Override
212+
public void addResource(ConfigurationCondition condition, Module module, String resourcePath, Object origin) {
213+
abortIfSealed();
214+
registerConditionalConfiguration(condition, cnd -> {
215+
addResourceEntry(module, resourcePath, origin);
216+
addCondition(condition, module, resourcePath);
217+
});
218+
}
219+
211220
/* Adds single resource defined with its module and name */
212221
@Override
213222
public void addResourceEntry(Module module, String resourcePath, Object origin) {
@@ -229,31 +238,34 @@ public void addResourceEntry(Module module, String resourcePath, Object origin)
229238

230239
@Override
231240
public void injectResource(Module module, String resourcePath, byte[] resourceContent, Object origin) {
241+
abortIfSealed();
232242
EmbeddedResourcesInfo.singleton().declareResourceAsRegistered(module, resourcePath, "INJECTED", origin);
233243
Resources.currentLayer().registerResource(module, resourcePath, resourceContent);
234244
}
235245

236246
@Override
237247
public void ignoreResources(ConfigurationCondition condition, String pattern, Object origin) {
248+
abortIfSealed();
238249
registerConditionalConfiguration(condition, (cnd) -> {
239-
UserError.guarantee(!sealed, "Resources ignored too late: %s", pattern);
240-
241250
excludedResourcePatterns.add(new ConditionalPattern(condition, pattern, origin));
242251
});
243252
}
244253

245254
@Override
246255
public void addResourceBundles(ConfigurationCondition condition, String name) {
256+
abortIfSealed();
247257
registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(cnd, name));
248258
}
249259

250260
@Override
251261
public void addClassBasedResourceBundle(ConfigurationCondition condition, String basename, String className) {
262+
abortIfSealed();
252263
registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareClassResourceBundle(basename, className));
253264
}
254265

255266
@Override
256267
public void addResourceBundles(ConfigurationCondition condition, String basename, Collection<Locale> locales) {
268+
abortIfSealed();
257269
registerConditionalConfiguration(condition, (cnd) -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(cnd, basename, locales));
258270
}
259271

@@ -388,7 +400,7 @@ private void registerResource(Module module, String resourcePath, boolean fromJa
388400
public void afterRegistration(AfterRegistrationAccess a) {
389401
FeatureImpl.AfterRegistrationAccessImpl access = (FeatureImpl.AfterRegistrationAccessImpl) a;
390402
imageClassLoader = access.getImageClassLoader();
391-
ResourcesRegistryImpl resourcesRegistry = new ResourcesRegistryImpl();
403+
resourcesRegistry = new ResourcesRegistryImpl();
392404
ImageSingletons.add(ResourcesRegistry.class, resourcesRegistry);
393405
ImageSingletons.add(RuntimeResourceSupport.class, resourcesRegistry);
394406
EmbeddedResourcesInfo embeddedResourcesInfo = new EmbeddedResourcesInfo();
@@ -649,7 +661,7 @@ boolean moduleNameMatches(String resourceContainerModuleName) {
649661

650662
@Override
651663
public void afterAnalysis(AfterAnalysisAccess access) {
652-
sealed = true;
664+
resourcesRegistry.sealed();
653665
if (Options.GenerateEmbeddedResourcesFile.getValue()) {
654666
Path reportLocation = NativeImageGenerator.generatedFiles(HostedOptionValues.singleton()).resolve(Options.EMBEDDED_RESOURCES_FILE_NAME);
655667
try (JsonWriter writer = new JsonWriter(reportLocation)) {
@@ -709,7 +721,7 @@ public boolean isDecorator() {
709721

710722
@Override
711723
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
712-
VMError.guarantee(!sealed, "All bytecode parsing happens before the analysis, i.e., before the registry is sealed");
724+
VMError.guarantee(!resourcesRegistry.isSealed(), "All bytecode parsing happens before the analysis, i.e., before the registry is sealed");
713725
Class<?> clazz = SubstrateGraphBuilderPlugins.asConstantObject(b, Class.class, receiver.get(false));
714726
String resource = SubstrateGraphBuilderPlugins.asConstantObject(b, String.class, arg);
715727
if (clazz != null && resource != null) {

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ConfigurationParserUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
import org.graalvm.nativeimage.impl.ConfigurationCondition;
4747
import org.graalvm.nativeimage.impl.ReflectionRegistry;
4848
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
49-
import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport;
49+
import org.graalvm.nativeimage.impl.RuntimeProxyRegistrySupport;
5050
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
5151

5252
import com.oracle.svm.configure.ConfigurationFile;
@@ -64,7 +64,7 @@
6464
public final class ConfigurationParserUtils {
6565

6666
public static ReflectionConfigurationParser<ConfigurationCondition, Class<?>> create(ConfigurationFile configurationKind, boolean combinedFileSchema,
67-
ConfigurationConditionResolver<ConfigurationCondition> conditionResolver, ReflectionRegistry registry, RuntimeProxyCreationSupport proxyRegistry,
67+
ConfigurationConditionResolver<ConfigurationCondition> conditionResolver, ReflectionRegistry registry, RuntimeProxyRegistrySupport proxyRegistry,
6868
RuntimeSerializationSupport<ConfigurationCondition> serializationSupport, RuntimeJNIAccessSupport jniSupport, ImageClassLoader imageClassLoader) {
6969
var additionalParserOptions = configurationKind == ConfigurationFile.JNI ? EnumSet.of(JNI_PARSER) : null;
7070
return ReflectionConfigurationParser.create(combinedFileSchema, conditionResolver, RegistryAdapter.create(registry, proxyRegistry, serializationSupport, jniSupport, imageClassLoader),

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/ReflectionRegistryAdapter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
import org.graalvm.nativeimage.impl.ConfigurationCondition;
3535
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
36-
import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport;
36+
import org.graalvm.nativeimage.impl.RuntimeProxyRegistrySupport;
3737
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
3838
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
3939

@@ -46,11 +46,11 @@
4646

4747
public class ReflectionRegistryAdapter extends RegistryAdapter {
4848
private final RuntimeReflectionSupport reflectionSupport;
49-
private final RuntimeProxyCreationSupport proxyRegistry;
49+
private final RuntimeProxyRegistrySupport proxyRegistry;
5050
private final RuntimeSerializationSupport<ConfigurationCondition> serializationSupport;
5151
private final RuntimeJNIAccessSupport jniSupport;
5252

53-
ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, RuntimeProxyCreationSupport proxyRegistry, RuntimeSerializationSupport<ConfigurationCondition> serializationSupport,
53+
ReflectionRegistryAdapter(RuntimeReflectionSupport reflectionSupport, RuntimeProxyRegistrySupport proxyRegistry, RuntimeSerializationSupport<ConfigurationCondition> serializationSupport,
5454
RuntimeJNIAccessSupport jniSupport, ImageClassLoader classLoader) {
5555
super(reflectionSupport, classLoader);
5656
this.reflectionSupport = reflectionSupport;
@@ -63,7 +63,7 @@ public class ReflectionRegistryAdapter extends RegistryAdapter {
6363
public void registerType(ConfigurationCondition condition, Class<?> type) {
6464
super.registerType(condition, type);
6565
if (Proxy.isProxyClass(type)) {
66-
proxyRegistry.addProxyClass(condition, type.getInterfaces());
66+
proxyRegistry.registerProxy(condition, type.getInterfaces());
6767
}
6868
}
6969

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/config/RegistryAdapter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import org.graalvm.nativeimage.impl.ConfigurationCondition;
3939
import org.graalvm.nativeimage.impl.ReflectionRegistry;
4040
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
41-
import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport;
41+
import org.graalvm.nativeimage.impl.RuntimeProxyRegistrySupport;
4242
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
4343
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
4444

@@ -60,7 +60,7 @@ public class RegistryAdapter implements ReflectionConfigurationParserDelegate<Co
6060
protected final ReflectionRegistry registry;
6161
private final ImageClassLoader classLoader;
6262

63-
public static RegistryAdapter create(ReflectionRegistry registry, RuntimeProxyCreationSupport proxyRegistry, RuntimeSerializationSupport<ConfigurationCondition> serializationSupport,
63+
public static RegistryAdapter create(ReflectionRegistry registry, RuntimeProxyRegistrySupport proxyRegistry, RuntimeSerializationSupport<ConfigurationCondition> serializationSupport,
6464
RuntimeJNIAccessSupport jniSupport, ImageClassLoader classLoader) {
6565
if (registry instanceof RuntimeReflectionSupport) {
6666
return new ReflectionRegistryAdapter((RuntimeReflectionSupport) registry, proxyRegistry, serializationSupport, jniSupport, classLoader);

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/PreserveOptionsSupport.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
import org.graalvm.nativeimage.ImageSingletons;
5151
import org.graalvm.nativeimage.impl.ConfigurationCondition;
5252
import org.graalvm.nativeimage.impl.RuntimeJNIAccessSupport;
53-
import org.graalvm.nativeimage.impl.RuntimeProxyCreationSupport;
53+
import org.graalvm.nativeimage.impl.RuntimeProxyRegistrySupport;
5454
import org.graalvm.nativeimage.impl.RuntimeReflectionSupport;
5555
import org.graalvm.nativeimage.impl.RuntimeResourceSupport;
5656
import org.graalvm.nativeimage.impl.RuntimeSerializationSupport;
@@ -198,7 +198,7 @@ public static void registerPreservedClasses(BigBang bb, MetaAccessProvider origi
198198

199199
final RuntimeReflectionSupport reflection = ImageSingletons.lookup(RuntimeReflectionSupport.class);
200200
final RuntimeResourceSupport<ConfigurationCondition> resources = RuntimeResourceSupport.singleton();
201-
final RuntimeProxyCreationSupport proxy = ImageSingletons.lookup(RuntimeProxyCreationSupport.class);
201+
final RuntimeProxyRegistrySupport proxy = ImageSingletons.lookup(RuntimeProxyRegistrySupport.class);
202202
final RuntimeSerializationSupport<ConfigurationCondition> serialization = RuntimeSerializationSupport.singleton();
203203
final ConfigurationCondition always = ConfigurationCondition.alwaysTrue();
204204

@@ -217,7 +217,7 @@ public static void registerPreservedClasses(BigBang bb, MetaAccessProvider origi
217217
/* Register every single-interface proxy */
218218
// GR-62293 can't register proxies from jdk modules.
219219
if (c.getModule() == null && c.isInterface()) {
220-
proxy.addProxyClass(always, c);
220+
proxy.registerProxy(always, c);
221221
}
222222

223223
try {

0 commit comments

Comments
 (0)