Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
Expand Up @@ -180,7 +180,7 @@ private static Stream<String> maybeAttachEntitlementAgent(boolean useEntitlement
}
// We instrument classes in these modules to call the bridge. Because the bridge gets patched
// into java.base, we must export the bridge from java.base to these modules, as a comma-separated list
String modulesContainingEntitlementInstrumentation = "java.logging,java.net.http,java.naming";
String modulesContainingEntitlementInstrumentation = "java.logging,java.net.http,java.naming,jdk.net";
return Stream.of(
"-Des.entitlements.enabled=true",
"-XX:+EnableDynamicAgentLoading",
Expand Down
1 change: 1 addition & 0 deletions libs/entitlement/bridge/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// At build and run time, the bridge is patched into the java.base module.
module org.elasticsearch.entitlement.bridge {
requires java.net.http;
requires jdk.net;

exports org.elasticsearch.entitlement.bridge;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@

package org.elasticsearch.entitlement.bridge;

import jdk.nio.Channels;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.InputStream;
Expand Down Expand Up @@ -587,6 +590,37 @@ public interface EntitlementChecker {
void check$java_util_Scanner$(Class<?> callerClass, File source, Charset charset);

// nio
// channels
void check$java_nio_channels_FileChannel$(Class<?> callerClass);

void check$java_nio_channels_FileChannel$$open(
Class<?> callerClass,
Path path,
Set<? extends OpenOption> options,
FileAttribute<?>... attrs
);

void check$java_nio_channels_FileChannel$$open(Class<?> callerClass, Path path, OpenOption... options);

void check$java_nio_channels_AsynchronousFileChannel$(Class<?> callerClass);

void check$java_nio_channels_AsynchronousFileChannel$$open(
Class<?> callerClass,
Path path,
Set<? extends OpenOption> options,
ExecutorService executor,
FileAttribute<?>... attrs
);

void check$java_nio_channels_AsynchronousFileChannel$$open(Class<?> callerClass, Path path, OpenOption... options);

void check$jdk_nio_Channels$$readWriteSelectableChannel(
Class<?> callerClass,
FileDescriptor fd,
Channels.SelectableChannelCloser closer
);

// files
void check$java_nio_file_Files$$getOwner(Class<?> callerClass, Path path, LinkOption... options);

void check$java_nio_file_Files$$probeContentType(Class<?> callerClass, Path path);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@
// Modules we'll attempt to use in order to exercise entitlements
requires java.logging;
requires java.net.http;
requires jdk.net;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

package org.elasticsearch.entitlement.qa.test;

import jdk.nio.Channels;

import org.elasticsearch.core.SuppressForbidden;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
Expand All @@ -24,14 +28,23 @@
import java.net.SocketException;
import java.net.SocketImpl;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.nio.channels.DatagramChannel;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.Pipe;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.channels.SelectableChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.AsynchronousChannelProvider;
import java.nio.channels.spi.SelectorProvider;
Expand Down Expand Up @@ -67,6 +80,7 @@
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.spi.CalendarDataProvider;
import java.util.spi.CalendarNameProvider;
Expand Down Expand Up @@ -676,4 +690,162 @@ public void setAttribute(Path path, String attribute, Object value, LinkOption..

}
}

static class DummyFileChannel extends FileChannel {
Copy link
Contributor

@mosche mosche Feb 18, 2025

Choose a reason for hiding this comment

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

Wondering, that's a lot of code to just test close and it's not directly obvious what this is used for. Maybe add a comment or is it simpler in the end to create a channel via the entitled plugin?

Copy link
Contributor

Choose a reason for hiding this comment

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

Wait, makes sense, I misread the test... we reject subclassing 👍

@Override
protected void implCloseChannel() throws IOException {

}

@Override
public int read(ByteBuffer dst) throws IOException {
return 0;
}

@Override
public long read(ByteBuffer[] dsts, int offset, int length) throws IOException {
return 0;
}

@Override
public int write(ByteBuffer src) throws IOException {
return 0;
}

@Override
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException {
return 0;
}

@Override
public long position() throws IOException {
return 0;
}

@Override
public FileChannel position(long newPosition) throws IOException {
return null;
}

@Override
public long size() throws IOException {
return 0;
}

@Override
public FileChannel truncate(long size) throws IOException {
return null;
}

@Override
public void force(boolean metaData) throws IOException {

}

@Override
public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
return 0;
}

@Override
public long transferFrom(ReadableByteChannel src, long position, long count) throws IOException {
return 0;
}

@Override
public int read(ByteBuffer dst, long position) throws IOException {
return 0;
}

@Override
public int write(ByteBuffer src, long position) throws IOException {
return 0;
}

@Override
public MappedByteBuffer map(MapMode mode, long position, long size) throws IOException {
return null;
}

@Override
public FileLock lock(long position, long size, boolean shared) throws IOException {
return null;
}

@Override
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
return null;
}
}

