Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper getStackAccessControlContext implementation #3854

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -30,9 +30,7 @@
import java.lang.reflect.InvocationTargetException;
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 @@ -50,18 +48,19 @@
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;

Expand All @@ -79,133 +78,127 @@
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.
*/
return null;
}
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) {
if (JavaVersionUtil.JAVA_SPEC > 11) {
throw ex;
} else {
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;

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);
@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 Throwable wrapCheckedException(Throwable ex) {
if (ex instanceof Exception && !(ex instanceof RuntimeException)) {
return new PrivilegedActionException((Exception) ex);
} else {
return ex;
PrivilegedStack.push(context, caller);
try {
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 wrapCheckedExceptionForPrivilegedAction(Throwable ex) {
if (JavaVersionUtil.JAVA_SPEC <= 11) {
return wrapCheckedException(ex);
@Substitute
@TargetElement(onlyWith = JDK17OrLater.class)
@SuppressWarnings("deprecation")
static AccessControlContext checkContext(AccessControlContext context, Class<?> caller) {

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