Skip to content

Commit 07a0c3f

Browse files
Proper getStackAccessControlContext implementation
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.
1 parent 6773ebc commit 07a0c3f

File tree

8 files changed

+512
-110
lines changed

8 files changed

+512
-110
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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+
import java.security.AccessControlContext;
28+
import java.security.ProtectionDomain;
29+
import java.util.ArrayDeque;
30+
import java.util.Objects;
31+
32+
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
33+
import com.oracle.svm.core.threadlocal.FastThreadLocalObject;
34+
import com.oracle.svm.core.util.VMError;
35+
import com.oracle.svm.util.ReflectionUtil;
36+
37+
/**
38+
* Stack for storing AccessControlContexts. Used in conjunction with
39+
* {@code StackAccessControlContextVisitor}.
40+
*/
41+
class PrivilegedStack {
42+
43+
public static class StackElement {
44+
protected AccessControlContext context;
45+
protected Class<?> caller;
46+
47+
StackElement(AccessControlContext context, Class<?> caller) {
48+
this.context = context;
49+
this.caller = caller;
50+
}
51+
52+
public AccessControlContext getContext() {
53+
return context;
54+
}
55+
56+
public Class<?> getCaller() {
57+
return caller;
58+
}
59+
}
60+
61+
/* Local AccessControlContext stack */
62+
private static final FastThreadLocalObject<ArrayDeque<StackElement>> stack;
63+
64+
static {
65+
@SuppressWarnings("unchecked")
66+
Class<ArrayDeque<StackElement>> cls = (Class<ArrayDeque<StackElement>>) (Object) ArrayDeque.class;
67+
stack = FastThreadLocalFactory.createObject(cls, "AccessControlContextStack");
68+
}
69+
70+
@SuppressWarnings("unchecked")
71+
private static ArrayDeque<StackElement> getStack() {
72+
if (stack.get() == null) {
73+
initializeStack();
74+
}
75+
return stack.get();
76+
}
77+
78+
private static void initializeStack() {
79+
ArrayDeque<StackElement> tmp = new ArrayDeque<>();
80+
stack.set(tmp);
81+
}
82+
83+
public static void push(AccessControlContext context, Class<?> caller) {
84+
getStack().push(new StackElement(context, caller));
85+
}
86+
87+
public static void pop() {
88+
getStack().pop();
89+
}
90+
91+
public static AccessControlContext peekContext() {
92+
return Objects.requireNonNull(getStack().peek()).getContext();
93+
}
94+
95+
public static Class<?> peekCaller() {
96+
return Objects.requireNonNull(getStack().peek()).getCaller();
97+
}
98+
99+
public static int length() {
100+
return getStack().size();
101+
}
102+
}
103+
104+
@InternalVMMethod
105+
@SuppressWarnings({"unused"})
106+
public class AccessControllerUtil {
107+
108+
/**
109+
* Instance that is used to mark contexts that were disallowed in
110+
* {@code AccessControlContextReplacerFeature.replaceAccessControlContext()} If this marker is
111+
* passed to {@code AccessController.doPrivileged()} a runtime error will be thrown.
112+
*/
113+
public static final AccessControlContext DISALLOWED_CONTEXT_MARKER;
114+
115+
static {
116+
try {
117+
DISALLOWED_CONTEXT_MARKER = ReflectionUtil.lookupConstructor(AccessControlContext.class, ProtectionDomain[].class, boolean.class).newInstance(new ProtectionDomain[0], true);
118+
} catch (ReflectiveOperationException ex) {
119+
throw VMError.shouldNotReachHere(ex);
120+
}
121+
}
122+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SecuritySubstitutions.java

Lines changed: 89 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -28,9 +28,7 @@
2828

2929
import java.net.URL;
3030
import java.security.AccessControlContext;
31-
import java.security.AccessControlException;
3231
import java.security.CodeSource;
33-
import java.security.DomainCombiner;
3432
import java.security.Permission;
3533
import java.security.PermissionCollection;
3634
import java.security.Permissions;
@@ -46,25 +44,26 @@
4644
import java.util.concurrent.atomic.AtomicReference;
4745
import java.util.function.Predicate;
4846

49-
import jdk.vm.ci.meta.MetaAccessProvider;
50-
import jdk.vm.ci.meta.ResolvedJavaField;
5147
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
5248
import org.graalvm.nativeimage.Platform;
5349
import org.graalvm.nativeimage.Platforms;
54-
import org.graalvm.nativeimage.hosted.Feature;
5550
import org.graalvm.word.Pointer;
5651

52+
import com.oracle.svm.core.SubstrateUtil;
5753
import com.oracle.svm.core.annotate.Alias;
58-
import com.oracle.svm.core.annotate.AutomaticFeature;
5954
import com.oracle.svm.core.annotate.Delete;
6055
import com.oracle.svm.core.annotate.InjectAccessors;
6156
import com.oracle.svm.core.annotate.NeverInline;
6257
import com.oracle.svm.core.annotate.RecomputeFieldValue;
6358
import com.oracle.svm.core.annotate.Substitute;
6459
import com.oracle.svm.core.annotate.TargetClass;
6560
import com.oracle.svm.core.annotate.TargetElement;
61+
import com.oracle.svm.core.graal.snippets.CEntryPointSnippets;
62+
import com.oracle.svm.core.thread.Target_java_lang_Thread;
6663
import com.oracle.svm.core.util.VMError;
67-
import com.oracle.svm.util.ReflectionUtil;
64+
65+
import jdk.vm.ci.meta.MetaAccessProvider;
66+
import jdk.vm.ci.meta.ResolvedJavaField;
6867

6968
// Checkstyle: stop
7069
import sun.security.jca.ProviderList;
@@ -82,133 +81,125 @@
8281
final class Target_java_security_AccessController {
8382

8483
@Substitute
85-
private static <T> T doPrivileged(PrivilegedAction<T> action) throws Throwable {
86-
try {
87-
return action.run();
88-
} catch (Throwable ex) {
89-
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
90-
}
84+
@TargetElement(onlyWith = JDK11OrEarlier.class)
85+
public static <T> T doPrivileged(PrivilegedAction<T> action) throws Throwable {
86+
return executePrivileged(action, null, Target_jdk_internal_reflect_Reflection.getCallerClass());
9187
}
9288

9389
@Substitute
94-
private static <T> T doPrivilegedWithCombiner(PrivilegedAction<T> action) throws Throwable {
95-
try {
96-
return action.run();
97-
} catch (Throwable ex) {
98-
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
99-
}
90+
@TargetElement(onlyWith = JDK11OrEarlier.class)
91+
public static <T> T doPrivileged(PrivilegedAction<T> action, AccessControlContext context) throws Throwable {
92+
Class<?> caller = Target_jdk_internal_reflect_Reflection.getCallerClass();
93+
AccessControlContext acc = checkContext(context, caller);
94+
return executePrivileged(action, acc, caller);
10095
}
10196

10297
@Substitute
103-
private static <T> T doPrivileged(PrivilegedAction<T> action, AccessControlContext context) throws Throwable {
104-
try {
105-
return action.run();
106-
} catch (Throwable ex) {
107-
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
108-
}
98+
@TargetElement(onlyWith = JDK11OrEarlier.class)
99+
public static <T> T doPrivileged(PrivilegedExceptionAction<T> action) throws Throwable {
100+
Class<?> caller = Target_jdk_internal_reflect_Reflection.getCallerClass();
101+
return executePrivileged(action, null, caller);
109102
}
110103

111104
@Substitute
112-
private static <T> T doPrivileged(PrivilegedAction<T> action, AccessControlContext context, Permission... perms) throws Throwable {
113-
try {
114-
return action.run();
115-
} catch (Throwable ex) {
116-
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
117-
}
105+
@TargetElement(onlyWith = JDK11OrEarlier.class)
106+
static <T> T doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context) throws Throwable {
107+
Class<?> caller = Target_jdk_internal_reflect_Reflection.getCallerClass();
108+
AccessControlContext acc = checkContext(context, caller);
109+
return executePrivileged(action, acc, caller);
118110
}
119111

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

129129
@Substitute
130-
private static <T> T doPrivilegedWithCombiner(PrivilegedExceptionAction<T> action) throws Throwable {
131-
try {
132-
return action.run();
133-
} catch (Throwable ex) {
134-
throw AccessControllerUtil.wrapCheckedException(ex);
135-
}
130+
static AccessControlContext getInheritedAccessControlContext() {
131+
return SubstrateUtil.cast(Thread.currentThread(), Target_java_lang_Thread.class).inheritedAccessControlContext;
136132
}
137133

138134
@Substitute
139-
private static <T> T doPrivilegedWithCombiner(PrivilegedExceptionAction<T> action, AccessControlContext context, Permission... perms) throws Throwable {
140-
try {
141-
return action.run();
142-
} catch (Throwable ex) {
143-
throw AccessControllerUtil.wrapCheckedException(ex);
144-
}
135+
@TargetElement(onlyWith = JDK17OrLater.class)
136+
private static ProtectionDomain getProtectionDomain(final Class<?> caller) {
137+
return caller.getProtectionDomain();
145138
}
146139

147140
@Substitute
148-
private static <T> T doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context) throws Throwable {
141+
@TargetElement(onlyWith = JDK17OrLater.class)
142+
@SuppressWarnings("deprecation") // deprecated starting JDK 17
143+
static <T> T executePrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context, Class<?> caller) throws Throwable {
144+
if (action == null) {
145+
throw new NullPointerException("Null action");
146+
}
147+
148+
PrivilegedStack.push(context, caller);
149149
try {
150150
return action.run();
151-
} catch (Throwable ex) {
152-
throw AccessControllerUtil.wrapCheckedException(ex);
151+
} catch (RuntimeException ex) {
152+
throw ex;
153+
} catch (Exception ex) {
154+
throw new PrivilegedActionException(ex);
155+
} finally {
156+
PrivilegedStack.pop();
153157
}
154158
}
155159

156160
@Substitute
157-
private static void checkPermission(Permission perm) throws AccessControlException {
158-
}
159-
160-
@Substitute
161-
private static AccessControlContext getContext() {
162-
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
163-
}
164-
165-
@Substitute
166-
private static AccessControlContext createWrapper(DomainCombiner combiner, Class<?> caller, AccessControlContext parent, AccessControlContext context, Permission[] perms) {
167-
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
168-
}
169-
}
170-
171-
@InternalVMMethod
172-
class AccessControllerUtil {
173-
174-
static final AccessControlContext NO_CONTEXT_SINGLETON;
161+
@TargetElement(onlyWith = JDK17OrLater.class)
162+
@SuppressWarnings("deprecation") // deprecated starting JDK 17
163+
static <T> T executePrivileged(PrivilegedAction<T> action, AccessControlContext context, Class<?> caller) throws Throwable {
164+
if (action == null) {
165+
throw new NullPointerException("Null action");
166+
}
175167

176-
static {
168+
PrivilegedStack.push(context, caller);
177169
try {
178-
NO_CONTEXT_SINGLETON = ReflectionUtil.lookupConstructor(AccessControlContext.class, ProtectionDomain[].class, boolean.class).newInstance(new ProtectionDomain[0], true);
179-
} catch (ReflectiveOperationException ex) {
180-
throw VMError.shouldNotReachHere(ex);
170+
return action.run();
171+
} catch (RuntimeException ex) {
172+
throw ex;
173+
} catch (Exception ex) {
174+
if (JavaVersionUtil.JAVA_SPEC > 11) {
175+
throw ex;
176+
} else {
177+
throw new PrivilegedActionException(ex);
178+
}
179+
} finally {
180+
PrivilegedStack.pop();
181181
}
182182
}
183183

184-
static Throwable wrapCheckedException(Throwable ex) {
185-
if (ex instanceof Exception && !(ex instanceof RuntimeException)) {
186-
return new PrivilegedActionException((Exception) ex);
187-
} else {
188-
return ex;
189-
}
190-
}
184+
@Substitute
185+
@TargetElement(onlyWith = JDK17OrLater.class)
186+
@SuppressWarnings("deprecation")
187+
static AccessControlContext checkContext(AccessControlContext context, Class<?> caller) {
191188

192-
static Throwable wrapCheckedExceptionForPrivilegedAction(Throwable ex) {
193-
if (JavaVersionUtil.JAVA_SPEC <= 11) {
194-
return wrapCheckedException(ex);
189+
if (context != null && context.equals(AccessControllerUtil.DISALLOWED_CONTEXT_MARKER)) {
190+
VMError.shouldNotReachHere("Non-allowed AccessControlContext that was replaced with a blank one at build time was invoked without being reinitialized at run time.\n" +
191+
"This might be an indicator of improper build time initialization, or of a non-compatible JDK version.\n" +
192+
"In order to fix this you can either:\n" +
193+
" * Annotate the offending context's field with @RecomputeFieldValue\n" +
194+
" * Implement a custom runtime accessor and annotate said field with @InjectAccessors\n" +
195+
" * If this context originates from the JDK, and it doesn't leak sensitive info, you can allow it in 'AccessControlContextReplacerFeature.duringSetup'");
195196
}
196-
return ex;
197-
}
198-
}
199-
200-
@AutomaticFeature
201-
class AccessControlContextFeature implements Feature {
202-
@Override
203-
public void duringSetup(DuringSetupAccess access) {
204-
access.registerObjectReplacer(AccessControlContextFeature::replaceAccessControlContext);
205-
}
206197

207-
private static Object replaceAccessControlContext(Object obj) {
208-
if (obj instanceof AccessControlContext) {
209-
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
198+
// check if caller is authorized to create context
199+
if (System.getSecurityManager() != null) {
200+
throw VMError.unsupportedFeature("SecurityManager isn't supported");
210201
}
211-
return obj;
202+
return context;
212203
}
213204
}
214205

0 commit comments

Comments
 (0)