static class DummyAsynchronousFileChannel extends AsynchronousFileChannel {
@Override
public boolean isOpen() {
return false;
}

@Override
public void close() throws IOException {

}

@Override
public long size() throws IOException {
return 0;
}

@Override
public AsynchronousFileChannel truncate(long size) throws IOException {
return null;
}

@Override
public void force(boolean metaData) throws IOException {

}

@Override
public <A> void lock(long position, long size, boolean shared, A attachment, CompletionHandler<FileLock, ? super A> handler) {

}

@Override
public Future<FileLock> lock(long position, long size, boolean shared) {
return null;
}

@Override
public FileLock tryLock(long position, long size, boolean shared) throws IOException {
return null;
}

@Override
public <A> void read(ByteBuffer dst, long position, A attachment, CompletionHandler<Integer, ? super A> handler) {

}

@Override
public Future<Integer> read(ByteBuffer dst, long position) {
return null;
}

@Override
public <A> void write(ByteBuffer src, long position, A attachment, CompletionHandler<Integer, ? super A> handler) {

}

@Override
public Future<Integer> write(ByteBuffer src, long position) {
return null;
}
}

@SuppressForbidden(reason = "specifically testing readWriteSelectableChannel")
static class DummySelectableChannelCloser implements Channels.SelectableChannelCloser {
@Override
public void implCloseChannel(SelectableChannel sc) throws IOException {}

@Override
public void implReleaseChannel(SelectableChannel sc) throws IOException {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

package org.elasticsearch.entitlement.qa.test;

import org.elasticsearch.common.util.concurrent.EsExecutors;
import org.elasticsearch.core.SuppressForbidden;
import org.elasticsearch.entitlement.qa.entitled.EntitledActions;

import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.Set;

import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED;
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.PLUGINS;

class NioChannelsActions {

@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createFileChannel() throws IOException {
new DummyImplementations.DummyFileChannel().close();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void fileChannelOpenForWrite() throws IOException {
FileChannel.open(FileCheckActions.readWriteFile(), StandardOpenOption.WRITE).close();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void fileChannelOpenForRead() throws IOException {
FileChannel.open(FileCheckActions.readFile()).close();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void fileChannelOpenForWriteWithOptions() throws IOException {
FileChannel.open(FileCheckActions.readWriteFile(), Set.of(StandardOpenOption.WRITE)).close();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void fileChannelOpenForReadWithOptions() throws IOException {
FileChannel.open(FileCheckActions.readFile(), Set.of(StandardOpenOption.READ)).close();
}

@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void createAsynchronousFileChannel() throws IOException {
new DummyImplementations.DummyAsynchronousFileChannel().close();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void asynchronousFileChannelOpenForWrite() throws IOException {
var file = EntitledActions.createTempFileForWrite();
AsynchronousFileChannel.open(file, StandardOpenOption.WRITE).close();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void asynchronousFileChannelOpenForRead() throws IOException {
var file = EntitledActions.createTempFileForRead();
AsynchronousFileChannel.open(file).close();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void asynchronousFileChannelOpenForWriteWithOptions() throws IOException {
var file = EntitledActions.createTempFileForWrite();
AsynchronousFileChannel.open(file, Set.of(StandardOpenOption.WRITE), EsExecutors.DIRECT_EXECUTOR_SERVICE).close();
}

@EntitlementTest(expectedAccess = PLUGINS)
static void asynchronousFileChannelOpenForReadWithOptions() throws IOException {
var file = EntitledActions.createTempFileForRead();
AsynchronousFileChannel.open(file, Set.of(StandardOpenOption.READ), EsExecutors.DIRECT_EXECUTOR_SERVICE).close();
}

@SuppressForbidden(reason = "specifically testing jdk.nio.Channels")
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
static void channelsReadWriteSelectableChannel() throws IOException {
jdk.nio.Channels.readWriteSelectableChannel(new FileDescriptor(), new DummyImplementations.DummySelectableChannelCloser()).close();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
getTestEntries(FileStoreActions.class),
getTestEntries(ManageThreadsActions.class),
getTestEntries(NativeActions.class),
getTestEntries(NioChannelsActions.class),
getTestEntries(NioFileSystemActions.class),
getTestEntries(PathActions.class),
getTestEntries(SpiActions.class),
Expand Down
1 change: 1 addition & 0 deletions libs/entitlement/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
requires org.elasticsearch.base;
requires jdk.attach;
requires java.net.http;
requires jdk.net;

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

Expand Down
Loading