Skip to content

Commit 5a8c8f2

Browse files
committed
Allow URL.of(uri, null) in TCK permission checker.
1 parent df624d2 commit 5a8c8f2

File tree

4 files changed

+144
-10
lines changed

4 files changed

+144
-10
lines changed

substratevm/src/com.oracle.svm.truffle.tck/src/com/oracle/svm/truffle/tck/PermissionsFeature.java

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
import java.lang.reflect.Method;
3333
import java.lang.reflect.Proxy;
3434
import java.math.BigInteger;
35+
import java.net.URI;
36+
import java.net.URL;
37+
import java.net.URLStreamHandler;
3538
import java.nio.file.Files;
3639
import java.nio.file.Path;
3740
import java.nio.file.Paths;
@@ -57,7 +60,9 @@
5760

5861
import com.oracle.svm.hosted.code.FactoryMethod;
5962
import com.oracle.svm.util.LogUtils;
63+
import jdk.graal.compiler.nodes.ConstantNode;
6064
import jdk.graal.compiler.nodes.virtual.AllocatedObjectNode;
65+
import jdk.vm.ci.meta.JavaConstant;
6166
import org.graalvm.nativeimage.ImageSingletons;
6267
import org.graalvm.nativeimage.hosted.Feature;
6368
import org.graalvm.polyglot.io.FileSystem;
@@ -244,7 +249,8 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
244249
contextFilters = new HashSet<>();
245250
Collections.addAll(contextFilters, new SafeInterruptRecognizer(bb), new SafePrivilegedRecognizer(bb),
246251
new SafeReflectionRecognizer(bb), new SafeSetThreadNameRecognizer(bb),
247-
new SafeLocaleServiceProvider(bb), new SafeSystemGetProperty(bb));
252+
new SafeLocaleServiceProvider(bb), new SafeSystemGetProperty(bb),
253+
new SafeURLOf(bb));
248254

249255
/*
250256
* Ensure methods which are either deniedMethods or on the allow list are never inlined into
@@ -1069,6 +1075,61 @@ public Collection<AnalysisMethod> getInspectedMethods() {
10691075
}
10701076
}
10711077

1078+
/**
1079+
* A call to {@code URL.of(URI, URLStreamHandler)} is only considered safe when the specified
1080+
* {@link URLStreamHandler} is {@code null}.
1081+
*/
1082+
private static final class SafeURLOf implements CallGraphFilter {
1083+
1084+
private final SVMHost hostVM;
1085+
private final AnalysisMethodNode of;
1086+
1087+
SafeURLOf(BigBang bigBang) {
1088+
this.hostVM = (SVMHost) bigBang.getHostVM();
1089+
Set<AnalysisMethodNode> methods = findMethods(bigBang, URL.class, (m) -> {
1090+
if (!"of".equals(m.getName())) {
1091+
return false;
1092+
}
1093+
ResolvedJavaMethod.Parameter[] parameters = m.getParameters();
1094+
if (parameters.length != 2) {
1095+
return false;
1096+
}
1097+
if (!bigBang.getMetaAccess().lookupJavaType(URI.class).getWrapped().equals(parameters[0].getType())) {
1098+
return false;
1099+
}
1100+
return bigBang.getMetaAccess().lookupJavaType(URLStreamHandler.class).getWrapped().equals(parameters[1].getType());
1101+
});
1102+
if (methods.size() != 1) {
1103+
throw new IllegalStateException("Failed to lookup URL.of(URI, URLStreamHandler).");
1104+
}
1105+
this.of = methods.iterator().next();
1106+
}
1107+
1108+
@Override
1109+
public boolean test(BaseMethodNode methodNode, BaseMethodNode callerNode, List<BaseMethodNode> trace) {
1110+
if (!of.equals(methodNode)) {
1111+
return false;
1112+
}
1113+
AnalysisMethod method = methodNode.getMethod();
1114+
AnalysisMethod caller = callerNode.getMethod();
1115+
StructuredGraph graph = hostVM.getAnalysisGraph(caller);
1116+
Boolean res = null;
1117+
for (Invoke invoke : graph.getInvokes()) {
1118+
if (method.equals(invoke.callTarget().targetMethod())) {
1119+
NodeInputList<ValueNode> args = invoke.callTarget().arguments();
1120+
ValueNode arg1 = args.get(1);
1121+
res = arg1 instanceof ConstantNode constantNode && constantNode.getValue() == JavaConstant.NULL_POINTER;
1122+
}
1123+
}
1124+
return res != null && res;
1125+
}
1126+
1127+
@Override
1128+
public Collection<AnalysisMethod> getInspectedMethods() {
1129+
return List.of(of.getMethod());
1130+
}
1131+
}
1132+
10721133
abstract static class BaseMethodNode {
10731134
abstract StackTraceElement asStackTraceElement();
10741135

truffle/mx.truffle/suite.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@
870870
"jdk.unsupported", # sun.misc.Unsafe
871871
],
872872
"checkstyle" : "com.oracle.truffle.api",
873-
"javaCompliance" : "17+",
873+
"javaCompliance" : "20+",
874874
"annotationProcessors" : ["TRUFFLE_DSL_PROCESSOR"],
875875
"workingSets" : "Truffle,Test",
876876
"jacoco" : "exclude",
@@ -2028,7 +2028,7 @@
20282028

