Skip to content

Commit 0c67a38

Browse files
ldematteprdoyle
authored andcommitted
[Entitlements] Add missing NIO async network instrumentation (elastic#128582)
This PR adds some additional instrumentation to ensure we capture more cases in which we use async network usage via channels and `select`
1 parent b0beb54 commit 0c67a38

File tree

8 files changed

+274
-15
lines changed

8 files changed

+274
-15
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
import java.nio.channels.AsynchronousSocketChannel;
4747
import java.nio.channels.CompletionHandler;
4848
import java.nio.channels.DatagramChannel;
49+
import java.nio.channels.SelectableChannel;
50+
import java.nio.channels.Selector;
4951
import java.nio.channels.ServerSocketChannel;
5052
import java.nio.channels.SocketChannel;
5153
import java.nio.channels.spi.SelectorProvider;
@@ -577,6 +579,16 @@ public interface EntitlementChecker {
577579
* (not instrumentable).
578580
*/
579581

582+
void check$java_nio_channels_spi_AbstractSelectableChannel$register(
583+
Class<?> callerClass,
584+
SelectableChannel that,
585+
Selector sel,
586+
int ops,
587+
Object att
588+
);
589+
590+
void check$java_nio_channels_SelectableChannel$register(Class<?> callerClass, SelectableChannel that, Selector sel, int ops);
591+
580592
// bind
581593

582594
void check$java_nio_channels_AsynchronousServerSocketChannel$bind(
@@ -600,6 +612,12 @@ public interface EntitlementChecker {
600612

601613
void check$sun_nio_ch_ServerSocketChannelImpl$bind(Class<?> callerClass, ServerSocketChannel that, SocketAddress local, int backlog);
602614

615+
void check$java_nio_channels_SocketChannel$$open(Class<?> callerClass);
616+
617+
void check$java_nio_channels_SocketChannel$$open(Class<?> callerClass, java.net.ProtocolFamily family);
618+
619+
void check$java_nio_channels_SocketChannel$$open(Class<?> callerClass, SocketAddress remote);
620+
603621
void check$sun_nio_ch_SocketChannelImpl$bind(Class<?> callerClass, SocketChannel that, SocketAddress local);
604622

605623
// connect
@@ -647,6 +665,18 @@ public interface EntitlementChecker {
647665
// provider methods (dynamic)
648666
void checkSelectorProviderInheritedChannel(Class<?> callerClass, SelectorProvider that);
649667

668+
void checkSelectorProviderOpenDatagramChannel(Class<?> callerClass, SelectorProvider that);
669+
670+
void checkSelectorProviderOpenDatagramChannel(Class<?> callerClass, SelectorProvider that, java.net.ProtocolFamily family);
671+
672+
void checkSelectorProviderOpenServerSocketChannel(Class<?> callerClass, SelectorProvider that);
673+
674+
void checkSelectorProviderOpenServerSocketChannel(Class<?> callerClass, SelectorProvider that, java.net.ProtocolFamily family);
675+
676+
void checkSelectorProviderOpenSocketChannel(Class<?> callerClass, SelectorProvider that);
677+
678+
void checkSelectorProviderOpenSocketChannel(Class<?> callerClass, SelectorProvider that, java.net.ProtocolFamily family);
679+
650680
/// /////////////////
651681
//
652682
// Load native libraries

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/DummyImplementations.java

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import org.elasticsearch.core.SuppressForbidden;
1515

16+
import java.io.IOException;
1617
import java.io.InputStream;
1718
import java.io.OutputStream;
1819
import java.net.DatagramPacket;
@@ -41,9 +42,12 @@
4142
import java.nio.channels.ReadableByteChannel;
4243
import java.nio.channels.SeekableByteChannel;
4344
import java.nio.channels.SelectableChannel;
45+
import java.nio.channels.SelectionKey;
46+
import java.nio.channels.Selector;
4447
import java.nio.channels.ServerSocketChannel;
4548
import java.nio.channels.SocketChannel;
4649
import java.nio.channels.WritableByteChannel;
50+
import java.nio.channels.spi.AbstractSelectableChannel;
4751
import java.nio.channels.spi.AbstractSelector;
4852
import java.nio.channels.spi.AsynchronousChannelProvider;
4953
import java.nio.channels.spi.SelectorProvider;
@@ -846,4 +850,71 @@ public void implCloseChannel(SelectableChannel sc) {}
846850
@Override
847851
public void implReleaseChannel(SelectableChannel sc) {}
848852
}
853+
854+
static class DummySelectableChannel extends AbstractSelectableChannel {
855+
protected DummySelectableChannel(SelectorProvider provider) {
856+
super(provider);
857+
}
858+
859+
@Override
860+
protected void implCloseSelectableChannel() throws IOException {
861+
862+
}
863+
864+
@Override
865+
protected void implConfigureBlocking(boolean block) throws IOException {
866+
867+
}
868+
869+
@Override
870+
public int validOps() {
871+
return SelectionKey.OP_ACCEPT | SelectionKey.OP_CONNECT;
872+
}
873+
}
874+
875+
static class DummySelector extends AbstractSelector {
876+
protected DummySelector(SelectorProvider provider) {
877+
super(provider);
878+
}
879+
880+
@Override
881+
protected void implCloseSelector() throws IOException {
882+
883+
}
884+
885+
@Override
886+
protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att) {
887+
return null;
888+
}
889+
890+
@Override
891+
public Set<SelectionKey> keys() {
892+
return Set.of();
893+
}
894+
895+
@Override
896+
public Set<SelectionKey> selectedKeys() {
897+
return Set.of();
898+
}
899+
900+
@Override
901+
public int selectNow() throws IOException {
902+
return 0;
903+
}
904+
905+
@Override
906+
public int select(long timeout) throws IOException {
907+
return 0;
908+
}
909+
910+
@Override
911+
public int select() throws IOException {
912+
return 0;
913+
}
914+
915+
@Override
916+
public Selector wakeup() {
917+
return null;
918+
}
919+
}
849920
}

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NetworkAccessCheckActions.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.net.ServerSocket;
2626
import java.net.Socket;
2727
import java.net.SocketException;
28+
import java.net.StandardProtocolFamily;
2829
import java.net.URL;
2930
import java.net.URLConnection;
3031
import java.net.URLStreamHandler;
@@ -203,6 +204,20 @@ static void socketChannelConnect() throws IOException {
203204
}
204205
}
205206

207+
@EntitlementTest(expectedAccess = PLUGINS)
208+
static void socketChannelOpenProtocol() throws IOException {
209+
SocketChannel.open(StandardProtocolFamily.INET).close();
210+
}
211+
212+
@EntitlementTest(expectedAccess = PLUGINS)
213+
static void socketChannelOpenAddress() throws IOException {
214+
try {
215+
SocketChannel.open(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0)).close();
216+
} catch (SocketException ex) {
217+
// Some sort of SocketException is expected, we are trying to connect to port 0
218+
}
219+
}
220+
206221
@EntitlementTest(expectedAccess = PLUGINS)
207222
static void asynchronousSocketChannelBind() throws IOException {
208223
try (var socketChannel = AsynchronousSocketChannel.open()) {

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/NioChannelsActions.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515

1616
import java.io.FileDescriptor;
1717
import java.io.IOException;
18+
import java.net.StandardProtocolFamily;
1819
import java.nio.channels.AsynchronousFileChannel;
1920
import java.nio.channels.FileChannel;
21+
import java.nio.channels.SelectionKey;
22+
import java.nio.channels.spi.SelectorProvider;
2023
import java.nio.file.StandardOpenOption;
2124
import java.util.Set;
2225

@@ -85,4 +88,50 @@ static void asynchronousFileChannelOpenForReadWithOptions() throws IOException {
8588
static void channelsReadWriteSelectableChannel() throws IOException {
8689
jdk.nio.Channels.readWriteSelectableChannel(new FileDescriptor(), new DummyImplementations.DummySelectableChannelCloser()).close();
8790
}
91+
92+
@EntitlementTest(expectedAccess = PLUGINS)
93+
static void selectableChannelRegisterConnect() throws IOException {
94+
try (var selectableChannel = new DummyImplementations.DummySelectableChannel(SelectorProvider.provider())) {
95+
selectableChannel.configureBlocking(false);
96+
selectableChannel.register(new DummyImplementations.DummySelector(SelectorProvider.provider()), SelectionKey.OP_CONNECT);
97+
}
98+
}
99+
100+
@EntitlementTest(expectedAccess = PLUGINS)
101+
static void selectableChannelRegisterAccept() throws IOException {
102+
try (var selectableChannel = new DummyImplementations.DummySelectableChannel(SelectorProvider.provider())) {
103+
selectableChannel.configureBlocking(false);
104+
selectableChannel.register(new DummyImplementations.DummySelector(SelectorProvider.provider()), SelectionKey.OP_ACCEPT);
105+
}
106+
}
107+
108+
@EntitlementTest(expectedAccess = PLUGINS)
109+
static void selectorProviderOpenSocketChannel() throws IOException {
110+
SelectorProvider.provider().openSocketChannel().close();
111+
}
112+
113+
@EntitlementTest(expectedAccess = PLUGINS)
114+
static void selectorProviderOpenDatagramChannel() throws IOException {
115+
SelectorProvider.provider().openDatagramChannel().close();
116+
}
117+
118+
@EntitlementTest(expectedAccess = PLUGINS)
119+
static void selectorProviderOpenServerSocketChannel() throws IOException {
120+
SelectorProvider.provider().openServerSocketChannel().close();
121+
}
122+
123+
@EntitlementTest(expectedAccess = PLUGINS)
124+
static void selectorProviderOpenSocketChannelWithProtocol() throws IOException {
125+
SelectorProvider.provider().openSocketChannel(StandardProtocolFamily.INET).close();
126+
}
127+
128+
@EntitlementTest(expectedAccess = PLUGINS)
129+
static void selectorProviderOpenDatagramChannelWithProtocol() throws IOException {
130+
SelectorProvider.provider().openDatagramChannel(StandardProtocolFamily.INET).close();
131+
}
132+
133+
@EntitlementTest(expectedAccess = PLUGINS)
134+
static void selectorProviderOpenServerSocketChannelWithProtocol() throws IOException {
135+
SelectorProvider.provider().openServerSocketChannel(StandardProtocolFamily.INET).close();
136+
}
88137
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/DynamicInstrumentation.java

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,7 @@ static void initialize(Instrumentation inst, Class<?> checkerInterface, boolean
122122
private static Map<MethodKey, CheckMethod> getMethodsToInstrument(Class<?> checkerInterface) throws ClassNotFoundException,
123123
NoSuchMethodException {
124124
Map<MethodKey, CheckMethod> checkMethods = new HashMap<>(INSTRUMENTATION_SERVICE.lookupMethods(checkerInterface));
125-
Stream.of(
126-
fileSystemProviderChecks(),
127-
fileStoreChecks(),
128-
pathChecks(),
129-
Stream.of(
130-
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
131-
SelectorProvider.class,
132-
"inheritedChannel",
133-
SelectorProvider.provider().getClass(),
134-
EntitlementChecker.class,
135-
"checkSelectorProviderInheritedChannel"
136-
)
137-
)
138-
)
125+
Stream.of(fileSystemProviderChecks(), fileStoreChecks(), pathChecks(), selectorProviderChecks())
139126
.flatMap(Function.identity())
140127
.forEach(instrumentation -> checkMethods.put(instrumentation.targetMethod(), instrumentation.checkMethod()));
141128

@@ -282,6 +269,39 @@ private static Stream<InstrumentationService.InstrumentationInfo> pathChecks() {
282269
});
283270
}
284271

272+
private static Stream<InstrumentationService.InstrumentationInfo> selectorProviderChecks() {
273+
var selectorProviderClass = SelectorProvider.provider().getClass();
274+
275+
var instrumentation = new InstrumentationInfoFactory() {
276+
@Override
277+
public InstrumentationService.InstrumentationInfo of(String methodName, Class<?>... parameterTypes)
278+
throws ClassNotFoundException, NoSuchMethodException {
279+
return INSTRUMENTATION_SERVICE.lookupImplementationMethod(
280+
SelectorProvider.class,
281+
methodName,
282+
selectorProviderClass,
283+
EntitlementChecker.class,
284+
"checkSelectorProvider" + Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1),
285+
parameterTypes
286+
);
287+
}
288+
};
289+
290+
try {
291+
return Stream.of(
292+
instrumentation.of("inheritedChannel"),
293+
instrumentation.of("openDatagramChannel"),
294+
instrumentation.of("openDatagramChannel", java.net.ProtocolFamily.class),
295+
instrumentation.of("openServerSocketChannel"),
296+
instrumentation.of("openServerSocketChannel", java.net.ProtocolFamily.class),
297+
instrumentation.of("openSocketChannel"),
298+
instrumentation.of("openSocketChannel", java.net.ProtocolFamily.class)
299+
);
300+
} catch (NoSuchMethodException | ClassNotFoundException e) {
301+
throw new RuntimeException(e);
302+
}
303+
}
304+
285305
private static Class<?>[] findClassesToRetransform(Class<?>[] loadedClasses, Set<String> classesToTransform) {
286306
List<Class<?>> retransform = new ArrayList<>();
287307
for (Class<?> loadedClass : loadedClasses) {

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,9 @@ private static void ensureClassesSensitiveToVerificationAreInitialized() {
105105
var classesToInitialize = Set.of(
106106
"sun.net.www.protocol.http.HttpURLConnection",
107107
"sun.nio.ch.SocketChannelImpl",
108-
"java.net.ProxySelector"
108+
"java.net.ProxySelector",
109+
"sun.nio.ch.DatagramChannelImpl",
110+
"sun.nio.ch.ServerSocketChannelImpl"
109111
);
110112
for (String className : classesToInitialize) {
111113
try {

0 commit comments

Comments
 (0)