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

8317611: Add a tool like jdeprscan to find usage of restricted methods #19774

Closed
wants to merge 29 commits into from
Closed
Changes from 2 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
351e8f4
jnativescan tool
JornVernee May 29, 2024
719cd3e
remove references to --enable-native-access
JornVernee Jun 17, 2024
6729e12
jscan -> jnativescan
JornVernee Jun 17, 2024
83fbbc8
small format fix
JornVernee Jun 17, 2024
415b9b4
Handle array types + handle references through subclasses + implement
JornVernee Jun 17, 2024
9debed5
add test for array method refs
JornVernee Jun 18, 2024
3d0f4ff
add test for references through subclasses
JornVernee Jun 18, 2024
1f23bec
add more testing + cleanup code
JornVernee Jun 18, 2024
7531385
add missing newlinw
JornVernee Jun 18, 2024
01bcd0f
correct comment
JornVernee Jun 18, 2024
b9bd20f
correct one more comment
JornVernee Jun 18, 2024
c666f6a
add --print-native-access test
JornVernee Jun 18, 2024
4b560dc
add jnativescan to help flag test
JornVernee Jun 18, 2024
8a2ebae
use URI-based constructor of Path
JornVernee Jun 18, 2024
9e8dad0
Merge branch 'master' into jnativescan
JornVernee Jun 19, 2024
861965b
Update src/jdk.jdeps/share/classes/com/sun/tools/jnativescan/Main.java
JornVernee Jun 19, 2024
a1ef03a
add man page
JornVernee Jun 19, 2024
b546844
review comments
JornVernee Jun 19, 2024
4c6abdd
man page review comments
JornVernee Jun 20, 2024
06f53e3
update man page header to be consisten with the others
JornVernee Jun 20, 2024
75c9a6a
review comments Alan
JornVernee Jun 20, 2024
a472349
add extra test for missing root modules
JornVernee Jun 20, 2024
fda0568
Jan comments
JornVernee Jun 21, 2024
bb75a30
sort output for easier diffs
JornVernee Jun 21, 2024
a046f6f
Add support for module directories + class path directories
JornVernee Jun 21, 2024
40ca91e
de-dupe on path, not module name
JornVernee Jun 24, 2024
c597f24
use instance resolveAndBind + use junit in tests
JornVernee Jun 28, 2024
5afb356
ofInvokeInstruction
JornVernee Jul 1, 2024
1d1ff01
Merge branch 'master' into jnativescan
JornVernee Jul 5, 2024
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
@@ -35,8 +35,6 @@
import java.lang.classfile.ClassModel;
import java.lang.constant.ClassDesc;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.nio.file.Path;
import java.util.*;
import java.util.function.BiConsumer;
@@ -119,24 +117,28 @@ private static class SystemModuleClassResolver extends ClassResolver {

public SystemModuleClassResolver(JavaFileManager platformFileManager) {
this.platformFileManager = platformFileManager;
this.packageToSystemModule = packageToSystemModule();
this.packageToSystemModule = packageToSystemModule(platformFileManager);
}

private static Map<String, String> packageToSystemModule() {
List<ModuleDescriptor> descriptors = ModuleFinder.ofSystem()
.findAll()
.stream()
.map(ModuleReference::descriptor)
.toList();
Map<String, String> result = new HashMap<>();
for (ModuleDescriptor descriptor : descriptors) {
for (ModuleDescriptor.Exports export : descriptor.exports()) {
if (!export.isQualified()) {
result.put(export.source(), descriptor.name());
private static Map<String, String> packageToSystemModule(JavaFileManager platformFileManager) {
try {
Set<JavaFileManager.Location> locations = platformFileManager.listLocationsForModules(
StandardLocation.SYSTEM_MODULES).iterator().next();

Map<String, String> result = new HashMap<>();
for (JavaFileManager.Location loc : locations) {
JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, "module-info", JavaFileObject.Kind.CLASS);
ModuleDescriptor descriptor = ModuleDescriptor.read(jfo.openInputStream());
for (ModuleDescriptor.Exports export : descriptor.exports()) {
if (!export.isQualified()) {
result.put(export.source(), descriptor.name());
}
}
}
return result;
} catch (IOException e) {
throw new RuntimeException(e);
}
return result;
}

@Override
@@ -153,6 +155,9 @@ public Optional<Info> lookup(ClassDesc desc) {
try {
JavaFileManager.Location loc = platformFileManager.getLocationForModule(StandardLocation.SYSTEM_MODULES, moduleName);
JavaFileObject jfo = platformFileManager.getJavaFileForInput(loc, qualName, JavaFileObject.Kind.CLASS);
if (jfo == null) {
throw new JNativeScanFatalError("System class can not be found: " + qualName);
}
ClassModel model = ClassFile.of().parse(jfo.openInputStream().readAllBytes());
return new Info(moduleName, null, model);
} catch (IOException e) {
Original file line number Diff line number Diff line change
@@ -80,9 +80,9 @@ public void run() throws JNativeScanFatalError {
Map<ScannedModule, Map<ClassDesc, List<RestrictedUse>>> allRestrictedMethods;
try(ClassResolver classesToScan = ClassResolver.forScannedModules(modulesToScan, version);
ClassResolver systemClassResolver = ClassResolver.forSystemModules(version)) {
RestrictedMethodFinder finder = RestrictedMethodFinder.create(classesToScan, systemClassResolver);
NativeMethodFinder finder = NativeMethodFinder.create(classesToScan, systemClassResolver);
allRestrictedMethods = finder.findAll();
} catch (Exception e) {
} catch (IOException e) {
throw new RuntimeException(e);
}

Original file line number Diff line number Diff line change
@@ -75,6 +75,11 @@ public int run(String[] args) {
parseOptionsAndRun(expandedArgs);
} catch (JNativeScanFatalError fatalError) {
printError(fatalError.getMessage());
for (Throwable cause = fatalError.getCause();
cause instanceof JNativeScanFatalError jNativeScanFatalError;
cause = jNativeScanFatalError.getCause()) {
err.println("CAUSED BY: " + jNativeScanFatalError.getMessage());
}
if (DEBUG) {
fatalError.printStackTrace(err);
}
@@ -122,8 +127,8 @@ private void parseOptionsAndRun(String[] expandedArgs) throws JNativeScanFatalEr

if (optionSet.has(helpOpt)) {
out.println("""
The jnativescan tool can be used to find methods that may access native functionalities when
run. This includes methods that call restricted methods, and 'native' method declarations.
The jnativescan tool can be used to find methods that may access native functionality when
run. This includes restricted method calls and 'native' method declarations.
""");
try {
parser.printHelpOn(out);
Original file line number Diff line number Diff line change
@@ -25,8 +25,6 @@
package com.sun.tools.jnativescan;

import java.lang.classfile.MethodModel;
import java.lang.classfile.constantpool.ClassEntry;
import java.lang.classfile.constantpool.InterfaceMethodRefEntry;
import java.lang.classfile.constantpool.MemberRefEntry;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
@@ -37,7 +35,7 @@ public static MethodRef ofModel(MethodModel model) {
model.methodName().stringValue(), model.methodTypeSymbol());
}

public static MethodRef ofMethodRefEntry(MemberRefEntry method) {
public static MethodRef ofMemberRefEntry(MemberRefEntry method) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend to change this to ofInvokeInstruction:

public static MethodRef ofInvokeInstruction(InvokeInstruction instruction) {
    return new MethodRef(instruction.owner().asSymbol(),
            instruction.name().stringValue(), instruction.typeSymbol());
}

return new MethodRef(method.owner().asSymbol(),
method.name().stringValue(), MethodTypeDesc.ofDescriptor(method.type().stringValue()));
}
Original file line number Diff line number Diff line change
@@ -40,24 +40,23 @@
import java.lang.reflect.AccessFlag;
import java.util.*;

class RestrictedMethodFinder {
class NativeMethodFinder {

// ct.sym uses this fake name for the restricted annotation instead
// see make/langtools/src/classes/build/tools/symbolgenerator/CreateSymbols.java
private static final String RESTRICTED_NAME = "Ljdk/internal/javac/Restricted+Annotation;";
private static final List<String> RESTRICTED_MODULES = List.of("java.base");

private final Map<MethodRef, Boolean> CACHE = new HashMap<>();
private final ClassResolver classesToScan;
private final ClassResolver systemClassResolver;

private RestrictedMethodFinder(ClassResolver classesToScan, ClassResolver systemClassResolver) {
private NativeMethodFinder(ClassResolver classesToScan, ClassResolver systemClassResolver) {
this.classesToScan = classesToScan;
this.systemClassResolver = systemClassResolver;
}

public static RestrictedMethodFinder create(ClassResolver classesToScan, ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException {
return new RestrictedMethodFinder(classesToScan, systemClassResolver);
public static NativeMethodFinder create(ClassResolver classesToScan, ClassResolver systemClassResolver) throws JNativeScanFatalError, IOException {
return new NativeMethodFinder(classesToScan, systemClassResolver);
}

public Map<ScannedModule, Map<ClassDesc, List<RestrictedUse>>> findAll() throws JNativeScanFatalError {
@@ -70,20 +69,25 @@ public Map<ScannedModule, Map<ClassDesc, List<RestrictedUse>>> findAll() throws
perClass.add(new NativeMethodDecl(MethodRef.ofModel(methodModel)));
} else {
Set<MethodRef> perMethod = new HashSet<>();
methodModel.code()
.ifPresent(code -> {
code.forEach(e -> {
switch (e) {
case InvokeInstruction invoke -> {
if (isRestrictedMethod(invoke.method())) {
perMethod.add(MethodRef.ofMethodRefEntry(invoke.method()));
}
}
default -> {
}
}
});
});
methodModel.code().ifPresent(code -> {
try {
code.forEach(e -> {
switch (e) {
case InvokeInstruction invoke -> {
MethodRef ref = MethodRef.ofMemberRefEntry(invoke.method());
if (isRestrictedMethod(ref)) {
perMethod.add(ref);
}
}
default -> {
}
}
});
} catch (JNativeScanFatalError e) {
throw new JNativeScanFatalError("Error while processing method: " +
MethodRef.ofModel(methodModel), e);
}
Comment on lines +84 to +87
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm re-wrapping the exception here, thrown from SystemClassResolver::lookup, in order to include the method that called the missing system class in the error message as well. (See also the code added in Main to print out an exception cause if it is a JNativeScanFatalError).

});
if (!perMethod.isEmpty()) {
perClass.add(new RestrictedMethodRefs(MethodRef.ofModel(methodModel),
Set.copyOf(perMethod)));
@@ -99,18 +103,8 @@ public Map<ScannedModule, Map<ClassDesc, List<RestrictedUse>>> findAll() throws
return restrictedMethods;
}

private boolean isRestrictedMethod(MemberRefEntry method) throws JNativeScanFatalError {
return switch (method) {
case MethodRefEntry mre ->
isRestrictedMethod(mre.owner().asSymbol(), mre.name().stringValue(), mre.typeSymbol());
case InterfaceMethodRefEntry mre ->
isRestrictedMethod(mre.owner().asSymbol(), mre.name().stringValue(), mre.typeSymbol());
default -> throw new IllegalStateException("Unexpected type: " + method);
};
}

private boolean isRestrictedMethod(ClassDesc owner, String name, MethodTypeDesc type) throws JNativeScanFatalError {
return CACHE.computeIfAbsent(new MethodRef(owner, name, type), methodRef -> {
private boolean isRestrictedMethod(MethodRef ref) throws JNativeScanFatalError {
return CACHE.computeIfAbsent(ref, methodRef -> {
if (methodRef.owner().isArray()) {
// no restricted methods in arrays atm, and we can't look them up since they have no class file
return false;
15 changes: 9 additions & 6 deletions src/jdk.jdeps/share/classes/module-info.java
Original file line number Diff line number Diff line change
@@ -28,12 +28,13 @@
/**
* Defines tools for analysing dependencies in Java libraries and programs,
* including the <em>{@index jdeps jdeps tool}</em>,
* <em>{@index javap javap tool}</em>, and
* <em>{@index jdeprscan jdeprscan tool}</em> tools.
* <em>{@index javap javap tool}</em>,
* <em>{@index jdeprscan jdeprscan tool}</em>, and
* <em>{@index jnativescan jnativescan tool}</em> tools.
*
* <p>
* This module provides the equivalent of command-line access to the
* <em>javap</em> and <em>jdeps</em> tools via the
* <em>javap</em>, <em>jdeps</em>, and <em>jnativescan</em> tools via the
* {@link java.util.spi.ToolProvider ToolProvider} service provider
* interface (SPI)</p>
*
@@ -49,12 +50,14 @@
* @toolGuide javap
* @toolGuide jdeprscan
* @toolGuide jdeps
* @toolGuide jnativescan
*
* @provides java.util.spi.ToolProvider
* Use {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("javap")}
* or {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jdeps")}
* Use {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("javap")},
* {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jdeps")},
* or {@link java.util.spi.ToolProvider#findFirst ToolProvider.findFirst("jnativescan")}
* to obtain an instance of a {@code ToolProvider} that provides the equivalent
* of command-line access to the {@code javap} or {@code jdeps} tool.
* of command-line access to the {@code javap}, {@code jdeps}, {@code jnativescan} tool.
*
* @moduleGraph
* @since 9
Loading