Skip to content

Commit 436e604

Browse files
authored
[Entitlements] Add checks for native libraries restricted methods (#120775)
1 parent 723387a commit 436e604

File tree

6 files changed

+334
-2
lines changed

6 files changed

+334
-2
lines changed

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
import java.io.InputStream;
1313
import java.io.PrintStream;
1414
import java.io.PrintWriter;
15+
import java.lang.foreign.AddressLayout;
16+
import java.lang.foreign.Arena;
17+
import java.lang.foreign.FunctionDescriptor;
18+
import java.lang.foreign.Linker;
19+
import java.lang.foreign.MemoryLayout;
20+
import java.lang.foreign.MemorySegment;
21+
import java.lang.invoke.MethodHandle;
1522
import java.net.ContentHandlerFactory;
1623
import java.net.DatagramPacket;
1724
import java.net.DatagramSocket;
@@ -40,11 +47,13 @@
4047
import java.nio.channels.DatagramChannel;
4148
import java.nio.channels.ServerSocketChannel;
4249
import java.nio.channels.SocketChannel;
50+
import java.nio.file.Path;
4351
import java.security.cert.CertStoreParameters;
4452
import java.util.List;
4553
import java.util.Locale;
4654
import java.util.Properties;
4755
import java.util.TimeZone;
56+
import java.util.function.Consumer;
4857

4958
import javax.net.ssl.HostnameVerifier;
5059
import javax.net.ssl.HttpsURLConnection;
@@ -411,11 +420,68 @@ public interface EntitlementChecker {
411420
//
412421
// Load native libraries
413422
//
423+
// Using the list of restricted methods from https://download.java.net/java/early_access/jdk24/docs/api/restricted-list.html
414424
void check$java_lang_Runtime$load(Class<?> callerClass, Runtime that, String filename);
415425

416426
void check$java_lang_Runtime$loadLibrary(Class<?> callerClass, Runtime that, String libname);
417427

418428
void check$java_lang_System$$load(Class<?> callerClass, String filename);
419429

420430
void check$java_lang_System$$loadLibrary(Class<?> callerClass, String libname);
431+
432+
// Sealed implementation of java.lang.foreign.AddressLayout
433+
void check$jdk_internal_foreign_layout_ValueLayouts$OfAddressImpl$withTargetLayout(
434+
Class<?> callerClass,
435+
AddressLayout that,
436+
MemoryLayout memoryLayout
437+
);
438+
439+
// Sealed implementation of java.lang.foreign.Linker
440+
void check$jdk_internal_foreign_abi_AbstractLinker$downcallHandle(
441+
Class<?> callerClass,
442+
Linker that,
443+
FunctionDescriptor function,
444+
Linker.Option... options
445+
);
446+
447+
void check$jdk_internal_foreign_abi_AbstractLinker$downcallHandle(
448+
Class<?> callerClass,
449+
Linker that,
450+
MemorySegment address,
451+
FunctionDescriptor function,
452+
Linker.Option... options
453+
);
454+
455+
void check$jdk_internal_foreign_abi_AbstractLinker$upcallStub(
456+
Class<?> callerClass,
457+
Linker that,
458+
MethodHandle target,
459+
FunctionDescriptor function,
460+
Arena arena,
461+
Linker.Option... options
462+
);
463+
464+
// Sealed implementation for java.lang.foreign.MemorySegment.reinterpret(long)
465+
void check$jdk_internal_foreign_AbstractMemorySegmentImpl$reinterpret(Class<?> callerClass, MemorySegment that, long newSize);
466+
467+
void check$jdk_internal_foreign_AbstractMemorySegmentImpl$reinterpret(
468+
Class<?> callerClass,
469+
MemorySegment that,
470+
long newSize,
471+
Arena arena,
472+
Consumer<MemorySegment> cleanup
473+
);
474+
475+
void check$jdk_internal_foreign_AbstractMemorySegmentImpl$reinterpret(
476+
Class<?> callerClass,
477+
MemorySegment that,
478+
Arena arena,
479+
Consumer<MemorySegment> cleanup
480+
);
481+
482+
void check$java_lang_foreign_SymbolLookup$$libraryLookup(Class<?> callerClass, String name, Arena arena);
483+
484+
void check$java_lang_foreign_SymbolLookup$$libraryLookup(Class<?> callerClass, Path path, Arena arena);
485+
486+
void check$java_lang_ModuleLayer$Controller$enableNativeAccess(Class<?> callerClass, ModuleLayer.Controller that, Module target);
421487
}

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,21 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
199199
entry("runtime_load", forPlugins(LoadNativeLibrariesCheckActions::runtimeLoad)),
200200
entry("runtime_load_library", forPlugins(LoadNativeLibrariesCheckActions::runtimeLoadLibrary)),
201201
entry("system_load", forPlugins(LoadNativeLibrariesCheckActions::systemLoad)),
202-
entry("system_load_library", forPlugins(LoadNativeLibrariesCheckActions::systemLoadLibrary))
202+
entry("system_load_library", forPlugins(LoadNativeLibrariesCheckActions::systemLoadLibrary)),
203+
204+
entry("enable_native_access", new CheckAction(VersionSpecificNativeChecks::enableNativeAccess, false, 22)),
205+
entry("address_target_layout", new CheckAction(VersionSpecificNativeChecks::addressLayoutWithTargetLayout, false, 22)),
206+
entry("donwncall_handle", new CheckAction(VersionSpecificNativeChecks::linkerDowncallHandle, false, 22)),
207+
entry("donwncall_handle_with_address", new CheckAction(VersionSpecificNativeChecks::linkerDowncallHandleWithAddress, false, 22)),
208+
entry("upcall_stub", new CheckAction(VersionSpecificNativeChecks::linkerUpcallStub, false, 22)),
209+
entry("reinterpret", new CheckAction(VersionSpecificNativeChecks::memorySegmentReinterpret, false, 22)),
210+
entry("reinterpret_cleanup", new CheckAction(VersionSpecificNativeChecks::memorySegmentReinterpretWithCleanup, false, 22)),
211+
entry(
212+
"reinterpret_size_cleanup",
213+
new CheckAction(VersionSpecificNativeChecks::memorySegmentReinterpretWithSizeAndCleanup, false, 22)
214+
),
215+
entry("symbol_lookup_name", new CheckAction(VersionSpecificNativeChecks::symbolLookupWithName, false, 22)),
216+
entry("symbol_lookup_path", new CheckAction(VersionSpecificNativeChecks::symbolLookupWithPath, false, 22))
203217
)
204218
.filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion())
205219
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
class VersionSpecificNativeChecks {
13+
14+
static void enableNativeAccess() throws Exception {}
15+
16+
static void addressLayoutWithTargetLayout() {}
17+
18+
static void linkerDowncallHandle() {}
19+
20+
static void linkerDowncallHandleWithAddress() {}
21+
22+
static void linkerUpcallStub() throws NoSuchMethodException {}
23+
24+
static void memorySegmentReinterpret() {}
25+
26+
static void memorySegmentReinterpretWithCleanup() {}
27+
28+
static void memorySegmentReinterpretWithSizeAndCleanup() {}
29+
30+
static void symbolLookupWithPath() {}
31+
32+
static void symbolLookupWithName() {}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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.entitlement.qa.entitled.EntitledPlugin;
13+
14+
import java.lang.foreign.AddressLayout;
15+
import java.lang.foreign.Arena;
16+
import java.lang.foreign.FunctionDescriptor;
17+
import java.lang.foreign.Linker;
18+
import java.lang.foreign.MemoryLayout;
19+
import java.lang.foreign.MemorySegment;
20+
import java.lang.foreign.SymbolLookup;
21+
import java.lang.foreign.ValueLayout;
22+
import java.lang.invoke.MethodHandle;
23+
import java.lang.invoke.MethodHandles;
24+
import java.lang.invoke.MethodType;
25+
import java.lang.module.Configuration;
26+
import java.lang.module.ModuleFinder;
27+
import java.nio.file.Path;
28+
import java.util.List;
29+
import java.util.Set;
30+
31+
import static java.lang.foreign.ValueLayout.ADDRESS;
32+
import static java.lang.foreign.ValueLayout.JAVA_LONG;
33+
34+
class VersionSpecificNativeChecks {
35+
36+
static void enableNativeAccess() throws Exception {
37+
ModuleLayer parent = ModuleLayer.boot();
38+
39+
var location = EntitledPlugin.class.getProtectionDomain().getCodeSource().getLocation();
40+
41+
// We create a layer for our own module, so we have a controller to try and call enableNativeAccess on it.
42+
// This works in both the modular and non-modular case: the target module has to be present in the new layer, but its entitlements
43+
// and policies do not matter to us: we are checking that the caller is (or isn't) entitled to use enableNativeAccess
44+
Configuration cf = parent.configuration()
45+
.resolve(ModuleFinder.of(Path.of(location.toURI())), ModuleFinder.of(), Set.of("org.elasticsearch.entitlement.qa.entitled"));
46+
var controller = ModuleLayer.defineModulesWithOneLoader(cf, List.of(parent), ClassLoader.getSystemClassLoader());
47+
var targetModule = controller.layer().findModule("org.elasticsearch.entitlement.qa.entitled");
48+
49+
controller.enableNativeAccess(targetModule.get());
50+
}
51+
52+
static void addressLayoutWithTargetLayout() {
53+
AddressLayout addressLayout = ADDRESS.withoutTargetLayout();
54+
addressLayout.withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, ValueLayout.JAVA_BYTE));
55+
}
56+
57+
static void linkerDowncallHandle() {
58+
Linker linker = Linker.nativeLinker();
59+
linker.downcallHandle(FunctionDescriptor.of(JAVA_LONG, ADDRESS));
60+
}
61+
62+
static void linkerDowncallHandleWithAddress() {
63+
Linker linker = Linker.nativeLinker();
64+
linker.downcallHandle(linker.defaultLookup().find("strlen").get(), FunctionDescriptor.of(JAVA_LONG, ADDRESS));
65+
}
66+
67+
static int callback() {
68+
return 0;
69+
}
70+
71+
static void linkerUpcallStub() throws NoSuchMethodException {
72+
Linker linker = Linker.nativeLinker();
73+
74+
MethodHandle mh = null;
75+
try {
76+
mh = MethodHandles.lookup().findStatic(VersionSpecificNativeChecks.class, "callback", MethodType.methodType(int.class));
77+
} catch (IllegalAccessException e) {
78+
assert false;
79+
}
80+
81+
FunctionDescriptor callbackDescriptor = FunctionDescriptor.of(ValueLayout.JAVA_INT);
82+
linker.upcallStub(mh, callbackDescriptor, Arena.ofAuto());
83+
}
84+
85+
static void memorySegmentReinterpret() {
86+
Arena arena = Arena.ofAuto();
87+
MemorySegment segment = arena.allocate(100);
88+
segment.reinterpret(50);
89+
}
90+
91+
static void memorySegmentReinterpretWithCleanup() {
92+
Arena arena = Arena.ofAuto();
93+
MemorySegment segment = arena.allocate(100);
94+
segment.reinterpret(Arena.ofAuto(), s -> {});
95+
}
96+
97+
static void memorySegmentReinterpretWithSizeAndCleanup() {
98+
Arena arena = Arena.ofAuto();
99+
MemorySegment segment = arena.allocate(100);
100+
segment.reinterpret(50, Arena.ofAuto(), s -> {});
101+
}
102+
103+
static void symbolLookupWithPath() {
104+
try {
105+
SymbolLookup.libraryLookup(Path.of("/foo/bar/libFoo.so"), Arena.ofAuto());
106+
} catch (IllegalArgumentException e) {
107+
// IllegalArgumentException is thrown if path does not point to a valid library (and it does not)
108+
}
109+
}
110+
111+
static void symbolLookupWithName() {
112+
try {
113+
SymbolLookup.libraryLookup("foo", Arena.ofAuto());
114+
} catch (IllegalArgumentException e) {
115+
// IllegalArgumentException is thrown if path does not point to a valid library (and it does not)
116+
}
117+
}
118+
}

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
@@ -98,7 +98,9 @@ private static PolicyManager createPolicyManager() {
9898
)
9999
),
100100
new Scope("org.apache.httpcomponents.httpclient", List.of(new OutboundNetworkEntitlement())),
101-
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement()))
101+
new Scope("io.netty.transport", List.of(new InboundNetworkEntitlement(), new OutboundNetworkEntitlement())),
102+
new Scope("org.apache.lucene.core", List.of(new LoadNativeLibrariesEntitlement())),
103+
new Scope("org.elasticsearch.nativeaccess", List.of(new LoadNativeLibrariesEntitlement()))
102104
)
103105
);
104106
// agents run without a module, so this is a special hack for the apm agent

0 commit comments

Comments
 (0)