Skip to content

Commit 8ae0751

Browse files
committed
[GR-35117] Avoid pulling File and URL code in image via CodeSource.location.
PullRequest: graal/10334
2 parents b319eff + 9125d07 commit 8ae0751

File tree

3 files changed

+151
-29
lines changed

3 files changed

+151
-29
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
//Checkstyle: allow reflection
2828

29-
import java.io.File;
3029
import java.io.InputStream;
3130
import java.io.Serializable;
3231
import java.lang.annotation.Annotation;
@@ -43,11 +42,8 @@
4342
import java.lang.reflect.Modifier;
4443
import java.lang.reflect.Type;
4544
import java.lang.reflect.TypeVariable;
46-
import java.net.MalformedURLException;
4745
import java.net.URL;
48-
import java.security.CodeSource;
4946
import java.security.ProtectionDomain;
50-
import java.security.cert.Certificate;
5147
import java.util.List;
5248
import java.util.Optional;
5349
import java.util.Set;
@@ -58,7 +54,6 @@
5854
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
5955
import org.graalvm.nativeimage.Platform;
6056
import org.graalvm.nativeimage.Platforms;
61-
import org.graalvm.nativeimage.ProcessProperties;
6257
import org.graalvm.nativeimage.c.function.CFunctionPointer;
6358
import org.graalvm.util.DirectAnnotationAccess;
6459

@@ -89,7 +84,6 @@
8984
import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError;
9085

9186
import jdk.vm.ci.meta.JavaKind;
92-
import sun.security.util.SecurityConstants;
9387

9488
@Hybrid(canHybridFieldsBeDuplicated = false)
9589
@Substitute
@@ -318,28 +312,6 @@ public void setModule(Object module) {
318312
this.module = module;
319313
}
320314

321-
/**
322-
* Final fields in subsituted classes are treated as implicitly RecomputeFieldValue even when
323-
* not annotated with @RecomputeFieldValue. Their name must not match a field in the original
324-
* class, i.e., allPermDomain.
325-
*/
326-
static final LazyFinalReference<java.security.ProtectionDomain> allPermDomainReference = new LazyFinalReference<>(() -> {
327-
java.security.Permissions perms = new java.security.Permissions();
328-
perms.add(SecurityConstants.ALL_PERMISSION);
329-
CodeSource cs;
330-
try {
331-
// Try to use executable image's name as code source for the class.
332-
// The file location can be used by Java code to determine its location on disk, similar
333-
// to argv[0].
334-
cs = new CodeSource(new File(ProcessProperties.getExecutableName()).toURI().toURL(), (Certificate[]) null);
335-
} catch (MalformedURLException ex) {
336-
// This should not really happen; the file is cannonicalized, absolute, so it should
337-
// always have file:// URL.
338-
cs = null;
339-
}
340-
return new java.security.ProtectionDomain(cs, perms);
341-
});
342-
343315
private final LazyFinalReference<DynamicHubCompanion> companion = new LazyFinalReference<>(() -> new DynamicHubCompanion(this));
344316

345317
@Platforms(Platform.HOSTED_ONLY.class)

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.graalvm.nativeimage.ImageSingletons;
4242

