Skip to content

Commit fa522f7

Browse files
[GR-33968] Proper getStackAccessControlContext implementation
PullRequest: graal/9946
2 parents 36016d4 + a767aaa commit fa522f7

File tree

9 files changed

+482
-107
lines changed

9 files changed

+482
-107
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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+
@SuppressWarnings({"unused"})
105+
public class AccessControllerUtil {
106+
107+
/**
108+
* Instance that is used to mark contexts that were disallowed in
109+
* {@code AccessControlContextReplacerFeature.replaceAccessControlContext()} If this marker is
110+
* passed to {@code AccessController.doPrivileged()} a runtime error will be thrown.
111+
*/
112+
public static final AccessControlContext DISALLOWED_CONTEXT_MARKER;
113+
114+
static {
115+
try {
116+
DISALLOWED_CONTEXT_MARKER = ReflectionUtil.lookupConstructor(AccessControlContext.class, ProtectionDomain[].class, boolean.class).newInstance(new ProtectionDomain[0], true);
117+
} catch (ReflectiveOperationException ex) {
118+
throw VMError.shouldNotReachHere(ex);
119+
}
120+
}
121+
}

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

Lines changed: 89 additions & 96 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
@@ -30,9 +30,7 @@
3030
import java.lang.reflect.InvocationTargetException;
3131
import java.net.URL;
3232
import java.security.AccessControlContext;
33-
import java.security.AccessControlException;
3433
import java.security.CodeSource;
35-
import java.security.DomainCombiner;
3634
import java.security.Permission;
3735
import java.security.PermissionCollection;
3836
import java.security.Permissions;
@@ -50,18 +48,19 @@
5048
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
5149
import org.graalvm.nativeimage.Platform;
5250
import org.graalvm.nativeimage.Platforms;
53-
import org.graalvm.nativeimage.hosted.Feature;
5451
import org.graalvm.word.Pointer;
5552

53+
import com.oracle.svm.core.SubstrateUtil;
5654
import com.oracle.svm.core.annotate.Alias;
57-
import com.oracle.svm.core.annotate.AutomaticFeature;
5855
import com.oracle.svm.core.annotate.Delete;
5956
import com.oracle.svm.core.annotate.InjectAccessors;
6057
import com.oracle.svm.core.annotate.NeverInline;
6158
import com.oracle.svm.core.annotate.RecomputeFieldValue;
6259
import com.oracle.svm.core.annotate.Substitute;
6360
import com.oracle.svm.core.annotate.TargetClass;
6461
import com.oracle.svm.core.annotate.TargetElement;
62+
import com.oracle.svm.core.graal.snippets.CEntryPointSnippets;
63+
import com.oracle.svm.core.thread.Target_java_lang_Thread;
6564
import com.oracle.svm.core.util.VMError;
6665
import com.oracle.svm.util.ReflectionUtil;
6766

