diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index 6b63ea2a9f2b..0acdec7d882b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -26,7 +26,6 @@ //Checkstyle: allow reflection -import java.io.File; import java.io.InputStream; import java.io.Serializable; import java.lang.annotation.Annotation; @@ -43,11 +42,8 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; -import java.net.MalformedURLException; import java.net.URL; -import java.security.CodeSource; import java.security.ProtectionDomain; -import java.security.cert.Certificate; import java.util.List; import java.util.Optional; import java.util.Set; @@ -58,7 +54,6 @@ import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.ProcessProperties; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.util.DirectAnnotationAccess; @@ -89,7 +84,6 @@ import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError; import jdk.vm.ci.meta.JavaKind; -import sun.security.util.SecurityConstants; @Hybrid(canHybridFieldsBeDuplicated = false) @Substitute @@ -318,28 +312,6 @@ public void setModule(Object module) { this.module = module; } - /** - * Final fields in subsituted classes are treated as implicitly RecomputeFieldValue even when - * not annotated with @RecomputeFieldValue. Their name must not match a field in the original - * class, i.e., allPermDomain. - */ - static final LazyFinalReference allPermDomainReference = new LazyFinalReference<>(() -> { - java.security.Permissions perms = new java.security.Permissions(); - perms.add(SecurityConstants.ALL_PERMISSION); - CodeSource cs; - try { - // Try to use executable image's name as code source for the class. - // The file location can be used by Java code to determine its location on disk, similar - // to argv[0]. - cs = new CodeSource(new File(ProcessProperties.getExecutableName()).toURI().toURL(), (Certificate[]) null); - } catch (MalformedURLException ex) { - // This should not really happen; the file is cannonicalized, absolute, so it should - // always have file:// URL. - cs = null; - } - return new java.security.ProtectionDomain(cs, perms); - }); - private final LazyFinalReference companion = new LazyFinalReference<>(() -> new DynamicHubCompanion(this)); @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java index 7ed1e196a7fa..5db04a2066cb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubCompanion.java @@ -41,6 +41,7 @@ import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.hub.DynamicHub.ReflectionData; +import com.oracle.svm.core.jdk.ProtectionDomainSupport; import com.oracle.svm.core.reflect.MethodMetadataDecoder; import com.oracle.svm.core.reflect.MethodMetadataDecoder.MethodDescriptor; import com.oracle.svm.core.util.VMError; @@ -82,7 +83,7 @@ public void setClassLoader(ClassLoader loader) { public ProtectionDomain getProtectionDomain() { if (protectionDomain == null) { - protectionDomain = DynamicHub.allPermDomainReference.get(); + protectionDomain = ProtectionDomainSupport.allPermDomain(); } return protectionDomain; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ProtectionDomainSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ProtectionDomainSupport.java new file mode 100644 index 000000000000..36689b1176d7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ProtectionDomainSupport.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.jdk; + +// Checkstyle: allow reflection + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.CodeSource; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.util.function.Supplier; + +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionType; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.ProcessProperties; +import org.graalvm.nativeimage.hosted.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.util.LazyFinalReference; +import com.oracle.svm.util.ReflectionUtil; + +import sun.security.util.SecurityConstants; + +/** + * All classes that do not set a ProtectionDomain explicitly have a "default" ProtectionDomain. And + * every ProtectionDomain has a CodeSource with a URL as its location. + * + * If an application invokes {@link CodeSource#getLocation()}, it can reasonably expect a non-null + * URL. We use the executable image's name as the location, because that is the best value we can + * provide. + * + * But computing the URL for the location pulls in a lot of JDK dependencies. For a simple + * application like "Hello World", that would significantly increase the image size. So we only add + * code to compute the URL if the application explicitly invokes {@link CodeSource#getLocation()}. + * This is done using a reachability handler registered in + * {@link ProtectionDomainFeature#beforeAnalysis}. + * + * Note that this still leads to observable differences in places where the location is used + * implicitly, like {@link CodeSource#toString} and {@link CodeSource#implies}. We accept that + * difference in behavior. + */ +public final class ProtectionDomainSupport { + + public static class Options { + @Option(help = "Return the application path as the Class.getProtectionDomain().getCodeSource().getLocation() for all classes that have no explicit ProtectionDomain.", type = OptionType.Expert) // + public static final HostedOptionKey UseApplicationCodeSourceLocation = new HostedOptionKey<>(null); + } + + private final LazyFinalReference allPermDomain = new LazyFinalReference<>(this::createAllPermDomain); + + /** Remains null as long as the reachability handler has not triggered. */ + Supplier executableURLSupplier; + + public static ProtectionDomain allPermDomain() { + return ImageSingletons.lookup(ProtectionDomainSupport.class).allPermDomain.get(); + } + + private ProtectionDomain createAllPermDomain() { + java.security.Permissions perms = new java.security.Permissions(); + perms.add(SecurityConstants.ALL_PERMISSION); + /* + * When enableCodeSource() was not called at image generation time, executableURLSupplier is + * null. In that case, the static analysis does not see createExecutableURL() as reachable + * because there is no code path to it. + */ + URL executableURL = executableURLSupplier != null ? executableURLSupplier.get() : null; + CodeSource cs = new CodeSource(executableURL, (Certificate[]) null); + return new java.security.ProtectionDomain(cs, perms); + } + + private static URL createExecutableURL() { + /* + * Try to use executable image's name as code source for the class. The file location can be + * used by Java code to determine its location on disk, similar to argv[0]. + */ + String executableName = ProcessProperties.getExecutableName(); + if (executableName != null) { + try { + return new File(executableName).toURI().toURL(); + } catch (MalformedURLException ex) { + /* + * This should not really happen; the file is canonicalized, absolute, so it should + * always have a file:// URL. But if it happens, it is OK to just ignore. + */ + } + } + return null; + } + + @Platforms(Platform.HOSTED_ONLY.class) + static void enableCodeSource(Feature.DuringAnalysisAccess access) { + ImageSingletons.lookup(ProtectionDomainSupport.class).executableURLSupplier = ProtectionDomainSupport::createExecutableURL; + if (access != null) { + access.requireAnalysisIteration(); + } + } +} + +@AutomaticFeature +final class ProtectionDomainFeature implements Feature { + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(ProtectionDomainSupport.class, new ProtectionDomainSupport()); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + Boolean useApplicationCodeSourceLocation = ProtectionDomainSupport.Options.UseApplicationCodeSourceLocation.getValue(); + if (useApplicationCodeSourceLocation == null) { + /* Option not set explicitly, so use automatic behavior based on reachability. */ + access.registerReachabilityHandler(ProtectionDomainSupport::enableCodeSource, + ReflectionUtil.lookupMethod(CodeSource.class, "getLocation")); + } else if (useApplicationCodeSourceLocation) { + /* Always enabled. */ + ProtectionDomainSupport.enableCodeSource(null); + } else { + /* Always disabled - nothing to do. */ + } + } +}