Skip to content

Commit a4c7d29

Browse files
authored
[Entitlements] Instrumentation of NIO file channels (#122816)
1 parent 52b3713 commit a4c7d29

File tree

10 files changed

+372
-1
lines changed

10 files changed

+372
-1
lines changed

distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ private static Stream<String> maybeAttachEntitlementAgent(boolean useEntitlement
180180
}
181181
// We instrument classes in these modules to call the bridge. Because the bridge gets patched
182182
// into java.base, we must export the bridge from java.base to these modules, as a comma-separated list
183-
String modulesContainingEntitlementInstrumentation = "java.logging,java.net.http,java.naming";
183+
String modulesContainingEntitlementInstrumentation = "java.logging,java.net.http,java.naming,jdk.net";
184184
return Stream.of(
185185
"-Des.entitlements.enabled=true",
186186
"-XX:+EnableDynamicAgentLoading",

libs/entitlement/bridge/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// At build and run time, the bridge is patched into the java.base module.
1212
module org.elasticsearch.entitlement.bridge {
1313
requires java.net.http;
14+
requires jdk.net;
1415

1516
exports org.elasticsearch.entitlement.bridge;
1617
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
package org.elasticsearch.entitlement.bridge;
1111

12+
import jdk.nio.Channels;
13+
1214
import java.io.File;
1315
import java.io.FileDescriptor;
1416
import java.io.FileFilter;
@@ -676,6 +678,37 @@ public interface EntitlementChecker {
676678
void check$java_util_zip_ZipFile$(Class<?> callerClass, File file, int mode, Charset charset);
677679

678680
// nio
681+
// channels
682+
void check$java_nio_channels_FileChannel$(Class<?> callerClass);
683+
684+
void check$java_nio_channels_FileChannel$$open(
685+
Class<?> callerClass,
686+
Path path,
687+
Set<? extends OpenOption> options,
688+
FileAttribute<?>... attrs
689+
);
690+
691+
void check$java_nio_channels_FileChannel$$open(Class<?> callerClass, Path path, OpenOption... options);
692+
693+
void check$java_nio_channels_AsynchronousFileChannel$(Class<?> callerClass);
694+
695+
void check$java_nio_channels_AsynchronousFileChannel$$open(
696+
Class<?> callerClass,
697+
Path path,
698+
Set<? extends OpenOption> options,
699+
ExecutorService executor,
700+
FileAttribute<?>... attrs
701+
);
702+
703+
void check$java_nio_channels_AsynchronousFileChannel$$open(Class<?> callerClass, Path path, OpenOption... options);
704+
705+
void check$jdk_nio_Channels$$readWriteSelectableChannel(
706+
Class<?> callerClass,
707+
FileDescriptor fd,
708+
Channels.SelectableChannelCloser closer
709+
);
710+
711+
// files
679712
void check$java_nio_file_Files$$getOwner(Class<?> callerClass, Path path, LinkOption... options);
680713

681714
void check$java_nio_file_Files$$probeContentType(Class<?> callerClass, Path path);

libs/entitlement/qa/entitlement-test-plugin/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,5 @@
1616
// Modules we'll attempt to use in order to exercise entitlements
1717
requires java.logging;
1818
requires java.net.http;
19+
requires jdk.net;
1920
}

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

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99

1010
package org.elasticsearch.entitlement.qa.test;
1111

12+
import jdk.nio.Channels;
13+
14+
import org.elasticsearch.core.SuppressForbidden;
15+
1216
import java.io.IOException;
1317
import java.io.InputStream;
1418
import java.io.OutputStream;
@@ -24,14 +28,23 @@
2428
import java.net.SocketException;
2529
import java.net.SocketImpl;
2630
import java.net.URI;
31+
import java.nio.ByteBuffer;
32+
import java.nio.MappedByteBuffer;
2733
import java.nio.channels.AsynchronousChannelGroup;
34+
import java.nio.channels.AsynchronousFileChannel;
2835
import java.nio.channels.AsynchronousServerSocketChannel;
2936
import java.nio.channels.AsynchronousSocketChannel;
37+
import java.nio.channels.CompletionHandler;
3038
import java.nio.channels.DatagramChannel;
39+
import java.nio.channels.FileChannel;
40+
import java.nio.channels.FileLock;
3141
import java.nio.channels.Pipe;
42+
import java.nio.channels.ReadableByteChannel;
3243
import java.nio.channels.SeekableByteChannel;
44+
import java.nio.channels.SelectableChannel;
3345
import java.nio.channels.ServerSocketChannel;
3446
import java.nio.channels.SocketChannel;
47+
import java.nio.channels.WritableByteChannel;
3548
import java.nio.channels.spi.AbstractSelector;
3649
import java.nio.channels.spi.AsynchronousChannelProvider;
3750
import java.nio.channels.spi.SelectorProvider;
@@ -67,6 +80,7 @@
6780
import java.util.Map;
6881
import java.util.Set;
6982
import java.util.concurrent.ExecutorService;
83+
import java.util.concurrent.Future;
7084
import java.util.concurrent.ThreadFactory;
7185
import java.util.spi.CalendarDataProvider;
7286
import java.util.spi.CalendarNameProvider;
@@ -676,4 +690,162 @@ public void setAttribute(Path path, String attribute, Object value, LinkOption..
676690

677691
}
678692
}
693+
694+
static class DummyFileChannel extends FileChannel {
695+
@Override
696+
protected void implCloseChannel() throws IOException {
697+
698+
}
699+
700+
@Override
701+
public int read(ByteBuffer dst) throws IOException {
702+
return 0;
703+
}
704+
705+
@Override
706+
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
707+
return 0;
708+
}
709+
710+
@Override
711+
public int write(ByteBuffer src) throws IOException {
712+
return 0;
713+
}
714+
715+
@Override
716+
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
717+
return 0;
718+
}
719+
720+
@Override
721+
public long position() throws IOException {
722+
return 0;
723+
}
724+
725+
@Override
726+
public FileChannel position(long newPosition) throws IOException {
727+
return null;
728+
}
729+
730+
@Override
731+
public long size() throws IOException {
732+
return 0;
733+
}
734+
735+
@Override
736+
public FileChannel truncate(long size) throws IOException {
737+
return null;
738+
}
739+
740+
@Override
741+
public void force(boolean metaData) throws IOException {
742+
743+
}
744+
745+
@Override
746+
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
747+
return 0;
748+
}
749+
750+
@Override
751+
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
752+
return 0;
753+
}
754+
755+
@Override
756+
public int read(ByteBuffer dst, long position) throws IOException {
757+
return 0;
758+
}
759+
760+
@Override
761+
public int write(ByteBuffer src, long position) throws IOException {
762+
return 0;
763+
}
764+
765+
@Override
766+
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
767+
return null;
768+
}
769+
770+
@Override
771+
public FileLock lock(long position, long size, boolean shared) throws IOException {
772+
return null;
773+
}
774+
775+
@Override
776+
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
777+
return null;
778+
}
779+
}
780+
781+
static class DummyAsynchronousFileChannel extends AsynchronousFileChannel {
782+
@Override
783+
public boolean isOpen() {
784+
return false;
785+
}
786+
787+
@Override
788+
public void close() throws IOException {
789+
790+
}
791+
792+
@Override
793+
public long size() throws IOException {
794+
return 0;
795+
}
796+
797+
@Override
798+
public AsynchronousFileChannel truncate(long size) throws IOException {
799+
return null;
800+
}
801+
802+
@Override
803+
public void force(boolean metaData) throws IOException {
804+
805+
}
806+
807+
@Override
808+
public <A> void lock(long position, long size, boolean shared, A attachment, CompletionHandler<FileLock, ? super A> handler) {
809+
810+
}
811+
812+
@Override
813+
public Future<FileLock> lock(long position, long size, boolean shared) {
814+
return null;
815+
}
816+
817+
@Override
818+
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
819+
return null;
820+
}
821+
822+
@Override
823+
public <A> void read(ByteBuffer dst, long position, A attachment, CompletionHandler<Integer, ? super A> handler) {
824+
825+
}
826+
827+
@Override
828+
public Future<Integer> read(ByteBuffer dst, long position) {
829+
return null;
830+
}
831+
832+
@Override
833+
public <A> void write(ByteBuffer src, long position, A attachment, CompletionHandler<Integer, ? super A> handler) {
834+
835+
}
836+
837+
@Override
838+
public Future<Integer> write(ByteBuffer src, long position) {
839+
return null;
840+
}
841+
}
842+
843+
@SuppressForbidden(reason = "specifically testing readWriteSelectableChannel")
844+
static class DummySelectableChannelCloser implements Channels.SelectableChannelCloser {
845+
@Override
846+
public void implCloseChannel(SelectableChannel sc) throws IOException {}
847+
848+
@Override
849+
public void implReleaseChannel(SelectableChannel sc) throws IOException {}
850+
}
679851
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.entitlement.qa.test;
11+
12+
import org.elasticsearch.common.util.concurrent.EsExecutors;
13+
import org.elasticsearch.core.SuppressForbidden;
14+
import org.elasticsearch.entitlement.qa.entitled.EntitledActions;
15+
16+
import java.io.FileDescriptor;
17+
import java.io.IOException;
18+
import java.nio.channels.AsynchronousFileChannel;
19+
import java.nio.channels.FileChannel;
20+
import java.nio.file.StandardOpenOption;
21+
import java.util.Set;
22+
23+
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED;
24+
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS;
25+
26+
class NioChannelsActions {
27+
28+
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
29+
static void createFileChannel() throws IOException {
30+
new DummyImplementations.DummyFileChannel().close();
31+
}
32+
33+
@EntitlementTest(expectedAccess = PLUGINS)
34+
static void fileChannelOpenForWrite() throws IOException {
35+
FileChannel.open(FileCheckActions.readWriteFile(), StandardOpenOption.WRITE).close();
36+
}
37+
38+
@EntitlementTest(expectedAccess = PLUGINS)
39+
static void fileChannelOpenForRead() throws IOException {
40+
FileChannel.open(FileCheckActions.readFile()).close();
41+
}
42+
43+
@EntitlementTest(expectedAccess = PLUGINS)
44+
static void fileChannelOpenForWriteWithOptions() throws IOException {
45+
FileChannel.open(FileCheckActions.readWriteFile(), Set.of(StandardOpenOption.WRITE)).close();
46+
}
47+
48+
@EntitlementTest(expectedAccess = PLUGINS)
49+
static void fileChannelOpenForReadWithOptions() throws IOException {
50+
FileChannel.open(FileCheckActions.readFile(), Set.of(StandardOpenOption.READ)).close();
51+
}
52+
53+
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
54+
static void createAsynchronousFileChannel() throws IOException {
55+
new DummyImplementations.DummyAsynchronousFileChannel().close();
56+
}
57+
58+
@EntitlementTest(expectedAccess = PLUGINS)
59+
static void asynchronousFileChannelOpenForWrite() throws IOException {
60+
var file = EntitledActions.createTempFileForWrite();
61+
AsynchronousFileChannel.open(file, StandardOpenOption.WRITE).close();
62+
}
63+
64+
@EntitlementTest(expectedAccess = PLUGINS)
65+
static void asynchronousFileChannelOpenForRead() throws IOException {
66+
var file = EntitledActions.createTempFileForRead();
67+
AsynchronousFileChannel.open(file).close();
68+
}
69+
70+
@EntitlementTest(expectedAccess = PLUGINS)
71+
static void asynchronousFileChannelOpenForWriteWithOptions() throws IOException {
72+
var file = EntitledActions.createTempFileForWrite();
73+
AsynchronousFileChannel.open(file, Set.of(StandardOpenOption.WRITE), EsExecutors.DIRECT_EXECUTOR_SERVICE).close();
74+
}
75+
76+
@EntitlementTest(expectedAccess = PLUGINS)
77+
static void asynchronousFileChannelOpenForReadWithOptions() throws IOException {
78+
var file = EntitledActions.createTempFileForRead();
79+
AsynchronousFileChannel.open(file, Set.of(StandardOpenOption.READ), EsExecutors.DIRECT_EXECUTOR_SERVICE).close();
80+
}
81+
82+
@SuppressForbidden(reason = "specifically testing jdk.nio.Channels")
83+
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
84+
static void channelsReadWriteSelectableChannel() throws IOException {
85+
jdk.nio.Channels.readWriteSelectableChannel(new FileDescriptor(), new DummyImplementations.DummySelectableChannelCloser()).close();
86+
}
87+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
189189
getTestEntries(FileStoreActions.class),
190190
getTestEntries(ManageThreadsActions.class),
191191
getTestEntries(NativeActions.class),
192+
getTestEntries(NioChannelsActions.class),
192193
getTestEntries(NioFilesActions.class),
193194
getTestEntries(NioFileSystemActions.class),
194195
getTestEntries(PathActions.class),

libs/entitlement/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
requires org.elasticsearch.base;
1515
requires jdk.attach;
1616
requires java.net.http;
17+
requires jdk.net;
1718

1819
requires static org.elasticsearch.entitlement.bridge; // At runtime, this will be in java.base
1920

0 commit comments

Comments
 (0)