Skip to content

Commit

Permalink
Fix review feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
lazar-mitrovic committed Oct 29, 2021
1 parent f3b65f6 commit 74ff3eb
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 161 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,14 @@
import java.util.Set;
import java.util.StringJoiner;

import com.oracle.svm.core.BaseProcessPropertiesSupport;
import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.compiler.core.common.SuppressFBWarnings;
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.ImageInfo;
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.nativeimage.impl.ProcessPropertiesSupport;
import org.graalvm.util.DirectAnnotationAccess;

import com.oracle.svm.core.RuntimeAssertionsSupport;
Expand Down Expand Up @@ -329,6 +327,7 @@ public void setModule(Object module) {
this.module = module;
}

static final boolean IS_EXECUTABLE = ImageInfo.isExecutable();
/**
* Final fields in substituted classes are treated as implicitly RecomputeFieldValue even when
* not annotated with @RecomputeFieldValue. Their name must not match a field in the original
Expand All @@ -337,20 +336,21 @@ public void setModule(Object module) {
static final LazyFinalReference<java.security.ProtectionDomain> allPermDomainReference = new LazyFinalReference<>(() -> {
java.security.Permissions perms = new java.security.Permissions();
perms.add(SecurityConstants.ALL_PERMISSION);
CodeSource cs = null;
URL url = null;

if (ImageSingletons.lookup(ProcessPropertiesSupport.class) instanceof BaseProcessPropertiesSupport) {
if (IS_EXECUTABLE) {
// 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].
try {
cs = new CodeSource(new File(ProcessProperties.getExecutableName()).toURI().toURL(), (Certificate[]) null);
url = new File(ProcessProperties.getExecutableName()).toURI().toURL();
} catch (MalformedURLException e) {
// This should not really happen; the file is cannonicalized, absolute, so it should
// always have file:// URL.
}
}

CodeSource cs = new CodeSource(url, (Certificate[]) null);
return new java.security.ProtectionDomain(cs, perms);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* 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;

import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
import com.oracle.svm.core.threadlocal.FastThreadLocalObject;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;

import java.security.AccessControlContext;
import java.security.PrivilegedActionException;
import java.security.ProtectionDomain;
import java.util.ArrayDeque;
import java.util.Objects;

@InternalVMMethod
@SuppressWarnings({"unused"})
public class AccessControllerUtil {

public static final AccessControlContext DISALLOWED_CONTEXT_MARKER;

static {
try {
DISALLOWED_CONTEXT_MARKER = ReflectionUtil.lookupConstructor(AccessControlContext.class, ProtectionDomain[].class, boolean.class).newInstance(new ProtectionDomain[0], true);
} catch (ReflectiveOperationException ex) {
throw VMError.shouldNotReachHere(ex);
}
}

public static class PrivilegedStack {

public static class StackElement {
protected AccessControlContext context;
protected Class<?> caller;

StackElement(AccessControlContext context, Class<?> caller) {
this.context = context;
this.caller = caller;
}

public AccessControlContext getContext() {
return context;
}

public Class<?> getCaller() {
return caller;
}
}

@SuppressWarnings("rawtypes") private static final FastThreadLocalObject<ArrayDeque> stack = FastThreadLocalFactory.createObject(ArrayDeque.class, "AccessControlContextStack");

@SuppressWarnings("unchecked")
private static ArrayDeque<StackElement> getStack() {
ArrayDeque<StackElement> tmp = stack.get();
if (tmp == null) {
tmp = new ArrayDeque<>();
stack.set(tmp);
}
return tmp;
}

public static void push(AccessControlContext context, Class<?> caller) {
getStack().push(new StackElement(context, caller));
}

public static void pop() {
getStack().pop();
}

public static AccessControlContext peekContext() {
return Objects.requireNonNull(getStack().peek()).getContext();
}

public static Class<?> peekCaller() {
return Objects.requireNonNull(getStack().peek()).getCaller();
}

public static int length() {
return getStack().size();
}
}

static Throwable wrapCheckedException(Throwable ex) {
if (ex instanceof Exception && !(ex instanceof RuntimeException)) {
return new PrivilegedActionException((Exception) ex);
} else {
return ex;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,12 @@
import java.security.Permissions;
import java.security.Policy;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.ProtectionDomain;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;

Expand All @@ -52,12 +48,10 @@
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.word.Pointer;

import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.AutomaticFeature;
import com.oracle.svm.core.annotate.Delete;
import com.oracle.svm.core.annotate.InjectAccessors;
import com.oracle.svm.core.annotate.NeverInline;
Expand All @@ -66,10 +60,7 @@
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
import com.oracle.svm.core.thread.Target_java_lang_Thread;
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
import com.oracle.svm.core.threadlocal.FastThreadLocalObject;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;

// Checkstyle: stop
import sun.security.jca.ProviderList;
Expand Down Expand Up @@ -171,13 +162,13 @@ static <T> T executePrivileged(PrivilegedAction<T> action, AccessControlContext
@SuppressWarnings({"unused", "deprecation"})
static AccessControlContext checkContext(AccessControlContext context, Class<?> caller) {

if (context != null && context.equals(AccessControllerUtil.NO_CONTEXT_SINGLETON)) {
VMError.shouldNotReachHere("Non-allowed AccessControlContext that was replaced with a blank one at build time was invoked without being reinitialized at run time.\n" +
if (context != null && context.equals(AccessControllerUtil.DISALLOWED_CONTEXT_MARKER)) {
throw VMError.shouldNotReachHere("Non-allowed AccessControlContext that was replaced with a blank one at build time was invoked without being reinitialized at run time.\n" +
"This might be an indicator of improper build time initialization, or of a non-compatible JDK version.\n" +
"In order to fix this you can either:\n" +
" * Annotate the offending context's field with @RecomputeFieldValue\n" +
" * Implement a custom runtime accessor and annotate said field with @InjectAccessors\n" +
" * If this context originates from the JDK, and it doesn't leak sensitive info, you can allow it in 'AccessControlContextFeature.duringSetup'");
" * If this context originates from the JDK, and it doesn't leak sensitive info, you can allow it in 'AccessControlContextReplacerFeature.duringSetup'");
}

// check if caller is authorized to create context
Expand All @@ -188,149 +179,6 @@ static AccessControlContext checkContext(AccessControlContext context, Class<?>
}
}

@InternalVMMethod
@SuppressWarnings({"unused"})
class AccessControllerUtil {

static final AccessControlContext NO_CONTEXT_SINGLETON;

static {
try {
NO_CONTEXT_SINGLETON = ReflectionUtil.lookupConstructor(AccessControlContext.class, ProtectionDomain[].class, boolean.class).newInstance(new ProtectionDomain[0], true);
} catch (ReflectiveOperationException ex) {
throw VMError.shouldNotReachHere(ex);
}
}

public static class PrivilegedStack {

public static class StackElement {
protected AccessControlContext context;
protected Class<?> caller;

StackElement(AccessControlContext context, Class<?> caller) {
this.context = context;
this.caller = caller;
}

public AccessControlContext getContext() {
return context;
}

public Class<?> getCaller() {
return caller;
}
}

@SuppressWarnings("rawtypes") private static final FastThreadLocalObject<ArrayDeque> stack = FastThreadLocalFactory.createObject(ArrayDeque.class, "AccessControlContextStack");

@SuppressWarnings("unchecked")
private static ArrayDeque<StackElement> getStack() {
ArrayDeque<StackElement> tmp = stack.get();
if (tmp == null) {
tmp = new ArrayDeque<>();
stack.set(tmp);
}
return tmp;
}

public static void push(AccessControlContext context, Class<?> caller) {
getStack().push(new StackElement(context, caller));
}

public static void pop() {
getStack().pop();
}

public static AccessControlContext peekContext() {
return Objects.requireNonNull(getStack().peek()).getContext();
}

public static Class<?> peekCaller() {
return Objects.requireNonNull(getStack().peek()).getCaller();
}

public static int length() {
return getStack().size();
}
}

static Throwable wrapCheckedException(Throwable ex) {
if (ex instanceof Exception && !(ex instanceof RuntimeException)) {
return new PrivilegedActionException((Exception) ex);
} else {
return ex;
}
}
}

@AutomaticFeature
@SuppressWarnings({"unused"})
class AccessControlContextFeature implements Feature {

static Map<String, AccessControlContext> allowedContexts = new HashMap<>();

static void allowContextIfExists(String className, String fieldName) {
try {
// Checkstyle: stop
Class<?> clazz = Class.forName(className);
// Checkstyle: resume
String description = className + "." + fieldName;
try {
AccessControlContext acc = ReflectionUtil.readStaticField(clazz, fieldName);
allowedContexts.put(description, acc);
} catch (ReflectionUtil.ReflectionUtilError e) {
VMError.shouldNotReachHere("Following field isn't present in JDK" + JavaVersionUtil.JAVA_SPEC + ": " + description);
}

} catch (ReflectiveOperationException e) {
VMError.shouldNotReachHere("Following class isn't present in JDK" + JavaVersionUtil.JAVA_SPEC + ": " + className);
}
}

@Override
public void duringSetup(DuringSetupAccess access) {
// Following AccessControlContexts are allowed in the image heap since they cannot leak
// sensitive information.
// They mostly originate from JDK's static final fields, and they do not feature
// CodeSources, DomainCombiners etc.
// New JDK versions can feature new or remove old contexts, so this method should be kept
// up-to-date.
allowContextIfExists("java.util.Calendar$CalendarAccessControlContext", "INSTANCE");
allowContextIfExists("javax.management.monitor.Monitor", "noPermissionsACC");

if (JavaVersionUtil.JAVA_SPEC < 9) {
allowContextIfExists("sun.misc.InnocuousThread", "ACC");
}
if (JavaVersionUtil.JAVA_SPEC >= 9) {
allowContextIfExists("java.security.AccessController$AccHolder", "innocuousAcc");
allowContextIfExists("java.util.concurrent.ForkJoinPool$DefaultForkJoinWorkerThreadFactory", "ACC");
}
if (JavaVersionUtil.JAVA_SPEC < 17) {
allowContextIfExists("java.util.concurrent.ForkJoinWorkerThread", "INNOCUOUS_ACC");
}
if (JavaVersionUtil.JAVA_SPEC >= 9 && JavaVersionUtil.JAVA_SPEC < 17) {
allowContextIfExists("java.util.concurrent.ForkJoinPool$InnocuousForkJoinWorkerThreadFactory", "ACC");
}
if (JavaVersionUtil.JAVA_SPEC >= 17) {
allowContextIfExists("java.util.concurrent.ForkJoinPool$WorkQueue", "INNOCUOUS_ACC");
allowContextIfExists("java.util.concurrent.ForkJoinPool$DefaultCommonPoolForkJoinWorkerThreadFactory", "ACC");
}
access.registerObjectReplacer(AccessControlContextFeature::replaceAccessControlContext);
}

private static Object replaceAccessControlContext(Object obj) {
if (obj instanceof AccessControlContext && obj != AccessControllerUtil.NO_CONTEXT_SINGLETON) {
if (allowedContexts.containsValue(obj)) {
return obj;
} else {
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
}
}
return obj;
}
}

@TargetClass(java.security.AccessControlContext.class)
@SuppressWarnings({"unused"})
final class Target_java_security_AccessControlContext {
Expand Down
Loading

0 comments on commit 74ff3eb

Please sign in to comment.