Skip to content

Commit 32285b1

Browse files
authored
[8.x][Entitlements] Instrumentation for FileSystemProvider (#122232) (#122471)
* [Entitlements] Instrumentation for FileSystemProvider (#122232) * Move some check function and tests to version specific checker classes * Refactor/fix: lookupImplementationMethod looks up the class hierarchy too * Spotless
1 parent 523371e commit 32285b1

File tree

19 files changed

+885
-107
lines changed

19 files changed

+885
-107
lines changed

libs/entitlement/asm-provider/src/main/java/org/elasticsearch/entitlement/instrumentation/impl/InstrumentationServiceImpl.java

Lines changed: 63 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ public Instrumenter newInstrumenter(Class<?> clazz, Map<MethodKey, CheckMethod>
4444
return InstrumenterImpl.create(clazz, methods);
4545
}
4646

47-
@Override
48-
public Map<MethodKey, CheckMethod> lookupMethods(Class<?> checkerClass) throws IOException {
49-
Map<MethodKey, CheckMethod> methodsToInstrument = new HashMap<>();
47+
private interface CheckerMethodVisitor {
48+
void visit(Class<?> currentClass, int access, String checkerMethodName, String checkerMethodDescriptor);
49+
}
5050

51+
private void visitClassAndSupers(Class<?> checkerClass, CheckerMethodVisitor checkerMethodVisitor) throws ClassNotFoundException {
5152
Set<Class<?>> visitedClasses = new HashSet<>();
5253
ArrayDeque<Class<?>> classesToVisit = new ArrayDeque<>(Collections.singleton(checkerClass));
5354
while (classesToVisit.isEmpty() == false) {
@@ -57,67 +58,76 @@ public Map<MethodKey, CheckMethod> lookupMethods(Class<?> checkerClass) throws I
5758
}
5859
visitedClasses.add(currentClass);
5960

60-
var classFileInfo = InstrumenterImpl.getClassFileInfo(currentClass);
61-
ClassReader reader = new ClassReader(classFileInfo.bytecodes());
62-
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9) {
61+
try {
62+
var classFileInfo = InstrumenterImpl.getClassFileInfo(currentClass);
63+
ClassReader reader = new ClassReader(classFileInfo.bytecodes());
64+
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9) {
6365

64-
@Override
65-
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
66-
super.visit(version, access, name, signature, superName, interfaces);
67-
try {
68-
if (OBJECT_INTERNAL_NAME.equals(superName) == false) {
69-
classesToVisit.add(Class.forName(Type.getObjectType(superName).getClassName()));
66+
@Override
67+
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
68+
super.visit(version, access, name, signature, superName, interfaces);
69+
try {
70+
if (OBJECT_INTERNAL_NAME.equals(superName) == false) {
71+
classesToVisit.add(Class.forName(Type.getObjectType(superName).getClassName()));
72+
}
73+
for (var interfaceName : interfaces) {
74+
classesToVisit.add(Class.forName(Type.getObjectType(interfaceName).getClassName()));
75+
}
76+
} catch (ClassNotFoundException e) {
77+
throw new IllegalArgumentException("Cannot inspect checker class " + currentClass.getName(), e);
7078
}
71-
for (var interfaceName : interfaces) {
72-
classesToVisit.add(Class.forName(Type.getObjectType(interfaceName).getClassName()));
73-
}
74-
} catch (ClassNotFoundException e) {
75-
throw new IllegalArgumentException("Cannot inspect checker class " + checkerClass.getName(), e);
7679
}
77-
}
7880

79-
@Override
80-
public MethodVisitor visitMethod(
81-
int access,
82-
String checkerMethodName,
83-
String checkerMethodDescriptor,
84-
String signature,
85-
String[] exceptions
86-
) {
87-
var mv = super.visitMethod(access, checkerMethodName, checkerMethodDescriptor, signature, exceptions);
88-
if (checkerMethodName.startsWith(InstrumentationService.CHECK_METHOD_PREFIX)) {
89-
var checkerMethodArgumentTypes = Type.getArgumentTypes(checkerMethodDescriptor);
90-
var methodToInstrument = parseCheckerMethodSignature(checkerMethodName, checkerMethodArgumentTypes);
91-
92-
var checkerParameterDescriptors = Arrays.stream(checkerMethodArgumentTypes).map(Type::getDescriptor).toList();
93-
var checkMethod = new CheckMethod(
94-
Type.getInternalName(currentClass),
95-
checkerMethodName,
96-
checkerParameterDescriptors
97-
);
98-
99-
methodsToInstrument.putIfAbsent(methodToInstrument, checkMethod);
81+
@Override
82+
public MethodVisitor visitMethod(
83+
int access,
84+
String checkerMethodName,
85+
String checkerMethodDescriptor,
86+
String signature,
87+
String[] exceptions
88+
) {
89+
var mv = super.visitMethod(access, checkerMethodName, checkerMethodDescriptor, signature, exceptions);
90+
checkerMethodVisitor.visit(currentClass, access, checkerMethodName, checkerMethodDescriptor);
91+
return mv;
10092
}
101-
return mv;
102-
}
103-
};
104-
reader.accept(visitor, 0);
93+
};
94+
reader.accept(visitor, 0);
95+
} catch (IOException e) {
96+
throw new ClassNotFoundException("Cannot find a definition for class [" + checkerClass.getName() + "]", e);
97+
}
10598
}
99+
}
100+
101+
@Override
102+
public Map<MethodKey, CheckMethod> lookupMethods(Class<?> checkerClass) throws ClassNotFoundException {
103+
Map<MethodKey, CheckMethod> methodsToInstrument = new HashMap<>();
104+
105+
visitClassAndSupers(checkerClass, (currentClass, access, checkerMethodName, checkerMethodDescriptor) -> {
106+
if (checkerMethodName.startsWith(InstrumentationService.CHECK_METHOD_PREFIX)) {
107+
var checkerMethodArgumentTypes = Type.getArgumentTypes(checkerMethodDescriptor);
108+
var methodToInstrument = parseCheckerMethodSignature(checkerMethodName, checkerMethodArgumentTypes);
109+
110+
var checkerParameterDescriptors = Arrays.stream(checkerMethodArgumentTypes).map(Type::getDescriptor).toList();
111+
var checkMethod = new CheckMethod(Type.getInternalName(currentClass), checkerMethodName, checkerParameterDescriptors);
112+
methodsToInstrument.putIfAbsent(methodToInstrument, checkMethod);
113+
}
114+
});
115+
106116
return methodsToInstrument;
107117
}
108118

109119
@SuppressForbidden(reason = "Need access to abstract methods (protected/package internal) in base class")
110120
@Override
111121
public InstrumentationInfo lookupImplementationMethod(
112122
Class<?> targetSuperclass,
113-
String methodName,
123+
String targetMethodName,
114124
Class<?> implementationClass,
115125
Class<?> checkerClass,
116126
String checkMethodName,
117127
Class<?>... parameterTypes
118128
) throws NoSuchMethodException, ClassNotFoundException {
119129

120-
var targetMethod = targetSuperclass.getDeclaredMethod(methodName, parameterTypes);
130+
var targetMethod = targetSuperclass.getDeclaredMethod(targetMethodName, parameterTypes);
121131
var implementationMethod = implementationClass.getMethod(targetMethod.getName(), targetMethod.getParameterTypes());
122132
validateTargetMethod(implementationClass, targetMethod, implementationMethod);
123133

@@ -128,33 +138,15 @@ public InstrumentationInfo lookupImplementationMethod(
128138

129139
CheckMethod[] checkMethod = new CheckMethod[1];
130140

131-
try {
132-
InstrumenterImpl.ClassFileInfo classFileInfo = InstrumenterImpl.getClassFileInfo(checkerClass);
133-
ClassReader reader = new ClassReader(classFileInfo.bytecodes());
134-
ClassVisitor visitor = new ClassVisitor(Opcodes.ASM9) {
135-
@Override
136-
public MethodVisitor visitMethod(
137-
int access,
138-
String methodName,
139-
String methodDescriptor,
140-
String signature,
141-
String[] exceptions
142-
) {
143-
var mv = super.visitMethod(access, methodName, methodDescriptor, signature, exceptions);
144-
if (methodName.equals(checkMethodName)) {
145-
var methodArgumentTypes = Type.getArgumentTypes(methodDescriptor);
146-
if (Arrays.equals(methodArgumentTypes, checkMethodArgumentTypes)) {
147-
var checkerParameterDescriptors = Arrays.stream(methodArgumentTypes).map(Type::getDescriptor).toList();
148-
checkMethod[0] = new CheckMethod(Type.getInternalName(checkerClass), methodName, checkerParameterDescriptors);
149-
}
150-
}
151-
return mv;
141+
visitClassAndSupers(checkerClass, (currentClass, access, methodName, methodDescriptor) -> {
142+
if (methodName.equals(checkMethodName)) {
143+
var methodArgumentTypes = Type.getArgumentTypes(methodDescriptor);
144+
if (Arrays.equals(methodArgumentTypes, checkMethodArgumentTypes)) {
145+
var checkerParameterDescriptors = Arrays.stream(methodArgumentTypes).map(Type::getDescriptor).toList();
146+
checkMethod[0] = new CheckMethod(Type.getInternalName(currentClass), methodName, checkerParameterDescriptors);
152147
}
153-
};
154-
reader.accept(visitor, 0);
155-
} catch (IOException e) {
156-
throw new ClassNotFoundException("Cannot find a definition for class [" + checkerClass.getName() + "]", e);
157-
}
148+
}
149+
});
158150

159151
if (checkMethod[0] == null) {
160152
throw new NoSuchMethodException(

libs/entitlement/asm-provider/src/test/java/org/elasticsearch/entitlement/instrumentation/impl/InstrumentationServiceImplTests.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import org.elasticsearch.test.ESTestCase;
1616
import org.objectweb.asm.Type;
1717

18-
import java.io.IOException;
1918
import java.util.List;
2019
import java.util.Map;
2120

@@ -90,7 +89,7 @@ interface TestCheckerMixed {
9089
void checkInstanceMethodManual(Class<?> clazz, TestTargetBaseClass that, int x, String y);
9190
}
9291

93-
public void testInstrumentationTargetLookup() throws IOException {
92+
public void testInstrumentationTargetLookup() throws ClassNotFoundException {
9493
Map<MethodKey, CheckMethod> checkMethods = instrumentationService.lookupMethods(TestChecker.class);
9594

9695
assertThat(checkMethods, aMapWithSize(3));
@@ -143,7 +142,7 @@ public void testInstrumentationTargetLookup() throws IOException {
143142
);
144143
}
145144

146-
public void testInstrumentationTargetLookupWithOverloads() throws IOException {
145+
public void testInstrumentationTargetLookupWithOverloads() throws ClassNotFoundException {
147146
Map<MethodKey, CheckMethod> checkMethods = instrumentationService.lookupMethods(TestCheckerOverloads.class);
148147

149148
assertThat(checkMethods, aMapWithSize(2));
@@ -175,7 +174,7 @@ public void testInstrumentationTargetLookupWithOverloads() throws IOException {
175174
);
176175
}
177176

178-
public void testInstrumentationTargetLookupWithDerivedClass() throws IOException {
177+
public void testInstrumentationTargetLookupWithDerivedClass() throws ClassNotFoundException {
179178
Map<MethodKey, CheckMethod> checkMethods = instrumentationService.lookupMethods(TestCheckerDerived2.class);
180179

181180
assertThat(checkMethods, aMapWithSize(4));
@@ -244,7 +243,7 @@ public void testInstrumentationTargetLookupWithDerivedClass() throws IOException
244243
);
245244
}
246245

247-
public void testInstrumentationTargetLookupWithCtors() throws IOException {
246+
public void testInstrumentationTargetLookupWithCtors() throws ClassNotFoundException {
248247
Map<MethodKey, CheckMethod> checkMethods = instrumentationService.lookupMethods(TestCheckerCtors.class);
249248

250249
assertThat(checkMethods, aMapWithSize(2));
@@ -276,7 +275,7 @@ public void testInstrumentationTargetLookupWithCtors() throws IOException {
276275
);
277276
}
278277

279-
public void testInstrumentationTargetLookupWithExtraMethods() throws IOException {
278+
public void testInstrumentationTargetLookupWithExtraMethods() throws ClassNotFoundException {
280279
Map<MethodKey, CheckMethod> checkMethods = instrumentationService.lookupMethods(TestCheckerMixed.class);
281280

282281
assertThat(checkMethods, aMapWithSize(1));

libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.net.Socket;
2929
import java.net.SocketAddress;
3030
import java.net.SocketImplFactory;
31+
import java.net.URI;
3132
import java.net.URL;
3233
import java.net.URLStreamHandler;
3334
import java.net.URLStreamHandlerFactory;
@@ -43,17 +44,24 @@
4344
import java.nio.channels.SocketChannel;
4445
import java.nio.channels.spi.SelectorProvider;
4546
import java.nio.charset.Charset;
47+
import java.nio.file.AccessMode;
48+
import java.nio.file.CopyOption;
49+
import java.nio.file.DirectoryStream;
4650
import java.nio.file.FileStore;
4751
import java.nio.file.LinkOption;
4852
import java.nio.file.OpenOption;
4953
import java.nio.file.Path;
54+
import java.nio.file.attribute.FileAttribute;
5055
import java.nio.file.attribute.UserPrincipal;
5156
import java.nio.file.spi.FileSystemProvider;
5257
import java.security.cert.CertStoreParameters;
5358
import java.util.List;
5459
import java.util.Locale;
60+
import java.util.Map;
5561
import java.util.Properties;
62+
import java.util.Set;
5663
import java.util.TimeZone;
64+
import java.util.concurrent.ExecutorService;
5765

5866
import javax.net.ssl.HostnameVerifier;
5967
import javax.net.ssl.HttpsURLConnection;
@@ -491,8 +499,75 @@ public interface EntitlementChecker {
491499
void check$java_nio_file_Files$$setOwner(Class<?> callerClass, Path path, UserPrincipal principal);
492500

493501
// file system providers
502+
void check$java_nio_file_spi_FileSystemProvider$(Class<?> callerClass);
503+
504+
void checkNewFileSystem(Class<?> callerClass, FileSystemProvider that, URI uri, Map<String, ?> env);
505+
506+
void checkNewFileSystem(Class<?> callerClass, FileSystemProvider that, Path path, Map<String, ?> env);
507+
494508
void checkNewInputStream(Class<?> callerClass, FileSystemProvider that, Path path, OpenOption... options);
495509

510+
void checkNewOutputStream(Class<?> callerClass, FileSystemProvider that, Path path, OpenOption... options);
511+
512+
void checkNewFileChannel(
513+
Class<?> callerClass,
514+
FileSystemProvider that,
515+
Path path,
516+
Set<? extends OpenOption> options,
517+
FileAttribute<?>... attrs
518+
);
519+
520+
void checkNewAsynchronousFileChannel(
521+
Class<?> callerClass,
522+
FileSystemProvider that,
523+
Path path,
524+
Set<? extends OpenOption> options,
525+
ExecutorService executor,
526+
FileAttribute<?>... attrs
527+
);
528+
529+
void checkNewByteChannel(
530+
Class<?> callerClass,
531+
FileSystemProvider that,
532+
Path path,
533+
Set<? extends OpenOption> options,
534+
FileAttribute<?>... attrs
535+
);
536+
537+
void checkNewDirectoryStream(Class<?> callerClass, FileSystemProvider that, Path dir, DirectoryStream.Filter<? super Path> filter);
538+
539+
void checkCreateDirectory(Class<?> callerClass, FileSystemProvider that, Path dir, FileAttribute<?>... attrs);
540+
541+
void checkCreateSymbolicLink(Class<?> callerClass, FileSystemProvider that, Path link, Path target, FileAttribute<?>... attrs);
542+
543+
void checkCreateLink(Class<?> callerClass, FileSystemProvider that, Path link, Path existing);
544+
545+
void checkDelete(Class<?> callerClass, FileSystemProvider that, Path path);
546+
547+
void checkDeleteIfExists(Class<?> callerClass, FileSystemProvider that, Path path);
548+
549+
void checkReadSymbolicLink(Class<?> callerClass, FileSystemProvider that, Path link);
550+
551+
void checkCopy(Class<?> callerClass, FileSystemProvider that, Path source, Path target, CopyOption... options);
552+
553+
void checkMove(Class<?> callerClass, FileSystemProvider that, Path source, Path target, CopyOption... options);
554+
555+
void checkIsSameFile(Class<?> callerClass, FileSystemProvider that, Path path, Path path2);
556+
557+
void checkIsHidden(Class<?> callerClass, FileSystemProvider that, Path path);
558+
559+
void checkGetFileStore(Class<?> callerClass, FileSystemProvider that, Path path);
560+
561+
void checkCheckAccess(Class<?> callerClass, FileSystemProvider that, Path path, AccessMode... modes);
562+
563+
void checkGetFileAttributeView(Class<?> callerClass, FileSystemProvider that, Path path, Class<?> type, LinkOption... options);
564+
565+
void checkReadAttributes(Class<?> callerClass, FileSystemProvider that, Path path, Class<?> type, LinkOption... options);
566+
567+
void checkReadAttributes(Class<?> callerClass, FileSystemProvider that, Path path, String attributes, LinkOption... options);
568+
569+
void checkSetAttribute(Class<?> callerClass, FileSystemProvider that, Path path, String attribute, Object value, LinkOption... options);
570+
496571
// file store
497572
void checkGetFileStoreAttributeView(Class<?> callerClass, FileStore that, Class<?> type);
498573

libs/entitlement/bridge/src/main20/java/org/elasticsearch/entitlement/bridge/Java20StableEntitlementChecker.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111

1212
import java.lang.foreign.FunctionDescriptor;
1313
import java.lang.foreign.Linker;
14+
import java.nio.file.LinkOption;
15+
import java.nio.file.Path;
16+
import java.nio.file.spi.FileSystemProvider;
1417

1518
/**
1619
* Interface with Java20 "stable" functions and types.
@@ -32,4 +35,8 @@ public interface Java20StableEntitlementChecker extends EntitlementChecker {
3235
FunctionDescriptor function,
3336
Linker.Option... options
3437
);
38+
39+
void checkReadAttributesIfExists(Class<?> callerClass, FileSystemProvider that, Path path, Class<?> type, LinkOption... options);
40+
41+
void checkExists(Class<?> callerClass, FileSystemProvider that, Path path, LinkOption... options);
3542
}

0 commit comments

Comments
 (0)