@@ -79,133 +78,127 @@
7978
final class Target_java_security_AccessController {
8079

8180
@Substitute
82-
private static <T> T doPrivileged(PrivilegedAction<T> action) throws Throwable {
83-
try {
84-
return action.run();
85-
} catch (Throwable ex) {
86-
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
87-
}
81+
@TargetElement(onlyWith = JDK11OrEarlier.class)
82+
public static <T> T doPrivileged(PrivilegedAction<T> action) throws Throwable {
83+
return executePrivileged(action, null, Target_jdk_internal_reflect_Reflection.getCallerClass());
8884
}
8985

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

9994
@Substitute
100-
private static <T> T doPrivileged(PrivilegedAction<T> action, AccessControlContext context) throws Throwable {
101-
try {
102-
return action.run();
103-
} catch (Throwable ex) {
104-
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
105-
}
95+
@TargetElement(onlyWith = JDK11OrEarlier.class)
96+
public static <T> T doPrivileged(PrivilegedExceptionAction<T> action) throws Throwable {
97+
Class<?> caller = Target_jdk_internal_reflect_Reflection.getCallerClass();
98+
return executePrivileged(action, null, caller);
10699
}
107100

108101
@Substitute
109-
private static <T> T doPrivileged(PrivilegedAction<T> action, AccessControlContext context, Permission... perms) throws Throwable {
110-
try {
111-
return action.run();
112-
} catch (Throwable ex) {
113-
throw AccessControllerUtil.wrapCheckedExceptionForPrivilegedAction(ex);
114-
}
102+
@TargetElement(onlyWith = JDK11OrEarlier.class)
103+
static <T> T doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context) throws Throwable {
104+
Class<?> caller = Target_jdk_internal_reflect_Reflection.getCallerClass();
105+
AccessControlContext acc = checkContext(context, caller);
106+
return executePrivileged(action, acc, caller);
115107
}
116108

117109
@Substitute
118-
private static <T> T doPrivileged(PrivilegedExceptionAction<T> action) throws Throwable {
119-
try {
120-
return action.run();
121-
} catch (Throwable ex) {
122-
throw AccessControllerUtil.wrapCheckedException(ex);
110+
@SuppressWarnings("deprecation")
111+
static AccessControlContext getStackAccessControlContext() {
112+
if (!CEntryPointSnippets.isIsolateInitialized()) {
113+
/*
114+
* If isolate still isn't initialized, we can assume that we are so early in the JDK
115+
* initialization that any attempt at stalk walk will fail as not even the basic
116+
* PrintWriter/Logging is available yet. This manifested when
117+
* UseDedicatedVMOperationThread hosted option was set, triggering a runtime crash.
118+
*/
119+
return null;
123120
}
121+
return StackAccessControlContextVisitor.getFromStack();
124122
}
125123

126124
@Substitute
127-
private static <T> T doPrivilegedWithCombiner(PrivilegedExceptionAction<T> action) throws Throwable {
128-
try {
129-
return action.run();
130-
} catch (Throwable ex) {
131-
throw AccessControllerUtil.wrapCheckedException(ex);
132-
}
125+
static AccessControlContext getInheritedAccessControlContext() {
126+
return SubstrateUtil.cast(Thread.currentThread(), Target_java_lang_Thread.class).inheritedAccessControlContext;
133127
}
134128

135129
@Substitute
136-
private static <T> T doPrivilegedWithCombiner(PrivilegedExceptionAction<T> action, AccessControlContext context, Permission... perms) throws Throwable {
137-
try {
138-
return action.run();
139-
} catch (Throwable ex) {
140-
throw AccessControllerUtil.wrapCheckedException(ex);
141-
}
130+
@TargetElement(onlyWith = JDK17OrLater.class)
131+
private static ProtectionDomain getProtectionDomain(final Class<?> caller) {
132+
return caller.getProtectionDomain();
142133
}
143134

144135
@Substitute
145-
private static <T> T doPrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context) throws Throwable {
136+
@TargetElement(onlyWith = JDK17OrLater.class)
137+
@SuppressWarnings("deprecation") // deprecated starting JDK 17
138+
static <T> T executePrivileged(PrivilegedExceptionAction<T> action, AccessControlContext context, Class<?> caller) throws Throwable {
139+
if (action == null) {
140+
throw new NullPointerException("Null action");
141+
}
142+
143+
PrivilegedStack.push(context, caller);
146144
try {
147145
return action.run();
148-
} catch (Throwable ex) {
149-
throw AccessControllerUtil.wrapCheckedException(ex);
146+
} catch (RuntimeException ex) {
147+
throw ex;
148+
} catch (Exception ex) {
149+
if (JavaVersionUtil.JAVA_SPEC > 11) {
150+
throw ex;
151+
} else {
152+
throw new PrivilegedActionException(ex);
153+
}
154+
} finally {
155+
PrivilegedStack.pop();
150156
}
151157
}
152158

153159
@Substitute
154-
private static void checkPermission(Permission perm) throws AccessControlException {
155-
}
156-
157-
@Substitute
158-
private static AccessControlContext getContext() {
159-
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
160-
}
161-
162-
@Substitute
163-
private static AccessControlContext createWrapper(DomainCombiner combiner, Class<?> caller, AccessControlContext parent, AccessControlContext context, Permission[] perms) {
164-
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
165-
}
166-
}
167-
168-
@InternalVMMethod
169-
class AccessControllerUtil {
170-
171-
static final AccessControlContext NO_CONTEXT_SINGLETON;
172-
173-
static {
174-
try {
175-
NO_CONTEXT_SINGLETON = ReflectionUtil.lookupConstructor(AccessControlContext.class, ProtectionDomain[].class, boolean.class).newInstance(new ProtectionDomain[0], true);
176-
} catch (ReflectiveOperationException ex) {
177-
throw VMError.shouldNotReachHere(ex);
160+
@TargetElement(onlyWith = JDK17OrLater.class)
161+
@SuppressWarnings("deprecation") // deprecated starting JDK 17
162+
static <T> T executePrivileged(PrivilegedAction<T> action, AccessControlContext context, Class<?> caller) throws Throwable {
163+
if (action == null) {
164+
throw new NullPointerException("Null action");
178165
}
179-
}
180166

181-
static Throwable wrapCheckedException(Throwable ex) {
182-
if (ex instanceof Exception && !(ex instanceof RuntimeException)) {
183-
return new PrivilegedActionException((Exception) ex);
184-
} else {
185-
return ex;
167+
PrivilegedStack.push(context, caller);
168+
try {
169+
return action.run();
170+
} catch (RuntimeException ex) {
171+
throw ex;
172+
} catch (Exception ex) {
173+
if (JavaVersionUtil.JAVA_SPEC > 11) {
174+
throw ex;
175+
} else {
176+
throw new PrivilegedActionException(ex);
177+
}
178+
} finally {
179+
PrivilegedStack.pop();
186180
}
187181
}
188182

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

204-
private static Object replaceAccessControlContext(Object obj) {
205-
if (obj instanceof AccessControlContext) {
206-
return AccessControllerUtil.NO_CONTEXT_SINGLETON;
197+
// check if caller is authorized to create context
198+
if (System.getSecurityManager() != null) {
199+
throw VMError.unsupportedFeature("SecurityManager isn't supported");
207200
}
208-
return obj;
201+
return context;
209202
}
210203
}
211204

0 commit comments

Comments
 (0)