4343
import com.oracle.svm.core.hub.DynamicHub.ReflectionData;
44+
import com.oracle.svm.core.jdk.ProtectionDomainSupport;
4445
import com.oracle.svm.core.reflect.MethodMetadataDecoder;
4546
import com.oracle.svm.core.reflect.MethodMetadataDecoder.MethodDescriptor;
4647
import com.oracle.svm.core.util.VMError;
@@ -82,7 +83,7 @@ public void setClassLoader(ClassLoader loader) {
8283

8384
public ProtectionDomain getProtectionDomain() {
8485
if (protectionDomain == null) {
85-
protectionDomain = DynamicHub.allPermDomainReference.get();
86+
protectionDomain = ProtectionDomainSupport.allPermDomain();
8687
}
8788
return protectionDomain;
8889
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jdk;
26+
27+
// Checkstyle: allow reflection
28+
29+
import java.io.File;
30+
import java.net.MalformedURLException;
31+
import java.net.URL;
32+
import java.security.CodeSource;
33+
import java.security.ProtectionDomain;
34+
import java.security.cert.Certificate;
35+
import java.util.function.Supplier;
36+
37+
import org.graalvm.compiler.options.Option;
38+
import org.graalvm.compiler.options.OptionType;
39+
import org.graalvm.nativeimage.ImageSingletons;
40+
import org.graalvm.nativeimage.Platform;
41+
import org.graalvm.nativeimage.Platforms;
42+
import org.graalvm.nativeimage.ProcessProperties;
43+
import org.graalvm.nativeimage.hosted.Feature;
44+
45+
import com.oracle.svm.core.annotate.AutomaticFeature;
46+
import com.oracle.svm.core.option.HostedOptionKey;
47+
import com.oracle.svm.core.util.LazyFinalReference;
48+
import com.oracle.svm.util.ReflectionUtil;
49+
50+
import sun.security.util.SecurityConstants;
51+
52+
/**
53+
* All classes that do not set a ProtectionDomain explicitly have a "default" ProtectionDomain. And
54+
* every ProtectionDomain has a CodeSource with a URL as its location.
55+
*
56+
* If an application invokes {@link CodeSource#getLocation()}, it can reasonably expect a non-null
57+
* URL. We use the executable image's name as the location, because that is the best value we can
58+
* provide.
59+
*
60+
* But computing the URL for the location pulls in a lot of JDK dependencies. For a simple
61+
* application like "Hello World", that would significantly increase the image size. So we only add
62+
* code to compute the URL if the application explicitly invokes {@link CodeSource#getLocation()}.
63+
* This is done using a reachability handler registered in
64+
* {@link ProtectionDomainFeature#beforeAnalysis}.
65+
*
66+
* Note that this still leads to observable differences in places where the location is used
67+
* implicitly, like {@link CodeSource#toString} and {@link CodeSource#implies}. We accept that
68+
* difference in behavior.
69+
*/
70+
public final class ProtectionDomainSupport {
71+
72+
public static class Options {
73+
@Option(help = "Return the application path as the Class.getProtectionDomain().getCodeSource().getLocation() for all classes that have no explicit ProtectionDomain.", type = OptionType.Expert) //
74+
public static final HostedOptionKey<Boolean> UseApplicationCodeSourceLocation = new HostedOptionKey<>(null);
75+
}
76+
77+
private final LazyFinalReference<ProtectionDomain> allPermDomain = new LazyFinalReference<>(this::createAllPermDomain);
78+
79+
/** Remains null as long as the reachability handler has not triggered. */
80+
Supplier<URL> executableURLSupplier;
81+
82+
public static ProtectionDomain allPermDomain() {
83+
return ImageSingletons.lookup(ProtectionDomainSupport.class).allPermDomain.get();
84+
}
85+
86+
private ProtectionDomain createAllPermDomain() {
87+
java.security.Permissions perms = new java.security.Permissions();
88+
perms.add(SecurityConstants.ALL_PERMISSION);
89+
/*
90+
* When enableCodeSource() was not called at image generation time, executableURLSupplier is
91+
* null. In that case, the static analysis does not see createExecutableURL() as reachable
92+
* because there is no code path to it.
93+
*/
94+
URL executableURL = executableURLSupplier != null ? executableURLSupplier.get() : null;
95+
CodeSource cs = new CodeSource(executableURL, (Certificate[]) null);
96+
return new java.security.ProtectionDomain(cs, perms);
97+
}
98+
99+
private static URL createExecutableURL() {
100+
/*
101+
* Try to use executable image's name as code source for the class. The file location can be
102+
* used by Java code to determine its location on disk, similar to argv[0].
103+
*/
104+
String executableName = ProcessProperties.getExecutableName();
105+
if (executableName != null) {
106+
try {
107+
return new File(executableName).toURI().toURL();
108+
} catch (MalformedURLException ex) {
109+
/*
110+
* This should not really happen; the file is canonicalized, absolute, so it should
111+
* always have a file:// URL. But if it happens, it is OK to just ignore.
112+
*/
113+
}
114+
}
115+
return null;
116+
}
117+
118+
@Platforms(Platform.HOSTED_ONLY.class)
119+
static void enableCodeSource(Feature.DuringAnalysisAccess access) {
120+
ImageSingletons.lookup(ProtectionDomainSupport.class).executableURLSupplier = ProtectionDomainSupport::createExecutableURL;
121+
if (access != null) {
122+
access.requireAnalysisIteration();
123+
}
124+
}
125+
}
126+
127+
@AutomaticFeature
128+
final class ProtectionDomainFeature implements Feature {
129+
130+
@Override
131+
public void afterRegistration(AfterRegistrationAccess access) {
132+
ImageSingletons.add(ProtectionDomainSupport.class, new ProtectionDomainSupport());
133+
}
134+
135+
@Override
136+
public void beforeAnalysis(BeforeAnalysisAccess access) {
137+
Boolean useApplicationCodeSourceLocation = ProtectionDomainSupport.Options.UseApplicationCodeSourceLocation.getValue();
138+
if (useApplicationCodeSourceLocation == null) {
139+
/* Option not set explicitly, so use automatic behavior based on reachability. */
140+
access.registerReachabilityHandler(ProtectionDomainSupport::enableCodeSource,
141+
ReflectionUtil.lookupMethod(CodeSource.class, "getLocation"));
142+
} else if (useApplicationCodeSourceLocation) {
143+
/* Always enabled. */
144+
ProtectionDomainSupport.enableCodeSource(null);
145+
} else {
146+
/* Always disabled - nothing to do. */
147+
}
148+
}
149+
}

0 commit comments

Comments
 (0)