20292029
"TRUFFLE_TCK_TESTS_LANGUAGE" : {
20302030
"subDir" : "src",
2031-
"javaCompliance" : "17+",
2031+
"javaCompliance" : "20+",
20322032
"dependencies" : [
20332033
"com.oracle.truffle.tck.tests.language"
20342034
],

truffle/src/com.oracle.truffle.tck.tests.language/src/com/oracle/truffle/tck/tests/language/TCKSmokeTestLanguage.java

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
package com.oracle.truffle.tck.tests.language;
4242

4343
import com.oracle.truffle.api.CallTarget;
44+
import com.oracle.truffle.api.CompilerDirectives;
4445
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4546
import com.oracle.truffle.api.TruffleLanguage;
4647
import com.oracle.truffle.api.TruffleLanguage.Registration;
@@ -64,6 +65,8 @@
6465
import java.net.MalformedURLException;
6566
import java.net.URI;
6667
import java.net.URL;
68+
import java.net.URLConnection;
69+
import java.net.URLStreamHandler;
6770
import java.nio.file.Files;
6871
import java.nio.file.Path;
6972
import java.util.ServiceLoader;
@@ -88,7 +91,11 @@ protected CallTarget parse(ParsingRequest request) {
8891
Thread thread = new Thread(() -> {
8992
});
9093
URL url = URI.create("http://localhost").toURL();
91-
RootNode root = new RootNodeImpl(this, new PrivilegedCallNode(thread, url), new UnsafeCallNode());
94+
RootNode root = new RootNodeImpl(this,
95+
new PrivilegedCallNode(thread, url),
96+
new UnsafeCallNode(),
97+
new AllowedURLNode(),
98+
new DeniedURLNode());
9299
return root.getCallTarget();
93100
} catch (MalformedURLException urlException) {
94101
throw new AssertionError(urlException);
@@ -270,4 +277,60 @@ static void doBehindBoundaryUnsafeAccess() {
270277
assert i == result;
271278
}
272279
}
280+
281+
private static final class AllowedURLNode extends BaseNode {
282+
283+
private final URI currentWorkingDirectory;
284+
285+
AllowedURLNode() {
286+
this.currentWorkingDirectory = Path.of("").toAbsolutePath().toUri();
287+
}
288+
289+
@Override
290+
void execute(VirtualFrame frame) {
291+
doURLOf();
292+
}
293+
294+
@TruffleBoundary
295+
private void doURLOf() {
296+
try {
297+
URL.of(currentWorkingDirectory, null);
298+
} catch (MalformedURLException e) {
299+
throw CompilerDirectives.shouldNotReachHere(e);
300+
}
301+
}
302+
}
303+
304+
private static final class DeniedURLNode extends BaseNode {
305+
306+
private final URLStreamHandler handler;
307+
private final URI currentWorkingDirectory;
308+
309+
DeniedURLNode() {
310+
this.handler = new MockURLStreamHandler();
311+
this.currentWorkingDirectory = Path.of("").toAbsolutePath().toUri();
312+
}
313+
314+
@Override
315+
void execute(VirtualFrame frame) {
316+
doURLOf();
317+
}
318+
319+
@TruffleBoundary
320+
private void doURLOf() {
321+
try {
322+
URL.of(currentWorkingDirectory, handler);
323+
} catch (MalformedURLException e) {
324+
throw CompilerDirectives.shouldNotReachHere(e);
325+
}
326+
}
327+
328+
private static final class MockURLStreamHandler extends URLStreamHandler {
329+
330+
@Override
331+
protected URLConnection openConnection(URL u) {
332+
throw new UnsupportedOperationException();
333+
}
334+
}
335+
}
273336
}

vm/mx.vm/mx_vm_gate.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ def _collect_excludes(suite, suite_import, excludes):
620620
excludes = []
621621
language_distribution.suite.visit_imports(_collect_excludes, excludes=excludes)
622622
svmbuild = mkdtemp()
623+
success = False
623624
try:
624625
report_file = join(svmbuild, "language_permissions.log")
625626
options = mx.get_runtime_jvm_args(dists, exclude_names=['substratevm:SVM']) + [
@@ -647,8 +648,11 @@ def _collect_excludes(suite, suite_import, excludes):
647648
mx.abort(message)
648649
else:
649650
return message
651+
else:
652+
success = True
650653
finally:
651-
mx.rmtree(svmbuild)
654+
if success:
655+
mx.rmtree(svmbuild)
652656
return None
653657

654658

@@ -663,7 +667,7 @@ def gate_truffle_native_tck_smoke_test(tasks):
663667
if not 'Failed: Language TCKSmokeTestLanguage performs following privileged calls' in result:
664668
mx.abort("Expected failure, log:\n" + result)
665669

666-
expected_methods = [
670+
must_have_methods = [
667671
'PrivilegedCallNode.callConstructorReflectively',
668672
'PrivilegedCallNode.callMethodHandle',
669673
'PrivilegedCallNode.callMethodReflectively',
@@ -674,11 +678,17 @@ def gate_truffle_native_tck_smoke_test(tasks):
674678
'ServiceImpl.execute',
675679
'UnsafeCallNode.doBehindBoundaryUnsafeAccess',
676680
'UnsafeCallNode.doUnsafeAccess',
681+
'DeniedURLNode.doURLOf'
677682
]
678-
for expected_method in expected_methods:
679-
if expected_method not in result:
680-
mx.abort(f"Missing {expected_method} call in the log.\nLog content:\n" + result)
681-
683+
must_not_have_methods = [
684+
'AllowedURLNode.doURLOf'
685+
]
686+
for method in must_have_methods:
687+
if method not in result:
688+
mx.abort(f"Missing {method} call in the log.\nLog content:\n" + result)
689+
for method in must_not_have_methods:
690+
if method in result:
691+
mx.abort(f"Found {method} call in the log.\nLog content:\n" + result)
682692

683693
def gate_truffle_native_tck_js(tasks):
684694
with Task('JavaScript Truffle Native TCK', tasks, tags=[VmGateTasks.truffle_native_tck_js]) as t:

0 commit comments

Comments
 (0)