Skip to content

Commit

Permalink
Proper getStackAccessControlContext implementation
Browse files Browse the repository at this point in the history
Implemented PrivilegedStack using FastThreadLocal.
Added missing getProtectionDomain method.
Check for DISALLOWED_CONTEXT_MARKER in executePrivileged.
Added allowlist for known-good JDK contexts.
Suppress deprecation warnings.
  • Loading branch information
lazar-mitrovic committed Dec 16, 2021
1 parent 179dc4f commit 2766ffe
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 110 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* 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 java.security.AccessControlContext;
import java.security.ProtectionDomain;
import java.util.ArrayDeque;
import java.util.Objects;

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;

/**
* Stack for storing AccessControlContexts. Used in conjunction with
* {@code StackAccessControlContextVisitor}.
*/
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;
}
}

/* Local AccessControlContext stack */
private static final FastThreadLocalObject<ArrayDeque<StackElement>> stack;

static {
@SuppressWarnings("unchecked")
Class<ArrayDeque<StackElement>> cls = (Class<ArrayDeque<StackElement>>) (Object) ArrayDeque.class;
stack = FastThreadLocalFactory.createObject(cls, "AccessControlContextStack");
}

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

private static void initializeStack() {
ArrayDeque<StackElement> tmp = new ArrayDeque<>();
stack.set(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();
}
}

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

/**
* Instance that is used to mark contexts that were disallowed in
* {@code AccessControlContextReplacerFeature.replaceAccessControlContext()} If this marker is
* passed to {@code AccessController.doPrivileged()} a runtime error will be thrown.
*/
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);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 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
Expand Down Expand Up @@ -28,9 +28,7 @@

import java.net.URL;
import java.security.AccessControlContext;
import java.security.AccessControlException;
import java.security.CodeSource;
import java.security.DomainCombiner;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
Expand All @@ -46,25 +44,26 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;

import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaField;
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;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
import com.oracle.svm.core.graal.snippets.CEntryPointSnippets;
import com.oracle.svm.core.thread.Target_java_lang_Thread;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;

import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.ResolvedJavaField;

