Skip to content

Commit 77ab5a4

Browse files
rjernstelasticsearchmachine
andauthored
Fail startup if entitlement instrumentation failed (#130051) (#130121)
* Fail startup if entitlement instrumentation failed (#130051) Java class transformers swallow exceptions, so any instrumentation failures, for example due to a java version mismatch, will silently proceed with startup, which then will cryptically fail the entitlement self test. This commit logs exceptions that occur during instrumentation, as well as plumb through the fact that any occured so that bootstrap can fail rather than allow startup to proceed. * [CI] Auto commit changes from spotless --------- Co-authored-by: elasticsearchmachine <[email protected]>
1 parent 72a8b9f commit 77ab5a4

File tree

4 files changed

+59
-16
lines changed

4 files changed

+59
-16
lines changed

libs/entitlement/src/main/java/org/elasticsearch/entitlement/bootstrap/EntitlementBootstrap.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,12 @@ public static void bootstrap(
122122
suppressFailureLogPackages
123123
);
124124
exportInitializationToAgent();
125+
125126
loadAgent(findAgentJar());
127+
128+
if (EntitlementInitialization.getError() != null) {
129+
throw EntitlementInitialization.getError();
130+
}
126131
}
127132

128133
private static Path getUserHome() {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ static void initialize(Instrumentation inst, Class<?> checkerInterface, boolean
119119
// We should have failed already in the loop above, but just in case we did not, rethrow.
120120
throw e;
121121
}
122+
123+
if (transformer.hadErrors()) {
124+
throw new RuntimeException("Failed to transform JDK classes for entitlements");
125+
}
122126
}
123127

124128
private static Map<MethodKey, CheckMethod> getMethodsToInstrument(Class<?> checkerInterface) throws ClassNotFoundException,

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

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
import org.elasticsearch.entitlement.runtime.policy.PathLookup;
1717
import org.elasticsearch.entitlement.runtime.policy.Policy;
1818
import org.elasticsearch.entitlement.runtime.policy.PolicyManager;
19+
import org.elasticsearch.logging.LogManager;
20+
import org.elasticsearch.logging.Logger;
1921

2022
import java.lang.instrument.Instrumentation;
2123
import java.lang.reflect.Constructor;
2224
import java.lang.reflect.InvocationTargetException;
2325
import java.util.Map;
2426
import java.util.Set;
27+
import java.util.concurrent.atomic.AtomicReference;
2528

2629
/**
2730
* Called by the agent during {@code agentmain} to configure the entitlement system,
@@ -31,16 +34,25 @@
3134
* to begin injecting our instrumentation.
3235
*/
3336
public class EntitlementInitialization {
37+
private static final Logger logger = LogManager.getLogger(EntitlementInitialization.class);
3438

3539
private static final Module ENTITLEMENTS_MODULE = PolicyManager.class.getModule();
3640

3741
private static ElasticsearchEntitlementChecker manager;
42+
private static AtomicReference<RuntimeException> error = new AtomicReference<>();
3843

3944
// Note: referenced by bridge reflectively
4045
public static EntitlementChecker checker() {
4146
return manager;
4247
}
4348

49+
/**
50+
* Return any exception that occurred during initialization
51+
*/
52+
public static RuntimeException getError() {
53+
return error.get();
54+
}
55+
4456
/**
4557
* Initializes the Entitlement system:
4658
* <ol>
@@ -60,19 +72,25 @@ public static EntitlementChecker checker() {
6072
*
6173
* @param inst the JVM instrumentation class instance
6274
*/
63-
public static void initialize(Instrumentation inst) throws Exception {
64-
manager = initChecker();
75+
public static void initialize(Instrumentation inst) {
76+
try {
77+
manager = initChecker();
6578

66-
var verifyBytecode = Booleans.parseBoolean(System.getProperty("es.entitlements.verify_bytecode", "false"));
67-
if (verifyBytecode) {
68-
ensureClassesSensitiveToVerificationAreInitialized();
69-
}
79+
var verifyBytecode = Booleans.parseBoolean(System.getProperty("es.entitlements.verify_bytecode", "false"));
80+
if (verifyBytecode) {
81+
ensureClassesSensitiveToVerificationAreInitialized();
82+
}
7083

71-
DynamicInstrumentation.initialize(
72-
inst,
73-
getVersionSpecificCheckerClass(EntitlementChecker.class, Runtime.version().feature()),
74-
verifyBytecode
75-
);
84+
DynamicInstrumentation.initialize(
85+
inst,
86+
getVersionSpecificCheckerClass(EntitlementChecker.class, Runtime.version().feature()),
87+
verifyBytecode
88+
);
89+
} catch (Exception e) {
90+
// exceptions thrown within the agent will be swallowed, so capture it here
91+
// instead so that it can be retrieved by bootstrap
92+
error.set(new RuntimeException("Failed to initialize entitlements", e));
93+
}
7694
}
7795

7896
private static PolicyManager createPolicyManager() {

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@
99

1010
package org.elasticsearch.entitlement.instrumentation;
1111

12+
import org.elasticsearch.logging.LogManager;
13+
import org.elasticsearch.logging.Logger;
14+
1215
import java.lang.instrument.ClassFileTransformer;
1316
import java.security.ProtectionDomain;
1417
import java.util.Set;
18+
import java.util.concurrent.atomic.AtomicBoolean;
1519

1620
/**
1721
* A {@link ClassFileTransformer} that applies an {@link Instrumenter} to the appropriate classes.
1822
*/
1923
public class Transformer implements ClassFileTransformer {
24+
private static final Logger logger = LogManager.getLogger(Transformer.class);
2025
private final Instrumenter instrumenter;
2126
private final Set<String> classesToTransform;
27+
private final AtomicBoolean hadErrors = new AtomicBoolean(false);
2228

2329
private boolean verifyClasses;
2430

@@ -33,6 +39,10 @@ public void enableClassVerification() {
3339
this.verifyClasses = true;
3440
}
3541

42+
public boolean hadErrors() {
43+
return hadErrors.get();
44+
}
45+
3646
@Override
3747
public byte[] transform(
3848
ClassLoader loader,
@@ -42,13 +52,19 @@ public byte[] transform(
4252
byte[] classfileBuffer
4353
) {
4454
if (classesToTransform.contains(className)) {
45-
// System.out.println("Transforming " + className);
46-
return instrumenter.instrumentClass(className, classfileBuffer, verifyClasses);
55+
logger.debug("Transforming " + className);
56+
try {
57+
return instrumenter.instrumentClass(className, classfileBuffer, verifyClasses);
58+
} catch (Throwable t) {
59+
hadErrors.set(true);
60+
logger.error("Failed to instrument class " + className, t);
61+
// throwing an exception from a transformer results in the exception being swallowed,
62+
// effectively the same as returning null anyways, so we instead log it here completely
63+
return null;
64+
}
4765
} else {
48-
// System.out.println("Not transforming " + className);
66+
logger.trace("Not transforming " + className);
4967
return null;
5068
}
5169
}
52-
53-
// private static final Logger LOGGER = LogManager.getLogger(Transformer.class);
5470
}

0 commit comments

Comments
 (0)