From 2680dc741be48c34da582d46ca6cfc80500554e4 Mon Sep 17 00:00:00 2001
From: Sylvain Juge
Date: Wed, 16 Nov 2022 15:29:15 +0100
Subject: [PATCH 01/14] make agent work better with security manager
---
.../java/lang/IndyBootstrapDispatcher.esclazz | Bin 2899 -> 2899 bytes
.../elastic/apm/agent/bci/IndyBootstrap.java | 21 +++-
.../ServerlessConfiguration.java | 4 +-
.../apm/agent/configuration/ServiceInfo.java | 13 ++-
.../impl/metadata/CloudMetadataProvider.java | 3 +-
.../agent/impl/metadata/ServiceFactory.java | 5 +-
.../apm/agent/util/PrivilegedActionUtils.java | 49 ++++++++
.../agent/configuration/ServiceInfoTest.java | 4 +-
.../apm/agent/impl/ElasticApmTracerTest.java | 2 +-
.../apm/agent/premain/ShadedClassLoader.java | 105 ++++++++++++++++--
10 files changed, 178 insertions(+), 28 deletions(-)
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/util/PrivilegedActionUtils.java
diff --git a/apm-agent-bootstrap/src/main/resources/bootstrap/java/lang/IndyBootstrapDispatcher.esclazz b/apm-agent-bootstrap/src/main/resources/bootstrap/java/lang/IndyBootstrapDispatcher.esclazz
index 8bdc67e7fd90d23ef5268fe35618d51a7e47dc8f..2601b4c79b55ee9fffc2c0971090b3567e21935d 100644
GIT binary patch
delta 107
zcmcaCc3Eu0MGi*I$(K0(+FCO(GuSY2GT1R_GPpA6FgP>lF}N@oGq^KYFn9n(y%^jW
zyczr$d>FzQd>IlM{27WE0vYNUf*3j(LMHQYtz(Rsyqhap(2zlx!GuAK!H7W|s9S--
Jbh0|P4gkr?6mb9m
delta 107
zcmcaCc3Eu0MGi*w$(K0(+FCL&GgvWjGT1O^GB`8nFgP;kF*q?8Gq^HXFt{<;F?aw;
zPX<2*FNQD%Z-zt$Uxp$Ee}+1S0ESM6pvgR3>lni(@8*gY)MpT8Fk%p6Fkld8Fl10*
KFrKW=tpfnSYZOBO
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
index 69dc2899e3..fb902f597a 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
@@ -43,6 +43,8 @@
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -354,10 +356,21 @@ static void setJavaBaseModule(Class> targetClass) throws Throwable {
* @return a {@link ConstantCallSite} that is the target of the invokedynamic
*/
@Nullable
- public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup,
- String adviceMethodName,
- MethodType adviceMethodType,
- Object... args) {
+ public static ConstantCallSite bootstrap(final MethodHandles.Lookup lookup,
+ final String adviceMethodName,
+ final MethodType adviceMethodType,
+ final Object... args) {
+
+ // callsite resolution needs privileged access to call Class#getClassLoader() and MethodHandles$Lookup#findStatic
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public ConstantCallSite run() {
+ return internalBootstrap(lookup, adviceMethodName, adviceMethodType, args);
+ }
+ });
+ }
+
+ private static ConstantCallSite internalBootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, Object[] args) {
try {
if (callDepth.isNestedCallAndIncrement()) {
// avoid re-entrancy and stack overflow errors
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServerlessConfiguration.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServerlessConfiguration.java
index 1a1b95341f..3b75cc93a6 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServerlessConfiguration.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServerlessConfiguration.java
@@ -18,6 +18,7 @@
*/
package co.elastic.apm.agent.configuration;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import org.stagemonitor.configuration.ConfigurationOption;
import org.stagemonitor.configuration.ConfigurationOptionProvider;
@@ -50,7 +51,8 @@ public long getDataFlushTimeout() {
}
public boolean runsOnAwsLambda() {
- String lambdaName = System.getenv("AWS_LAMBDA_FUNCTION_NAME");
+ String lambdaName = PrivilegedActionUtils.getEnv("AWS_LAMBDA_FUNCTION_NAME");
return null != lambdaName && !lambdaName.isEmpty();
}
+
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceInfo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceInfo.java
index 3f8d594e1d..7d6e36b769 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceInfo.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceInfo.java
@@ -18,7 +18,10 @@
*/
package co.elastic.apm.agent.configuration;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
+
import javax.annotation.Nullable;
+import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.jar.Attributes;
@@ -30,7 +33,7 @@ public class ServiceInfo {
private static final String JAR_VERSION_SUFFIX = "-(\\d+\\.)+(\\d+)(.*)?$";
private static final String DEFAULT_SERVICE_NAME = "unknown-java-service";
private static final ServiceInfo EMPTY = new ServiceInfo(null, null);
- private static final ServiceInfo AUTO_DETECTED = autoDetect(System.getProperties());
+ private static final ServiceInfo AUTO_DETECTED = autoDetect(System.getProperties(), PrivilegedActionUtils.getEnv());
private final String serviceName;
@Nullable
@@ -83,12 +86,12 @@ public static ServiceInfo autoDetected() {
return AUTO_DETECTED;
}
- public static ServiceInfo autoDetect(Properties properties) {
- String lambdaFunctionName = System.getenv("AWS_LAMBDA_FUNCTION_NAME");
+ public static ServiceInfo autoDetect(Properties sysProperties, Map sysEnv) {
+ String lambdaFunctionName = sysEnv.get("AWS_LAMBDA_FUNCTION_NAME");
if (lambdaFunctionName != null) {
- return new ServiceInfo(lambdaFunctionName, System.getenv("AWS_LAMBDA_FUNCTION_VERSION"));
+ return new ServiceInfo(lambdaFunctionName, sysEnv.get("AWS_LAMBDA_FUNCTION_VERSION"));
} else {
- ServiceInfo serviceInfo = createFromSunJavaCommand(properties.getProperty("sun.java.command"));
+ ServiceInfo serviceInfo = createFromSunJavaCommand(sysProperties.getProperty("sun.java.command"));
if (serviceInfo != null) {
return serviceInfo;
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/CloudMetadataProvider.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/CloudMetadataProvider.java
index 4d462cd935..73b56ef497 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/CloudMetadataProvider.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/CloudMetadataProvider.java
@@ -21,6 +21,7 @@
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.configuration.ServerlessConfiguration;
import co.elastic.apm.agent.util.ExecutorUtils;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import co.elastic.apm.agent.util.UrlConnectionUtils;
import com.dslplatform.json.DslJson;
import com.dslplatform.json.JsonReader;
@@ -68,7 +69,7 @@ static CloudProviderInfo getCloudInfoProvider(final CoreConfiguration.CloudProvi
if (serverlessConfiguration.runsOnAwsLambda()) {
CloudProviderInfo awsLambdaInfo = new CloudProviderInfo("aws");
- awsLambdaInfo.setRegion(System.getenv("AWS_REGION"));
+ awsLambdaInfo.setRegion(PrivilegedActionUtils.getEnv("AWS_REGION"));
awsLambdaInfo.setService(new CloudProviderInfo.Service("lambda"));
return awsLambdaInfo;
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/ServiceFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/ServiceFactory.java
index 0523af9f26..97d68d9329 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/ServiceFactory.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/ServiceFactory.java
@@ -20,6 +20,7 @@
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.configuration.ServerlessConfiguration;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import co.elastic.apm.agent.util.VersionUtils;
public class ServiceFactory {
@@ -41,14 +42,14 @@ public Service createService(CoreConfiguration coreConfiguration, String ephemer
}
private void augmentServiceForAWSLambda(Service service) {
- String runtimeName = System.getenv("AWS_EXECUTION_ENV");
+ String runtimeName = PrivilegedActionUtils.getEnv("AWS_EXECUTION_ENV");
runtimeName = null != runtimeName ? runtimeName : "AWS_Lambda_java";
service.withRuntime(new RuntimeInfo(runtimeName, System.getProperty("java.version")));
Node node = service.getNode();
String nodeName = (node != null) ? node.getName() : null;
if (nodeName == null || nodeName.isEmpty()) {
- String serviceNodeName = System.getenv("AWS_LAMBDA_LOG_STREAM_NAME");
+ String serviceNodeName = PrivilegedActionUtils.getEnv("AWS_LAMBDA_LOG_STREAM_NAME");
if (null != serviceNodeName) {
service.withNode(new Node(serviceNodeName));
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PrivilegedActionUtils.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PrivilegedActionUtils.java
new file mode 100644
index 0000000000..da885247df
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PrivilegedActionUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.util;
+
+import javax.annotation.Nullable;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Map;
+
+/**
+ * Delegates calls to {@link System} with wrapping in privileged actions which is required when security manager is active
+ */
+public class PrivilegedActionUtils {
+
+ @Nullable
+ public static String getEnv(final String key) {
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public String run() {
+ return System.getenv(key);
+ }
+ });
+ }
+
+ public static Map getEnv() {
+ return AccessController.doPrivileged(new PrivilegedAction
*
*
- * @param name
- * The binary name of the class
- *
- * @param resolve
- * If {@code true} then resolve the class
- *
- * @return The resulting {@code Class} object
- *
- * @throws ClassNotFoundException
- * If the class could not be found
+ * @param name The binary name of the class
+ * @param resolve If {@code true} then resolve the class
+ * @return The resulting {@code Class} object
+ * @throws ClassNotFoundException If the class could not be found
*/
@Override
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
@@ -161,14 +168,7 @@ private Class> defineClass(String name, byte[] classBytes) {
}
}
- ProtectionDomain protectionDomain = AccessController.doPrivileged(new PrivilegedAction() {
- @Override
- public ProtectionDomain run() {
- return ShadedClassLoader.class.getProtectionDomain();
- }
- });
-
- return defineClass(name, classBytes, 0, classBytes.length, protectionDomain);
+ return defineClass(name, classBytes, 0, classBytes.length, PROTECTION_DOMAIN);
}
@SuppressWarnings("deprecation")
@@ -208,6 +208,10 @@ private byte[] getShadedClassBytes(String name) throws ClassNotFoundException {
}
private InputStream getPrivilegedResourceAsStream(final String name) {
+ if (System.getSecurityManager() == null) {
+ return getResourceAsStreamInternal(name);
+ }
+
return AccessController.doPrivileged(new PrivilegedAction() {
@Override
public InputStream run() {
@@ -223,27 +227,31 @@ private InputStream getResourceAsStreamInternal(String name) {
/**
* This class loader should only see classes and resources that start with the custom prefix.
* It still allows for classes and resources of the parent to be resolved via the getResource methods
+ *
* @param name the name of the resource
* @return a {@code URL} for the resource, or {@code null}
* if the resource could not be found, or if the loader is closed.
*/
@Override
public URL findResource(final String name) {
- URL result = null;
- if (!locallyNonAvailableResources.get().contains(name)) {
+ if (locallyNonAvailableResources.get().contains(name)) {
+ return null;
+ }
- // while most of the body of default 'findResource' in JDK implementation is in a privileged action
- // an extra URL check is performed just after it, hence we have to wrap the whole method call in a privileged
- // action otherwise the security manager will complain about lack of proper read privileges on the agent jar
- result = AccessController.doPrivileged(new PrivilegedAction() {
- @Override
- public URL run() {
- return findResourceInternal(getShadedResourceName(name));
- }
- });
+ if (System.getSecurityManager() == null) {
+ return findResourceInternal(getShadedResourceName(name));
}
- return result;
+ // while most of the body of default 'findResource' in JDK implementation is in a privileged action
+ // an extra URL check is performed just after it, hence we have to wrap the whole method call in a privileged
+ // action otherwise the security manager will complain about lack of proper read privileges on the agent jar
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public URL run() {
+ return findResourceInternal(getShadedResourceName(name));
+ }
+ });
+
}
private URL findResourceInternal(String name) {
@@ -253,16 +261,22 @@ private URL findResourceInternal(String name) {
/**
* This class loader should only see classes and resources that start with the custom prefix.
* It still allows for classes and resources of the parent to be resolved via the getResource methods
+ *
* @param name the name of the resource
* @return a {@code URL} for the resource, or {@code null}
* if the resource could not be found, or if the loader is closed.
*/
@Override
public Enumeration findResources(final String name) throws IOException {
- Enumeration result = null;
- if (!locallyNonAvailableResources.get().contains(name)) {
- result = super.findResources(getShadedResourceName(name));
+ if (locallyNonAvailableResources.get().contains(name)) {
+ return Collections.emptyEnumeration();
+ }
+
+ Enumeration result = super.findResources(getShadedResourceName(name));
+ if (System.getSecurityManager() == null) {
+ return result;
}
+
return new PrivilegedEnumeration(result);
}
@@ -341,18 +355,14 @@ private String getShadedResourceName(String name) {
*/
private static class PrivilegedEnumeration implements Enumeration {
- @Nullable
private final Enumeration delegate;
- private PrivilegedEnumeration(@Nullable Enumeration delegate){
+ private PrivilegedEnumeration(Enumeration delegate) {
this.delegate = delegate;
}
@Override
public boolean hasMoreElements() {
- if (delegate == null) {
- return false;
- }
return AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Boolean run() {
@@ -363,9 +373,6 @@ public Boolean run() {
@Override
public E nextElement() {
- if (delegate == null) {
- throw new NoSuchElementException();
- }
return AccessController.doPrivileged(new PrivilegedAction() {
@Override
public E run() {
From e7dda8d7a9e7104002a41a19cf60649c0458dfa8 Mon Sep 17 00:00:00 2001
From: Sylvain Juge
Date: Fri, 18 Nov 2022 16:19:45 +0100
Subject: [PATCH 05/14] wrap most sensitive method calls
---
.../agent/bbwarmup/WarmupInstrumentation.java | 3 +-
.../apm/agent/bci/ElasticApmAgent.java | 17 +++---
.../elastic/apm/agent/bci/IndyBootstrap.java | 3 +-
.../elastic/apm/agent/impl/GlobalTracer.java | 3 +-
.../apm/agent/report/ApmServerReporter.java | 3 +-
.../TraceMethodInstrumentation.java | 3 +-
.../apm/agent/util/ClassLoaderUtils.java | 2 +-
.../DependencyInjectingServiceLoader.java | 2 +-
.../elastic/apm/agent/util/ExecutorUtils.java | 12 ++--
.../apm/agent/util/PrivilegedActionUtils.java | 57 +++++++++++++++++++
.../apm/agent/sdk/DynamicTransformer.java | 8 +--
.../apm/agent/sdk/internal/InternalUtil.java | 53 +++++++++++++++++
.../apm/agent/sdk/internal/package-info.java | 22 +++++++
.../apm/agent/sdk/logging/package-info.java | 22 +++++++
.../apm/agent/sdk/state/GlobalVariables.java | 4 +-
.../sdk/weakconcurrent/WeakConcurrent.java | 7 +--
.../CaptureExceptionInstrumentation.java | 3 +-
.../CaptureTransactionInstrumentation.java | 3 +-
.../ElasticApmApiInstrumentation.java | 12 ++--
.../pluginapi/TracedInstrumentation.java | 3 +-
.../AbstractSQSInstrumentationHelper.java | 3 +-
.../APIGatewayProxyV1TransactionHelper.java | 3 +-
.../APIGatewayProxyV2TransactionHelper.java | 3 +-
...AbstractMessageBasedTransactionHelper.java | 3 +-
.../helper/PlainTransactionHelper.java | 3 +-
.../awslambda/helper/S3TransactionHelper.java | 3 +-
.../advice/AlibabaMonitorFilterAdvice.java | 3 +-
.../advice/ApacheMonitorFilterAdvice.java | 3 +-
.../ServerCallHandlerInstrumentation.java | 3 +-
.../agent/jms/JmsInstrumentationHelper.java | 3 +-
.../JmsMessageConsumerInstrumentation.java | 3 +-
.../ConsumerRecordsIteratorWrapper.java | 3 +-
...ringKafkaBatchListenerInstrumentation.java | 3 +-
.../loginstr/error/LoggerErrorHelper.java | 3 +-
.../MicrometerMeterRegistrySerializer.java | 7 ++-
.../opentelemetry/sdk/OTelSpanBuilder.java | 5 +-
.../ApmSpanBuilderInstrumentation.java | 3 +-
...ractJobTransactionNameInstrumentation.java | 3 +-
.../rabbitmq/ConsumerInstrumentation.java | 3 +-
...qpBatchMessageListenerInstrumentation.java | 3 +-
.../rabbitmq/SpringAmqpTransactionHelper.java | 3 +-
...heduledTransactionNameInstrumentation.java | 3 +-
.../scheduled/TimerTaskInstrumentation.java | 3 +-
.../agent/springwebflux/WebfluxHelper.java | 3 +-
.../agent/vertx/AbstractVertxWebHelper.java | 3 +-
45 files changed, 257 insertions(+), 66 deletions(-)
create mode 100644 apm-agent-plugin-sdk/src/main/java/co/elastic/apm/agent/sdk/internal/InternalUtil.java
create mode 100644 apm-agent-plugin-sdk/src/main/java/co/elastic/apm/agent/sdk/internal/package-info.java
create mode 100644 apm-agent-plugin-sdk/src/main/java/co/elastic/apm/agent/sdk/logging/package-info.java
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bbwarmup/WarmupInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bbwarmup/WarmupInstrumentation.java
index c2b92094b7..39c76e0d49 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bbwarmup/WarmupInstrumentation.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bbwarmup/WarmupInstrumentation.java
@@ -21,6 +21,7 @@
import co.elastic.apm.agent.bci.TracerAwareInstrumentation;
import co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers;
import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
@@ -45,7 +46,7 @@ public ElementMatcher.Junction getClassLoaderMatcher() {
// (caused by java.lang.ClassFormatError) on OpenJDK 7.
// By allowing instrumentation only when the test class is loaded by the same class loader that loads this
// instrumentation class, we avoid this problem and still allow it to work both on production and unit tests
- return CustomElementMatchers.isSameClassLoader(getClass().getClassLoader());
+ return CustomElementMatchers.isSameClassLoader(PrivilegedActionUtils.getClassLoader(getClass()));
}
@Override
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java
index 5ffcfc4492..e00d07063d 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java
@@ -44,6 +44,7 @@
import co.elastic.apm.agent.tracemethods.TraceMethodInstrumentation;
import co.elastic.apm.agent.util.DependencyInjectingServiceLoader;
import co.elastic.apm.agent.util.ExecutorUtils;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy;
@@ -169,7 +170,7 @@ private static void initInstrumentation(ElasticApmTracer tracer, Instrumentation
@Nonnull
private static Iterable loadInstrumentations(ElasticApmTracer tracer) {
List pluginClassLoaders = new ArrayList<>();
- pluginClassLoaders.add(ElasticApmAgent.class.getClassLoader());
+ pluginClassLoaders.add(PrivilegedActionUtils.getClassLoader(ElasticApmAgent.class));
pluginClassLoaders.addAll(createExternalPluginClassLoaders(tracer.getConfig(CoreConfiguration.class).getPluginsDir()));
final List instrumentations = DependencyInjectingServiceLoader.load(ElasticApmInstrumentation.class, pluginClassLoaders, tracer);
for (MethodMatcher traceMethod : tracer.getConfig(CoreConfiguration.class).getTraceMethods()) {
@@ -203,7 +204,7 @@ public boolean accept(File dir, String name) {
for (File pluginJar : pluginJars) {
logger.info("Loading plugin {}", pluginJar.getName());
try {
- result.add(new ExternalPluginClassLoader(pluginJar, ElasticApmAgent.class.getClassLoader()));
+ result.add(new ExternalPluginClassLoader(pluginJar, PrivilegedActionUtils.getClassLoader(ElasticApmAgent.class)));
} catch (Exception e) {
logger.error("Error loading external plugin", e);
}
@@ -255,7 +256,7 @@ private static synchronized void initInstrumentation(final ElasticApmTracer trac
for (ElasticApmInstrumentation apmInstrumentation : instrumentations) {
mapInstrumentationCL2adviceClassName(
apmInstrumentation.getAdviceClassName(),
- apmInstrumentation.getClass().getClassLoader());
+ PrivilegedActionUtils.getClassLoader(apmInstrumentation.getClass()));
}
Runtime.getRuntime().addShutdownHook(new Thread(ThreadUtils.addElasticApmThreadPrefix("init-instrumentation-shutdown-hook")) {
@Override
@@ -485,7 +486,7 @@ public boolean matches(MethodDescription target) {
};
return new AgentBuilder.Transformer.ForAdvice(withCustomMapping)
.advice(instrumentationStats.shouldMeasureMatching() ? statsCollectingMatcher : matcher, instrumentation.getAdviceClassName())
- .include(ClassLoader.getSystemClassLoader(), instrumentation.getClass().getClassLoader())
+ .include(ClassLoader.getSystemClassLoader(), PrivilegedActionUtils.getClassLoader(instrumentation.getClass()))
.withExceptionHandler(PRINTING);
}
@@ -497,7 +498,7 @@ public static void validateAdvice(ElasticApmInstrumentation instrumentation) {
if (instrumentation.getClass().getName().equals(adviceClassName)) {
throw new IllegalStateException("The advice must be declared in a separate class: " + adviceClassName);
}
- ClassLoader adviceClassLoader = instrumentation.getClass().getClassLoader();
+ ClassLoader adviceClassLoader = PrivilegedActionUtils.getClassLoader(instrumentation.getClass());
if (adviceClassLoader == null) {
// the bootstrap class loader can't do resource lookup
// if classes are added via java.lang.instrument.Instrumentation.appendToBootstrapClassLoaderSearch
@@ -590,7 +591,7 @@ public static InstrumentationStats getInstrumentationStats() {
// may help to debug classloading problems
private static void logClassLoaderHierarchy(@Nullable ClassLoader classLoader, Logger logger, ElasticApmInstrumentation advice) {
- logger.trace("Advice {} is loaded by {}", advice.getClass().getName(), advice.getClass().getClassLoader());
+ logger.trace("Advice {} is loaded by {}", advice.getClass().getName(), PrivilegedActionUtils.getClassLoader(advice.getClass()));
if (classLoader != null) {
boolean canLoadAgent = false;
try {
@@ -782,7 +783,7 @@ public static void ensureInstrumented(Class> classToInstrument, Collection typeMatcher = getTypeMatcher(classToInstrument, apmInstrumentation.getMethodMatcher(), none());
if (typeMatcher != null && isIncluded(apmInstrumentation, config)) {
agentBuilder = applyAdvice(tracer, agentBuilder, apmInstrumentation, typeMatcher.and(apmInstrumentation.getTypeMatcher()));
@@ -872,7 +873,7 @@ private static ElasticApmInstrumentation tryInstantiate(Class extends ElasticA
}
public static ClassLoader getAgentClassLoader() {
- ClassLoader agentClassLoader = ElasticApmAgent.class.getClassLoader();
+ ClassLoader agentClassLoader = PrivilegedActionUtils.getClassLoader(ElasticApmAgent.class);
if (agentClassLoader == null) {
throw new IllegalStateException("Agent is loaded from bootstrap class loader as opposed to the dedicated agent class loader");
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
index 1924591b1f..39de857d1b 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
@@ -27,6 +27,7 @@
import co.elastic.apm.agent.sdk.state.CallDepth;
import co.elastic.apm.agent.sdk.state.GlobalState;
import co.elastic.apm.agent.util.PackageScanner;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.loading.ClassInjector;
@@ -391,7 +392,7 @@ private static ConstantCallSite internalBootstrap(MethodHandles.Lookup lookup, S
MethodHandle instrumentedMethod = args.length >= 5 ? (MethodHandle) args[4] : null;
ClassLoader instrumentationClassLoader = ElasticApmAgent.getInstrumentationClassLoader(adviceClassName);
- ClassLoader targetClassLoader = lookup.lookupClass().getClassLoader();
+ ClassLoader targetClassLoader = PrivilegedActionUtils.getClassLoader(lookup.lookupClass());
ClassFileLocator classFileLocator;
List pluginClasses = new ArrayList<>();
if (instrumentationClassLoader instanceof ExternalPluginClassLoader) {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java
index 927b9c2655..a9b27794a8 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java
@@ -26,6 +26,7 @@
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TextHeaderGetter;
import co.elastic.apm.agent.impl.transaction.Transaction;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import co.elastic.apm.agent.util.VersionUtils;
import javax.annotation.Nullable;
@@ -63,7 +64,7 @@ public static ElasticApmTracer requireTracerImpl() {
}
private static void checkClassloader() {
- ClassLoader cl = GlobalTracer.class.getClassLoader();
+ ClassLoader cl = PrivilegedActionUtils.getClassLoader(GlobalTracer.class);
// agent currently loaded in the bootstrap CL, which is the current correct location
if (cl == null) {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerReporter.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerReporter.java
index a80b8b3357..a7f3d7f3bc 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerReporter.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerReporter.java
@@ -24,6 +24,7 @@
import co.elastic.apm.agent.report.disruptor.ExponentionallyIncreasingSleepingWaitStrategy;
import co.elastic.apm.agent.util.MathUtils;
import co.elastic.apm.agent.common.ThreadUtils;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import com.dslplatform.json.JsonWriter;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventTranslator;
@@ -116,7 +117,7 @@ public ApmServerReporter(boolean dropTransactionIfQueueFull, ReporterConfigurati
disruptor = new Disruptor<>(new TransactionEventFactory(), MathUtils.getNextPowerOf2(reporterConfiguration.getMaxQueueSize()), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
- Thread thread = new Thread(r);
+ Thread thread = PrivilegedActionUtils.newThread(r);
thread.setDaemon(true);
thread.setName(ThreadUtils.addElasticApmThreadPrefix("server-reporter"));
return thread;
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/tracemethods/TraceMethodInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/tracemethods/TraceMethodInstrumentation.java
index 5b018560a5..9067f47f6d 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/tracemethods/TraceMethodInstrumentation.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/tracemethods/TraceMethodInstrumentation.java
@@ -27,6 +27,7 @@
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.matcher.MethodMatcher;
import co.elastic.apm.agent.matcher.WildcardMatcher;
+import co.elastic.apm.agent.util.PrivilegedActionUtils;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
@@ -139,7 +140,7 @@ public static Object onMethodEnter(@Advice.Origin Class> clazz,
AbstractSpan> span = null;
final AbstractSpan> parent = tracer.getActive();
if (parent == null) {
- span = tracer.startRootTransaction(clazz.getClassLoader());
+ span = tracer.startRootTransaction(PrivilegedActionUtils.getClassLoader(clazz));
if (span != null) {
span.withName(signature).activate();
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ClassLoaderUtils.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ClassLoaderUtils.java
index b10c09d78a..37df1d8b8d 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ClassLoaderUtils.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ClassLoaderUtils.java
@@ -27,7 +27,7 @@ public class ClassLoaderUtils {
public static boolean isAgentClassLoader(@Nullable ClassLoader classLoader) {
return (classLoader != null && classLoader.getClass().getName().startsWith("co.elastic.apm")) ||
// This one also covers unit tests, where the app class loader loads the agent
- ClassLoaderUtils.class.getClassLoader().equals(classLoader);
+ PrivilegedActionUtils.getClassLoader(ClassLoaderUtils.class).equals(classLoader);
}
public static boolean isBootstrapClassLoader(@Nullable ClassLoader classLoader) {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/DependencyInjectingServiceLoader.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/DependencyInjectingServiceLoader.java
index 1a4d38eec9..1362a79213 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/DependencyInjectingServiceLoader.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/DependencyInjectingServiceLoader.java
@@ -50,7 +50,7 @@ public class DependencyInjectingServiceLoader {
private final Set resourcePathCache;
private DependencyInjectingServiceLoader(Class clazz, Object... constructorArguments) {
- this(clazz, Collections.singletonList(clazz.getClassLoader()), constructorArguments);
+ this(clazz, Collections.singletonList(PrivilegedActionUtils.getClassLoader(clazz)), constructorArguments);
}
private DependencyInjectingServiceLoader(Class clazz, List classLoaders, Object... constructorArguments) {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ExecutorUtils.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ExecutorUtils.java
index 93d93516a2..b92919ae6a 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ExecutorUtils.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/ExecutorUtils.java
@@ -85,11 +85,11 @@ public SingleNamedThreadFactory(String threadName) {
@Override
public Thread newThread(Runnable r) {
- Thread thread = new Thread(r);
+ Thread thread = PrivilegedActionUtils.newThread(r);
thread.setDaemon(true);
thread.setName(threadName);
- ClassLoader originalContextCL = thread.getContextClassLoader();
- thread.setContextClassLoader(ExecutorUtils.class.getClassLoader());
+ ClassLoader originalContextCL = PrivilegedActionUtils.getContextClassLoader(thread);
+ PrivilegedActionUtils.setContextClassLoader(thread, PrivilegedActionUtils.getClassLoader(ExecutorUtils.class));
logThreadCreation(originalContextCL, threadName);
return thread;
}
@@ -116,12 +116,12 @@ public NamedThreadFactory(String threadPurpose) {
@Override
public Thread newThread(Runnable r) {
- Thread thread = new Thread(r);
+ Thread thread = PrivilegedActionUtils.newThread(r);
thread.setDaemon(true);
String threadName = ThreadUtils.addElasticApmThreadPrefix(threadPurpose) + "-" + threadCounter.getAndIncrement();
thread.setName(threadName);
- ClassLoader originalContextCL = thread.getContextClassLoader();
- thread.setContextClassLoader(ExecutorUtils.class.getClassLoader());
+ ClassLoader originalContextCL = PrivilegedActionUtils.getContextClassLoader(thread);
+ PrivilegedActionUtils.setContextClassLoader(thread, PrivilegedActionUtils.getClassLoader(ExecutorUtils.class));
logThreadCreation(originalContextCL, threadName);
return thread;
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PrivilegedActionUtils.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PrivilegedActionUtils.java
index d866185604..5a6d0f77b8 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PrivilegedActionUtils.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/util/PrivilegedActionUtils.java
@@ -54,4 +54,61 @@ public Map run() {
}
});
}
+
+ @Nullable
+ public static ClassLoader getClassLoader(final Class> type) {
+ if (System.getSecurityManager() == null) {
+ return type.getClassLoader();
+ }
+
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public ClassLoader run() {
+ return type.getClassLoader();
+ }
+ });
+ }
+
+ public static ClassLoader getContextClassLoader(final Thread t){
+ if (System.getSecurityManager() == null) {
+ return t.getContextClassLoader();
+ }
+
+ return AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public ClassLoader run() {
+ return t.getContextClassLoader();
+ }
+ });
+ }
+
+ public static void setContextClassLoader(final Thread t, final @Nullable ClassLoader cl){
+ if(System.getSecurityManager() == null){
+ t.setContextClassLoader(cl);
+ }
+
+ AccessController.doPrivileged(new PrivilegedAction