// Checkstyle: stop
import sun.security.jca.ProviderList;
Expand All @@ -82,133 +81,125 @@
final class Target_java_security_AccessController {

@Substitute
private static <T> T doPrivileged(PrivilegedAction<T> action) throws Throwable {
try {
return action.run();
} catch (Throwable ex) {
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
}
@TargetElement(onlyWith = JDK11OrEarlier.class)
public static <T> T doPrivileged(PrivilegedAction<T> action) throws Throwable {
return executePrivileged(action, null, Target_jdk_internal_reflect_Reflection.getCallerClass());
}

@Substitute
private static <T> T doPrivilegedWithCombiner(PrivilegedAction<T> action) throws Throwable {
try {
return action.run();
} catch (Throwable ex) {
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
}
@TargetElement(onlyWith = JDK11OrEarlier.class)
public static <T> T doPrivileged(PrivilegedAction<T> action, AccessControlContext context) throws Throwable {
Class<?> caller = Target_jdk_internal_reflect_Reflection.getCallerClass();
AccessControlContext acc = checkContext(context, caller);
return executePrivileged(action, acc, caller);
}

@Substitute
private static <T> T doPrivileged(PrivilegedAction<T> action, AccessControlContext context) throws Throwable {
try {
return action.run();
} catch (Throwable ex) {
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
}
@TargetElement(onlyWith = JDK11OrEarlier.class)
public static <T> T doPrivileged(PrivilegedExceptionAction<T> action) throws Throwable {
Class<?> caller = Target_jdk_internal_reflect_Reflection.getCallerClass();
return executePrivileged(action, null, caller);
}

@Substitute
private static <T> T doPrivileged(PrivilegedAction<T> action, AccessControlContext context, Permission... perms) throws Throwable {
try {
return action.run();
} catch (Throwable ex) {
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
}
@TargetElement(onlyWith = JDK11OrEarlier.class)
static <T> T doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context) throws Throwable {
Class<?> caller = Target_jdk_internal_reflect_Reflection.getCallerClass();
AccessControlContext acc = checkContext(context, caller);
return executePrivileged(action, acc, caller);
}

@Substitute
private static <T> T doPrivileged(PrivilegedExceptionAction<T> action) throws Throwable {
try {
return action.run();
} catch (Throwable ex) {
throw AccessControllerUtil.wrapCheckedException(ex);
@SuppressWarnings("deprecation")
static AccessControlContext getStackAccessControlContext() {
if (!CEntryPointSnippets.isIsolateInitialized()) {
/*
* If isolate still isn't initialized, we can assume that we are so early in the JDK
* initialization that any attempt at stalk walk will fail as not even the basic
* PrintWriter/Logging is available yet. This manifested when
* UseDedicatedVMOperationThread hosted option was set, triggering a runtime crash.
*/
Permissions perms = new Permissions();
perms.add(SecurityConstants.ALL_PERMISSION);
return new AccessControlContext(new ProtectionDomain[]{new ProtectionDomain(null, perms)});
}
return StackAccessControlContextVisitor.getFromStack();
}

@Substitute
private static <T> T doPrivilegedWithCombiner(PrivilegedExceptionAction<T> action) throws Throwable {
try {
return action.run();
} catch (Throwable ex) {
throw AccessControllerUtil.wrapCheckedException(ex);
}
static AccessControlContext getInheritedAccessControlContext() {
return SubstrateUtil.cast(Thread.currentThread(), Target_java_lang_Thread.class).inheritedAccessControlContext;
}

@Substitute
private static <T> T doPrivilegedWithCombiner(PrivilegedExceptionAction<T> action, AccessControlContext context, Permission... perms) throws Throwable {
try {
return action.run();
} catch (Throwable ex) {
throw AccessControllerUtil.wrapCheckedException(ex);
}
@TargetElement(onlyWith = JDK17OrLater.class)
private static ProtectionDomain getProtectionDomain(final Class<?> caller) {
return caller.getProtectionDomain();
}

@Substitute
private static <T> T doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context) throws Throwable {
@TargetElement(onlyWith = JDK17OrLater.class)
@SuppressWarnings("deprecation") // deprecated starting JDK 17
static <T> T executePrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context, Class<?> caller) throws Throwable {
if (action == null) {
throw new NullPointerException("Null action");
}

PrivilegedStack.push(context, caller);
try {
return action.run();
} catch (Throwable ex) {
throw AccessControllerUtil.wrapCheckedException(ex);
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new PrivilegedActionException(ex);
} finally {
PrivilegedStack.pop();
}
}

@Substitute
private static void checkPermission(Permission perm) throws AccessControlException {
}

@Substitute
private static AccessControlContext getContext() {
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
}

@Substitute
private static AccessControlContext createWrapper(DomainCombiner combiner, Class<?> caller, AccessControlContext parent, AccessControlContext context, Permission[] perms) {
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
}
}

@InternalVMMethod
class AccessControllerUtil {

static final AccessControlContext NO_CONTEXT_SINGLETON;
@TargetElement(onlyWith = JDK17OrLater.class)
@SuppressWarnings("deprecation") // deprecated starting JDK 17
static <T> T executePrivileged(PrivilegedAction<T> action, AccessControlContext context, Class<?> caller) throws Throwable {
if (action == null) {
throw new NullPointerException("Null action");
}

static {
PrivilegedStack.push(context, caller);
try {
NO_CONTEXT_SINGLETON = ReflectionUtil.lookupConstructor(AccessControlContext.class, ProtectionDomain[].class, boolean.class).newInstance(new ProtectionDomain[0], true);
} catch (ReflectiveOperationException ex) {
throw VMError.shouldNotReachHere(ex);
return action.run();
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
if (JavaVersionUtil.JAVA_SPEC > 11) {
throw ex;
} else {
throw new PrivilegedActionException(ex);
}
} finally {
PrivilegedStack.pop();
}
}

static Throwable wrapCheckedException(Throwable ex) {
if (ex instanceof Exception && !(ex instanceof RuntimeException)) {
return new PrivilegedActionException((Exception) ex);
} else {
return ex;
}
}
@Substitute
@TargetElement(onlyWith = JDK17OrLater.class)
@SuppressWarnings("deprecation")
static AccessControlContext checkContext(AccessControlContext context, Class<?> caller) {

static Throwable wrapCheckedExceptionForPrivilegedAction(Throwable ex) {
if (JavaVersionUtil.JAVA_SPEC <= 11) {
return wrapCheckedException(ex);
if (context != null && context.equals(AccessControllerUtil.DISALLOWED_CONTEXT_MARKER)) {
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 'AccessControlContextReplacerFeature.duringSetup'");
}
return ex;
}
}

@AutomaticFeature
class AccessControlContextFeature implements Feature {
@Override
public void duringSetup(DuringSetupAccess access) {
access.registerObjectReplacer(AccessControlContextFeature::replaceAccessControlContext);
}

private static Object replaceAccessControlContext(Object obj) {
if (obj instanceof AccessControlContext) {
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
// check if caller is authorized to create context
if (System.getSecurityManager() != null) {
throw VMError.unsupportedFeature("SecurityManager isn't supported");
}
return obj;
return context;
}
}

Expand Down
Loading

0 comments on commit 2766ffe

Please sign in to comment.