Skip to content

Commit 4664c29

Browse files
committed
[GR-61457] Warn about unused language allow list entries in language permissions tool.
PullRequest: graal/19829
2 parents d2dab91 + 44befdb commit 4664c29

File tree

5 files changed

+76
-90
lines changed

5 files changed

+76
-90
lines changed

substratevm/src/com.oracle.svm.truffle.tck/src/META-INF/native-image/native-image.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ ProvidedHostedOptions = \
2323
TruffleTCKPermissionsReportFile= \
2424
TruffleTCKPermissionsExcludeFiles= \
2525
TruffleTCKPermissionsMaxStackTraceDepth= \
26-
TruffleTCKPermissionsMaxErrors=
26+
TruffleTCKPermissionsMaxErrors= \
27+
TruffleTCKUnusedAllowListEntriesAction=

substratevm/src/com.oracle.svm.truffle.tck/src/com/oracle/svm/truffle/tck/WhiteListParser.java renamed to substratevm/src/com.oracle.svm.truffle.tck/src/com/oracle/svm/truffle/tck/AllowListParser.java

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -53,32 +53,27 @@
5353
import jdk.vm.ci.meta.ResolvedJavaType;
5454
import jdk.vm.ci.meta.Signature;
5555

56-
final class WhiteListParser extends ConfigurationParser {
56+
final class AllowListParser extends ConfigurationParser {
5757

5858
private static final String CONSTRUCTOR_NAME = "<init>";
5959

6060
private final ImageClassLoader imageClassLoader;
6161
private final BigBang bb;
62-
private Set<AnalysisMethodNode> whiteList;
62+
private final Set<AnalysisMethodNode> allowList;
6363

64-
WhiteListParser(ImageClassLoader imageClassLoader, BigBang bb) {
64+
AllowListParser(ImageClassLoader imageClassLoader, BigBang bb) {
6565
super(true);
6666
this.imageClassLoader = Objects.requireNonNull(imageClassLoader, "ImageClassLoader must be non null");
6767
this.bb = Objects.requireNonNull(bb, "BigBang must be non null");
68+
this.allowList = new HashSet<>();
6869
}
6970

70-
Set<AnalysisMethodNode> getLoadedWhiteList() {
71-
if (whiteList == null) {
72-
throw new IllegalStateException("Not parsed yet.");
73-
}
74-
return whiteList;
71+
Set<AnalysisMethodNode> getLoadedAllowList() {
72+
return allowList;
7573
}
7674

7775
@Override
7876
public void parseAndRegister(Object json, URI origin) {
79-
if (whiteList == null) {
80-
whiteList = new HashSet<>();
81-
}
8277
parseClassArray(castList(json, "First level of document must be an array of class descriptors"));
8378
}
8479

@@ -211,33 +206,33 @@ private boolean registerMethod(AnalysisType type, String methodName, List<Analys
211206
Predicate<ResolvedJavaMethod> p = (m) -> methodName.equals(m.getName());
212207
p = p.and(new SignaturePredicate(type, formalParameters, bb));
213208
Set<AnalysisMethodNode> methods = PermissionsFeature.findMethods(bb, type, p);
214-
whiteList.addAll(methods);
209+
allowList.addAll(methods);
215210
return !methods.isEmpty();
216211
}
217212

218213
private boolean registerAllMethodsWithName(AnalysisType type, String name) {
219214
Set<AnalysisMethodNode> methods = PermissionsFeature.findMethods(bb, type, (m) -> name.equals(m.getName()));
220-
whiteList.addAll(methods);
215+
allowList.addAll(methods);
221216
return !methods.isEmpty();
222217
}
223218

224219
private boolean registerConstructor(AnalysisType type, List<AnalysisType> formalParameters) {
225220
Predicate<ResolvedJavaMethod> p = new SignaturePredicate(type, formalParameters, bb);
226221
Set<AnalysisMethodNode> methods = PermissionsFeature.findConstructors(bb, type, p);
227-
whiteList.addAll(methods);
222+
allowList.addAll(methods);
228223
return !methods.isEmpty();
229224
}
230225

231226
private boolean registerDeclaredConstructors(AnalysisType type) {
232227
for (AnalysisMethod method : type.getDeclaredConstructors(false)) {
233-
whiteList.add(new AnalysisMethodNode(method));
228+
allowList.add(new AnalysisMethodNode(method));
234229
}
235230
return true;
236231
}
237232

238233
private boolean registerDeclaredMethods(AnalysisType type) {
239234
for (AnalysisMethod method : type.getDeclaredMethods(false)) {
240-
whiteList.add(new AnalysisMethodNode(method));
235+
allowList.add(new AnalysisMethodNode(method));
241236
}
242237
return true;
243238
}

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

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@
4949
import java.util.Objects;
5050
import java.util.Set;
5151
import java.util.function.BooleanSupplier;
52+
import java.util.function.Function;
5253
import java.util.function.Predicate;
5354
import java.util.stream.Collectors;
5455

5556
import com.oracle.svm.hosted.code.FactoryMethod;
57+
import com.oracle.svm.util.LogUtils;
5658
import org.graalvm.nativeimage.ImageSingletons;
5759
import org.graalvm.nativeimage.hosted.Feature;
5860
import org.graalvm.polyglot.io.FileSystem;
@@ -110,6 +112,12 @@ public class PermissionsFeature implements Feature {
110112

111113
private static final String CONFIG = "truffle-language-permissions-config.json";
112114

115+
public enum ActionKind {
116+
Ignore,
117+
Warn,
118+
Throw
119+
}
120+
113121
public static class Options {
114122
@Option(help = "Path to file where to store report of Truffle language privilege access.")//
115123
public static final HostedOptionKey<String> TruffleTCKPermissionsReportFile = new HostedOptionKey<>(null);
@@ -124,6 +132,13 @@ public static class Options {
124132

125133
@Option(help = "Maximum number of erroneous privileged accesses reported.", type = OptionType.Expert)//
126134
public static final HostedOptionKey<Integer> TruffleTCKPermissionsMaxErrors = new HostedOptionKey<>(100);
135+
136+
@Option(help = {"Specifies how unused methods in the language allow list should be handled.",
137+
"Available options are:",
138+
" \"Ignore\": Do not report unused methods in the allow list.",
139+
" \"Warn\": Log a warning message to stderr.",
140+
" \"Throw\" (default): Throw an exception and abort the native-image build process."}, type = OptionType.Expert)//
141+
public static final HostedOptionKey<ActionKind> TruffleTCKUnusedAllowListEntriesAction = new HostedOptionKey<>(ActionKind.Throw);
127142
}
128143

129144
/**
@@ -178,17 +193,22 @@ public boolean getAsBoolean() {
178193
/**
179194
* Methods which should not be found.
180195
*/
181-
private Set<BaseMethodNode> deniedMethods = new HashSet<>();
196+
private final Set<BaseMethodNode> deniedMethods = new HashSet<>();
182197

183198
/**
184199
* Path to store report into.
185200
*/
186201
private Path reportFilePath;
187202

188203
/**
189-
* Methods which are allowed to do privileged calls without being reported.
204+
* JDK methods which are allowed to do privileged calls without being reported.
190205
*/
191-
private Set<? extends BaseMethodNode> whiteList;
206+
private Set<? extends BaseMethodNode> platformAllowList;
207+
208+
/**
209+
* Language methods which are allowed to do privileged calls without being reported.
210+
*/
211+
private Map<BaseMethodNode, Boolean> languageAllowList;
192212

193213
private Set<CallGraphFilter> contextFilters;
194214

@@ -229,12 +249,13 @@ public void beforeAnalysis(BeforeAnalysisAccess access) {
229249
new SafeServiceLoaderRecognizer(bb, accessImpl.getImageClassLoader()), new SafeSetThreadNameRecognizer(bb));
230250

231251
/*
232-
* Ensure methods which are either deniedMethods or on the whiteList are never inlined into
252+
* Ensure methods which are either deniedMethods or on the allow list are never inlined into
233253
* methods. These methods are important for identifying violations.
234254
*/
235255
Set<AnalysisMethod> preventInlineBeforeAnalysis = new HashSet<>();
236256
deniedMethods.stream().map(BaseMethodNode::getMethod).forEach(preventInlineBeforeAnalysis::add);
237-
whiteList.stream().map(BaseMethodNode::getMethod).forEach(preventInlineBeforeAnalysis::add);
257+
platformAllowList.stream().map(BaseMethodNode::getMethod).forEach(preventInlineBeforeAnalysis::add);
258+
languageAllowList.keySet().stream().map(BaseMethodNode::getMethod).forEach(preventInlineBeforeAnalysis::add);
238259
contextFilters.stream().map(CallGraphFilter::getInspectedMethods).forEach(preventInlineBeforeAnalysis::addAll);
239260

240261
accessImpl.getHostVM().registerNeverInlineTrivialHandler((caller, callee) -> {
@@ -257,14 +278,19 @@ private void initializeDeniedMethods(FeatureImpl.BeforeAnalysisAccessImpl access
257278
if (sunMiscUnsafe != null) {
258279
inlinedUnsafeCall = new InlinedUnsafeMethodNode(bb.getMetaAccess().lookupJavaType(sunMiscUnsafe));
259280
}
260-
WhiteListParser parser = new WhiteListParser(accessImpl.getImageClassLoader(), bb);
261-
ConfigurationParserUtils.parseAndRegisterConfigurations(parser,
262-
accessImpl.getImageClassLoader(),
263-
ClassUtil.getUnqualifiedName(getClass()),
264-
Options.TruffleTCKPermissionsExcludeFiles,
265-
new ResourceAsOptionDecorator(getClass().getPackage().getName().replace('.', '/') + "/resources/jre.json"),
266-
CONFIG);
267-
whiteList = parser.getLoadedWhiteList();
281+
String featureName = ClassUtil.getUnqualifiedName(getClass());
282+
AllowListParser parser = new AllowListParser(accessImpl.getImageClassLoader(), bb);
283+
ConfigurationParserUtils.parseAndRegisterConfigurations(parser, accessImpl.getImageClassLoader(), featureName,
284+
CONFIG,
285+
List.of(),
286+
List.of(getClass().getPackage().getName().replace('.', '/') + "/resources/jre.json"));
287+
platformAllowList = parser.getLoadedAllowList();
288+
parser = new AllowListParser(accessImpl.getImageClassLoader(), bb);
289+
ConfigurationParserUtils.parseAndRegisterConfigurations(parser, accessImpl.getImageClassLoader(), featureName,
290+
CONFIG,
291+
Options.TruffleTCKPermissionsExcludeFiles.getValue().values(),
292+
List.of());
293+
languageAllowList = parser.getLoadedAllowList().stream().collect(Collectors.toMap(Function.identity(), key -> false));
268294
deniedMethods.addAll(findMethods(bb, SecurityManager.class, (m) -> m.getName().startsWith("check")));
269295
if (sunMiscUnsafe != null) {
270296
deniedMethods.addAll(findMethods(bb, sunMiscUnsafe, ModifiersProvider::isPublic));
@@ -321,6 +347,21 @@ public void afterAnalysis(AfterAnalysisAccess access) {
321347
pw.print(builder);
322348
});
323349
}
350+
List<BaseMethodNode> unusedLanguageAllowListEntries = languageAllowList.entrySet().stream().filter((e) -> !e.getValue()).map(Map.Entry::getKey).toList();
351+
if (!unusedLanguageAllowListEntries.isEmpty()) {
352+
StringBuilder errorMessageBuilder = new StringBuilder(
353+
"The following methods in the language allow list were not statically reachable during points-to analysis. " + "Please review and remove them from the allow list:\n");
354+
for (BaseMethodNode unused : unusedLanguageAllowListEntries) {
355+
errorMessageBuilder.append(" - ").append(unused.getMethod().format("%H.%n(%p)")).append("\n");
356+
}
357+
switch (Options.TruffleTCKUnusedAllowListEntriesAction.getValue()) {
358+
case Ignore -> {
359+
}
360+
case Warn -> LogUtils.warning("[%s] %s", ClassUtil.getUnqualifiedName(getClass()), errorMessageBuilder);
361+
case Throw -> throw UserError.abort(errorMessageBuilder.toString());
362+
default -> throw new AssertionError(Options.TruffleTCKUnusedAllowListEntriesAction.getValue());
363+
}
364+
}
324365
}
325366
}
326367

@@ -533,7 +574,7 @@ private int collectViolations(
533574
if (isSafeClass(mNode)) {
534575
return numReports;
535576
}
536-
// The denied method can be excluded by a white list
577+
// The denied method can be excluded by a allow list
537578
if (isExcludedClass(mNode)) {
538579
return numReports;
539580
}
@@ -618,12 +659,15 @@ private static boolean isClassInPackage(String javaName, Collection<? extends St
618659
}
619660

620661
/**
621-
* Tests if the given {@link BaseMethodNode} is excluded by white list.
662+
* Tests if the given {@link BaseMethodNode} is excluded by allow list.
622663
*
623664
* @param methodNode the {@link BaseMethodNode} to check
624665
*/
625666
private boolean isExcludedClass(BaseMethodNode methodNode) {
626-
return whiteList.contains(methodNode);
667+
if (platformAllowList.contains(methodNode)) {
668+
return true;
669+
}
670+
return languageAllowList.computeIfPresent(methodNode, (n, v) -> true) != null;
627671
}
628672

629673
/**
@@ -783,7 +827,7 @@ public boolean test(BaseMethodNode methodNode, BaseMethodNode callerNode, List<B
783827
if (args.isEmpty()) {
784828
return false;
785829
}
786-
ValueNode arg0 = args.get(0);
830+
ValueNode arg0 = args.getFirst();
787831
ResolvedJavaType newType = null;
788832
if (arg0 instanceof NewInstanceNode newInstanceNode) {
789833
newType = newInstanceNode.instanceClass();
@@ -938,7 +982,7 @@ public boolean test(BaseMethodNode methodNode, BaseMethodNode callerNode, List<B
938982
for (Invoke invoke : graph.getInvokes()) {
939983
if (method.equals(invoke.callTarget().targetMethod())) {
940984
NodeInputList<ValueNode> args = invoke.callTarget().arguments();
941-
ValueNode arg0 = args.get(0);
985+
ValueNode arg0 = args.getFirst();
942986
boolean isTruffleThread = false;
943987
if (arg0 instanceof PiNode piNode) {
944988
arg0 = piNode.getOriginalNode();
@@ -962,16 +1006,6 @@ public Collection<AnalysisMethod> getInspectedMethods() {
9621006
}
9631007
}
9641008

965-
/**
966-
* Options facade for a resource containing the JRE white list.
967-
*/
968-
private static final class ResourceAsOptionDecorator extends HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> {
969-
970-
ResourceAsOptionDecorator(String defaultValue) {
971-
super(AccumulatingLocatableMultiOptionValue.Strings.buildWithDefaults(defaultValue));
972-
}
973-
}
974-
9751009
abstract static class BaseMethodNode {
9761010
abstract StackTraceElement asStackTraceElement();
9771011

vm/mx.vm/mx_vm_gate.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ def _collect_excludes(suite, suite_import, excludes):
652652
options = mx.get_runtime_jvm_args(dists, exclude_names=['substratevm:SVM']) + [
653653
'--features=com.oracle.svm.truffle.tck.PermissionsFeature',
654654
] + mx_sdk_vm_impl.svm_experimental_options([
655+
'-H:TruffleTCKUnusedAllowListEntriesAction=Warn', # GR-61487: Clean JavaScript allow list
655656
'-H:ClassInitialization=:build_time',
656657
'-H:+EnforceMaxRuntimeCompileMethods',
657658
'-H:-FoldSecurityManagerGetter',

wasm/mx.wasm/truffle.tck.permissions/unsafe_excludes.json

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -262,27 +262,6 @@
262262
}, {
263263
"name" : "atomic_rmw_xchg_i64",
264264
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
265-
}, {
266-
"name" : "atomic_rmw_cmpxchg_i32_8u",
267-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
268-
}, {
269-
"name" : "atomic_rmw_cmpxchg_i32_16u",
270-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
271-
}, {
272-
"name" : "atomic_rmw_cmpxchg_i32",
273-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
274-
}, {
275-
"name" : "atomic_rmw_cmpxchg_i64_8u",
276-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
277-
}, {
278-
"name" : "atomic_rmw_cmpxchg_i64_16u",
279-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
280-
}, {
281-
"name" : "atomic_rmw_cmpxchg_i64_32u",
282-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
283-
}, {
284-
"name" : "atomic_rmw_cmpxchg_i64",
285-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
286265
}, {
287266
"name" : "initialize",
288267
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
@@ -307,9 +286,6 @@
307286
}, {
308287
"name" : "copyToBuffer",
309288
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
310-
}, {
311-
"name" : "getObjectFieldOffset",
312-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
313289
}
314290
]
315291
}, {
@@ -564,27 +540,6 @@
564540
}, {
565541
"name" : "atomic_rmw_xchg_i64",
566542
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
567-
}, {
568-
"name" : "atomic_rmw_cmpxchg_i32_8u",
569-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
570-
}, {
571-
"name" : "atomic_rmw_cmpxchg_i32_16u",
572-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
573-
}, {
574-
"name" : "atomic_rmw_cmpxchg_i32",
575-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
576-
}, {
577-
"name" : "atomic_rmw_cmpxchg_i64_8u",
578-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
579-
}, {
580-
"name" : "atomic_rmw_cmpxchg_i64_16u",
581-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
582-
}, {
583-
"name" : "atomic_rmw_cmpxchg_i64_32u",
584-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
585-
}, {
586-
"name" : "atomic_rmw_cmpxchg_i64",
587-
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."
588543
}, {
589544
"name" : "duplicate",
590545
"justification" : "Allow using sun.misc.Unsafe methods behind the --wasm.UseUnsafeMemory flag."

0 commit comments

Comments
 (0)