Skip to content

Commit 36bf9a0

Browse files
authored
Add package-info.java and javadocs to document Entitlements design and implementation (#127023) (#127141)
Design and implementation of Entitlement with this level of detail needs to stay close to the code, and take advantage of javadoc features like linking and class-references to help us with refactorings and future code changes. The bulk of the information went into the package-info file for the main library, but I split up some parts and referenced them from the main doc, where I thought it made sense (mainly: the bridge sub-project for some implementation details, PolicyManager, EntitlementInitialization and FileAccessTree); this way they still can be reached from the "overview" while being closer to where the information really belongs. Relates to ES-11284
1 parent 3b2f9d4 commit 36bf9a0

File tree

7 files changed

+484
-3
lines changed

7 files changed

+484
-3
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
/**
11+
* Contains classes that need to be used directly from instrumented methods.
12+
* It's a minimal shim that is patched into the {@code java.base} module so that it is callable from the class library methods instrumented
13+
* by the agent. The shim retains a {@link org.elasticsearch.entitlement.bridge.EntitlementChecker} instance (inside its
14+
* {@link org.elasticsearch.entitlement.bridge.EntitlementCheckerHandle} holder) and forwards the entitlement checks to the main library,
15+
* that exists in the system classloader.
16+
* {@link org.elasticsearch.entitlement.bridge.EntitlementChecker} holds all the entitlements check definitions, one for each instrumented
17+
* method.
18+
* <p>
19+
* In order to work across multiple Java versions, this project uses multi-release jars via the {@code mrjar} plugin, which makes it is
20+
* possible to specify classes for specific Java versions in specific {@code src} folders (e.g. {@code main23} for classes available to
21+
* Java 23+).
22+
* All the versioned Java classes are merged into the bridge jar. Therefore, we must prefix the class name
23+
* with the version, e.g. {@code Java23EntitlementCheckerHandle} and {@code Java23EntitlementChecker}.
24+
* </p>
25+
*/
26+
package org.elasticsearch.entitlement.bridge;

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

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,42 @@ public static EntitlementChecker checker() {
101101
return manager;
102102
}
103103

104-
// Note: referenced by agent reflectively
104+
/**
105+
* Initializes the Entitlement system:
106+
* <ol>
107+
* <li>
108+
* Finds the version-specific subclass of {@link EntitlementChecker} to use
109+
* </li>
110+
* <li>
111+
* Builds the set of methods to instrument using {@link InstrumentationService#lookupMethods}
112+
* </li>
113+
* <li>
114+
* Augment this set “dynamically” using {@link InstrumentationService#lookupImplementationMethod}
115+
* </li>
116+
* <li>
117+
* Creates an {@link Instrumenter} via {@link InstrumentationService#newInstrumenter}, and adds a new {@link Transformer} (derived from
118+
* {@link java.lang.instrument.ClassFileTransformer}) that uses it. Transformers are invoked when a class is about to load, after its
119+
* bytes have been deserialized to memory but before the class is initialized.
120+
* </li>
121+
* <li>
122+
* Re-transforms all already loaded classes: we force the {@link Instrumenter} to run on classes that might have been already loaded
123+
* before entitlement initialization by calling the {@link java.lang.instrument.Instrumentation#retransformClasses} method on all
124+
* classes that were already loaded.
125+
* </li>
126+
* </ol>
127+
* <p>
128+
* The third step is needed as the JDK exposes some API through interfaces that have different (internal) implementations
129+
* depending on the JVM host platform. As we cannot instrument an interfaces, we find its concrete implementation.
130+
* A prime example is {@link FileSystemProvider}, which has different implementations (e.g. {@code UnixFileSystemProvider} or
131+
* {@code WindowsFileSystemProvider}). At runtime, we find the implementation class which is currently used by the JVM, and add
132+
* its methods to the set of methods to instrument. See e.g. {@link EntitlementInitialization#fileSystemProviderChecks}.
133+
* </p>
134+
* <p>
135+
* <strong>NOTE:</strong> this method is referenced by the agent reflectively
136+
* </p>
137+
*
138+
* @param inst the JVM instrumentation class instance
139+
*/
105140
public static void initialize(Instrumentation inst) throws Exception {
106141
manager = initChecker();
107142

libs/entitlement/src/main/java/org/elasticsearch/entitlement/instrumentation/InstrumentationService.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,32 @@ record InstrumentationInfo(MethodKey targetMethod, CheckMethod checkMethod) {}
2222

2323
Instrumenter newInstrumenter(Class<?> clazz, Map<MethodKey, CheckMethod> methods);
2424

25+
/**
26+
* This method uses the method names of the provided class to identify the JDK method to instrument; it examines all methods prefixed
27+
* by {@code check$}, and parses the rest of the name to extract the JDK method:
28+
* <ul>
29+
* <li>
30+
* Instance methods have the fully qualified class name (with . replaced by _), followed by $, followed by the method name. Example:
31+
* {@link org.elasticsearch.entitlement.bridge.EntitlementChecker#check$java_lang_Runtime$halt}
32+
* </li>
33+
* <li>
34+
* Static methods have the fully qualified class name (with . replaced by _), followed by $$, followed by the method name. Example:
35+
* {@link org.elasticsearch.entitlement.bridge.EntitlementChecker#check$java_lang_System$$exit}
36+
* </li>
37+
* <li>
38+
* Constructors have the fully qualified class name (with . replaced by _), followed by $ and nothing else. Example:
39+
* {@link org.elasticsearch.entitlement.bridge.EntitlementChecker#check$java_lang_ClassLoader$}
40+
* </li>
41+
* </ul>
42+
* <p>
43+
* <strong>NOTE:</strong> look up of methods using this convention is the primary way we use to identify which methods to instrument,
44+
* but other methods can be added to the map of methods to instrument. See
45+
* {@link org.elasticsearch.entitlement.initialization.EntitlementInitialization#initialize} for details.
46+
* </p>
47+
*
48+
* @param clazz the class to inspect to find methods to instrument
49+
* @throws ClassNotFoundException if the class is not defined or cannot be inspected
50+
*/
2551
Map<MethodKey, CheckMethod> lookupMethods(Class<?> clazz) throws ClassNotFoundException;
2652

2753
InstrumentationInfo lookupImplementationMethod(

libs/entitlement/src/main/java/org/elasticsearch/entitlement/instrumentation/Instrumenter.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,30 @@
1010
package org.elasticsearch.entitlement.instrumentation;
1111

1212
public interface Instrumenter {
13+
14+
/**
15+
* Instruments the appropriate methods of a class by adding a prologue that checks for entitlements.
16+
* The prologue:
17+
* <ol>
18+
* <li>
19+
* gets the {@link org.elasticsearch.entitlement.bridge.EntitlementChecker} instance from the
20+
* {@link org.elasticsearch.entitlement.bridge.EntitlementCheckerHandle} holder;
21+
* </li>
22+
* <li>
23+
* identifies the caller class and pushes it onto the stack;
24+
* </li>
25+
* <li>
26+
* forwards the instrumented function parameters;
27+
* </li>
28+
* <li>
29+
* calls the {@link org.elasticsearch.entitlement.bridge.EntitlementChecker} method corresponding to the method it is injected into
30+
* (e.g. {@code check$java_net_DatagramSocket$receive} for {@link java.net.DatagramSocket#receive}).
31+
* </li>
32+
* </ol>
33+
* @param className the name of the class to instrument
34+
* @param classfileBuffer its bytecode
35+
* @param verify whether we should verify the bytecode before and after instrumentation
36+
* @return the instrumented class bytes
37+
*/
1338
byte[] instrumentClass(String className, byte[] classfileBuffer, boolean verify);
1439
}

0 commit comments

Comments
 (0)