Skip to content

Commit 965c656

Browse files
authored
Fix security manager issues (#2871)
1 parent 50bb1f1 commit 965c656

File tree

67 files changed

+999
-152
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+999
-152
lines changed

CHANGELOG.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ endif::[]
4949
* Prevent random `NullPointerException` when span compression is not possible - {pull}2859[#2859]
5050
* Fix security manager issues on OpenJDK 17, with errors like: `java.lang.UnsupportedOperationException: Could not access Unsafe class` -
5151
{pull}2874[#2874]
52+
* Fix security manager compatibility with Tomcat - {pull}2871[#2871]
5253
5354
[[release-notes-1.x]]
5455
=== Java Agent version 1.x

apm-agent-core/src/main/java/co/elastic/apm/agent/bbwarmup/WarmupInstrumentation.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import co.elastic.apm.agent.bci.TracerAwareInstrumentation;
2222
import co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers;
2323
import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory;
24+
import co.elastic.apm.agent.util.PrivilegedActionUtils;
2425
import net.bytebuddy.asm.Advice;
2526
import net.bytebuddy.description.method.MethodDescription;
2627
import net.bytebuddy.description.type.TypeDescription;
@@ -45,7 +46,7 @@ public ElementMatcher.Junction<ClassLoader> getClassLoaderMatcher() {
4546
// (caused by java.lang.ClassFormatError) on OpenJDK 7.
4647
// By allowing instrumentation only when the test class is loaded by the same class loader that loads this
4748
// instrumentation class, we avoid this problem and still allow it to work both on production and unit tests
48-
return CustomElementMatchers.isSameClassLoader(getClass().getClassLoader());
49+
return CustomElementMatchers.isSameClassLoader(PrivilegedActionUtils.getClassLoader(getClass()));
4950
}
5051

5152
@Override

apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import co.elastic.apm.agent.tracemethods.TraceMethodInstrumentation;
4545
import co.elastic.apm.agent.util.DependencyInjectingServiceLoader;
4646
import co.elastic.apm.agent.util.ExecutorUtils;
47+
import co.elastic.apm.agent.util.PrivilegedActionUtils;
4748
import net.bytebuddy.ByteBuddy;
4849
import net.bytebuddy.agent.builder.AgentBuilder;
4950
import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy;
@@ -169,7 +170,7 @@ private static void initInstrumentation(ElasticApmTracer tracer, Instrumentation
169170
@Nonnull
170171
private static Iterable<ElasticApmInstrumentation> loadInstrumentations(ElasticApmTracer tracer) {
171172
List<ClassLoader> pluginClassLoaders = new ArrayList<>();
172-
pluginClassLoaders.add(ElasticApmAgent.class.getClassLoader());
173+
pluginClassLoaders.add(PrivilegedActionUtils.getClassLoader(ElasticApmAgent.class));
173174
pluginClassLoaders.addAll(createExternalPluginClassLoaders(tracer.getConfig(CoreConfiguration.class).getPluginsDir()));
174175
final List<ElasticApmInstrumentation> instrumentations = DependencyInjectingServiceLoader.load(ElasticApmInstrumentation.class, pluginClassLoaders, tracer);
175176
for (MethodMatcher traceMethod : tracer.getConfig(CoreConfiguration.class).getTraceMethods()) {
@@ -203,7 +204,7 @@ public boolean accept(File dir, String name) {
203204
for (File pluginJar : pluginJars) {
204205
logger.info("Loading plugin {}", pluginJar.getName());
205206
try {
206-
result.add(new ExternalPluginClassLoader(pluginJar, ElasticApmAgent.class.getClassLoader()));
207+
result.add(new ExternalPluginClassLoader(pluginJar, PrivilegedActionUtils.getClassLoader(ElasticApmAgent.class)));
207208
} catch (Exception e) {
208209
logger.error("Error loading external plugin", e);
209210
}
@@ -255,7 +256,7 @@ private static synchronized void initInstrumentation(final ElasticApmTracer trac
255256
for (ElasticApmInstrumentation apmInstrumentation : instrumentations) {
256257
mapInstrumentationCL2adviceClassName(
257258
apmInstrumentation.getAdviceClassName(),
258-
apmInstrumentation.getClass().getClassLoader());
259+
PrivilegedActionUtils.getClassLoader(apmInstrumentation.getClass()));
259260
}
260261
Runtime.getRuntime().addShutdownHook(new Thread(ThreadUtils.addElasticApmThreadPrefix("init-instrumentation-shutdown-hook")) {
261262
@Override
@@ -485,7 +486,7 @@ public boolean matches(MethodDescription target) {
485486
};
486487
return new AgentBuilder.Transformer.ForAdvice(withCustomMapping)
487488
.advice(instrumentationStats.shouldMeasureMatching() ? statsCollectingMatcher : matcher, instrumentation.getAdviceClassName())
488-
.include(ClassLoader.getSystemClassLoader(), instrumentation.getClass().getClassLoader())
489+
.include(ClassLoader.getSystemClassLoader(), PrivilegedActionUtils.getClassLoader(instrumentation.getClass()))
489490
.withExceptionHandler(PRINTING);
490491
}
491492

@@ -497,7 +498,7 @@ public static void validateAdvice(ElasticApmInstrumentation instrumentation) {
497498
if (instrumentation.getClass().getName().equals(adviceClassName)) {
498499
throw new IllegalStateException("The advice must be declared in a separate class: " + adviceClassName);
499500
}
500-
ClassLoader adviceClassLoader = instrumentation.getClass().getClassLoader();
501+
ClassLoader adviceClassLoader = PrivilegedActionUtils.getClassLoader(instrumentation.getClass());
501502
if (adviceClassLoader == null) {
502503
// the bootstrap class loader can't do resource lookup
503504
// if classes are added via java.lang.instrument.Instrumentation.appendToBootstrapClassLoaderSearch
@@ -590,7 +591,7 @@ public static InstrumentationStats getInstrumentationStats() {
590591

591592
// may help to debug classloading problems
592593
private static void logClassLoaderHierarchy(@Nullable ClassLoader classLoader, Logger logger, ElasticApmInstrumentation advice) {
593-
logger.trace("Advice {} is loaded by {}", advice.getClass().getName(), advice.getClass().getClassLoader());
594+
logger.trace("Advice {} is loaded by {}", advice.getClass().getName(), PrivilegedActionUtils.getClassLoader(advice.getClass()));
594595
if (classLoader != null) {
595596
boolean canLoadAgent = false;
596597
try {
@@ -782,7 +783,7 @@ public static void ensureInstrumented(Class<?> classToInstrument, Collection<Cla
782783
ElasticApmInstrumentation apmInstrumentation = instantiate(instrumentationClass);
783784
mapInstrumentationCL2adviceClassName(
784785
apmInstrumentation.getAdviceClassName(),
785-
instrumentationClass.getClassLoader());
786+
PrivilegedActionUtils.getClassLoader(instrumentationClass));
786787
ElementMatcher.Junction<? super TypeDescription> typeMatcher = getTypeMatcher(classToInstrument, apmInstrumentation.getMethodMatcher(), none());
787788
if (typeMatcher != null && isIncluded(apmInstrumentation, config)) {
788789
agentBuilder = applyAdvice(tracer, agentBuilder, apmInstrumentation, typeMatcher.and(apmInstrumentation.getTypeMatcher()));
@@ -872,7 +873,7 @@ private static ElasticApmInstrumentation tryInstantiate(Class<? extends ElasticA
872873
}
873874

874875
public static ClassLoader getAgentClassLoader() {
875-
ClassLoader agentClassLoader = ElasticApmAgent.class.getClassLoader();
876+
ClassLoader agentClassLoader = PrivilegedActionUtils.getClassLoader(ElasticApmAgent.class);
876877
if (agentClassLoader == null) {
877878
throw new IllegalStateException("Agent is loaded from bootstrap class loader as opposed to the dedicated agent class loader");
878879
}

apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import java.lang.invoke.MethodType;
4444
import java.lang.reflect.Method;
4545
import java.net.URISyntaxException;
46+
import java.security.AccessController;
47+
import java.security.PrivilegedAction;
4648
import java.util.ArrayList;
4749
import java.util.Collection;
4850
import java.util.Collections;
@@ -354,10 +356,25 @@ static void setJavaBaseModule(Class<?> targetClass) throws Throwable {
354356
* @return a {@link ConstantCallSite} that is the target of the invokedynamic
355357
*/
356358
@Nullable
357-
public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup,
358-
String adviceMethodName,
359-
MethodType adviceMethodType,
360-
Object... args) {
359+
public static ConstantCallSite bootstrap(final MethodHandles.Lookup lookup,
360+
final String adviceMethodName,
361+
final MethodType adviceMethodType,
362+
final Object... args) {
363+
364+
if (System.getSecurityManager() == null) {
365+
return internalBootstrap(lookup, adviceMethodName, adviceMethodType, args);
366+
}
367+
368+
// callsite resolution needs privileged access to call Class#getClassLoader() and MethodHandles$Lookup#findStatic
369+
return AccessController.doPrivileged(new PrivilegedAction<ConstantCallSite>() {
370+
@Override
371+
public ConstantCallSite run() {
372+
return internalBootstrap(lookup, adviceMethodName, adviceMethodType, args);
373+
}
374+
});
375+
}
376+
377+
private static ConstantCallSite internalBootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, Object[] args) {
361378
try {
362379
if (callDepth.isNestedCallAndIncrement()) {
363380
// avoid re-entrancy and stack overflow errors

apm-agent-core/src/main/java/co/elastic/apm/agent/bci/classloading/IndyPluginClassLoader.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
*/
1919
package co.elastic.apm.agent.bci.classloading;
2020

21+
import co.elastic.apm.agent.util.PrivilegedActionUtils;
2122
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
23+
import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy;
2224
import net.bytebuddy.matcher.ElementMatcher;
2325
import net.bytebuddy.matcher.ElementMatchers;
2426

@@ -41,9 +43,15 @@ public class IndyPluginClassLoader extends ByteArrayClassLoader.ChildFirst {
4143

4244
public IndyPluginClassLoader(@Nullable ClassLoader targetClassLoader, ClassLoader agentClassLoader, Map<String, byte[]> typeDefinitions) {
4345
// See getResource on why we're using PersistenceHandler.LATENT over PersistenceHandler.MANIFEST
44-
super(getParent(targetClassLoader, agentClassLoader), true, typeDefinitions, PersistenceHandler.LATENT);
46+
super(getParent(targetClassLoader, agentClassLoader),
47+
true,
48+
typeDefinitions,
49+
PrivilegedActionUtils.getProtectionDomain(agentClassLoader.getClass()), // inherit protection domain from agent CL
50+
PersistenceHandler.LATENT,
51+
PackageDefinitionStrategy.Trivial.INSTANCE);
4552
}
4653

54+
4755
private static ClassLoader getParent(@Nullable ClassLoader targetClassLoader, ClassLoader agentClassLoader) {
4856
if (targetClassLoader == null) {
4957
// the MultipleParentClassLoader doesn't support null values

apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServerlessConfiguration.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package co.elastic.apm.agent.configuration;
2020

21+
import co.elastic.apm.agent.util.PrivilegedActionUtils;
2122
import org.stagemonitor.configuration.ConfigurationOption;
2223
import org.stagemonitor.configuration.ConfigurationOptionProvider;
2324

@@ -50,7 +51,8 @@ public long getDataFlushTimeout() {
5051
}
5152

5253
public boolean runsOnAwsLambda() {
53-
String lambdaName = System.getenv("AWS_LAMBDA_FUNCTION_NAME");
54+
String lambdaName = PrivilegedActionUtils.getEnv("AWS_LAMBDA_FUNCTION_NAME");
5455
return null != lambdaName && !lambdaName.isEmpty();
5556
}
57+
5658
}

apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ServiceInfo.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
*/
1919
package co.elastic.apm.agent.configuration;
2020

21+
import co.elastic.apm.agent.util.PrivilegedActionUtils;
22+
2123
import javax.annotation.Nullable;
24+
import java.util.Map;
2225
import java.util.Objects;
2326
import java.util.Properties;
2427
import java.util.jar.Attributes;
@@ -30,7 +33,7 @@ public class ServiceInfo {
3033
private static final String JAR_VERSION_SUFFIX = "-(\\d+\\.)+(\\d+)(.*)?$";
3134
private static final String DEFAULT_SERVICE_NAME = "unknown-java-service";
3235
private static final ServiceInfo EMPTY = new ServiceInfo(null, null);
33-
private static final ServiceInfo AUTO_DETECTED = autoDetect(System.getProperties());
36+
private static final ServiceInfo AUTO_DETECTED = autoDetect(System.getProperties(), PrivilegedActionUtils.getEnv());
3437

3538
private final String serviceName;
3639
@Nullable
@@ -83,12 +86,12 @@ public static ServiceInfo autoDetected() {
8386
return AUTO_DETECTED;
8487
}
8588

86-
public static ServiceInfo autoDetect(Properties properties) {
87-
String lambdaFunctionName = System.getenv("AWS_LAMBDA_FUNCTION_NAME");
89+
public static ServiceInfo autoDetect(Properties sysProperties, Map<String,String> sysEnv) {
90+
String lambdaFunctionName = sysEnv.get("AWS_LAMBDA_FUNCTION_NAME");
8891
if (lambdaFunctionName != null) {
89-
return new ServiceInfo(lambdaFunctionName, System.getenv("AWS_LAMBDA_FUNCTION_VERSION"));
92+
return new ServiceInfo(lambdaFunctionName, sysEnv.get("AWS_LAMBDA_FUNCTION_VERSION"));
9093
} else {
91-
ServiceInfo serviceInfo = createFromSunJavaCommand(properties.getProperty("sun.java.command"));
94+
ServiceInfo serviceInfo = createFromSunJavaCommand(sysProperties.getProperty("sun.java.command"));
9295
if (serviceInfo != null) {
9396
return serviceInfo;
9497
}

apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/source/ConfigSources.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
*/
1919
package co.elastic.apm.agent.configuration.source;
2020

21+
import co.elastic.apm.agent.util.PrivilegedActionUtils;
2122
import org.stagemonitor.configuration.source.SimpleSource;
2223

2324
import javax.annotation.Nullable;
25+
import java.io.File;
2426
import java.io.FileInputStream;
2527
import java.io.FileNotFoundException;
2628
import java.io.IOException;
@@ -72,7 +74,7 @@ static Properties getPropertiesFromFilesystem(String location) {
7274
}
7375

7476
Properties props = new Properties();
75-
try (InputStream input = new FileInputStream(location)) {
77+
try (InputStream input = PrivilegedActionUtils.newFileInputStream(new File(location))) {
7678
props.load(input);
7779
return props;
7880
} catch (FileNotFoundException ex) {

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import co.elastic.apm.agent.impl.transaction.Span;
2727
import co.elastic.apm.agent.impl.transaction.TextHeaderGetter;
2828
import co.elastic.apm.agent.impl.transaction.Transaction;
29+
import co.elastic.apm.agent.util.PrivilegedActionUtils;
2930
import co.elastic.apm.agent.util.VersionUtils;
3031

3132
import javax.annotation.Nullable;
@@ -63,7 +64,7 @@ public static ElasticApmTracer requireTracerImpl() {
6364
}
6465

6566
private static void checkClassloader() {
66-
ClassLoader cl = GlobalTracer.class.getClassLoader();
67+
ClassLoader cl = PrivilegedActionUtils.getClassLoader(GlobalTracer.class);
6768

6869
// agent currently loaded in the bootstrap CL, which is the current correct location
6970
if (cl == null) {
@@ -74,7 +75,7 @@ private static void checkClassloader() {
7475
return;
7576
}
7677

77-
String agentLocation = GlobalTracer.class.getProtectionDomain().getCodeSource().getLocation().getFile();
78+
String agentLocation = PrivilegedActionUtils.getProtectionDomain(GlobalTracer.class).getCodeSource().getLocation().getFile();
7879
if (!agentLocation.endsWith(".jar")) {
7980
// agent is not packaged, thus we assume running tests
8081
classloaderCheckOk = true;

apm-agent-core/src/main/java/co/elastic/apm/agent/impl/metadata/CloudMetadataProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import co.elastic.apm.agent.configuration.CoreConfiguration;
2222
import co.elastic.apm.agent.configuration.ServerlessConfiguration;
2323
import co.elastic.apm.agent.util.ExecutorUtils;
24+
import co.elastic.apm.agent.util.PrivilegedActionUtils;
2425
import co.elastic.apm.agent.util.UrlConnectionUtils;
2526
import com.dslplatform.json.DslJson;
2627
import com.dslplatform.json.JsonReader;
@@ -68,7 +69,7 @@ static CloudProviderInfo getCloudInfoProvider(final CoreConfiguration.CloudProvi
6869

6970
if (serverlessConfiguration.runsOnAwsLambda()) {
7071
CloudProviderInfo awsLambdaInfo = new CloudProviderInfo("aws");
71-
awsLambdaInfo.setRegion(System.getenv("AWS_REGION"));
72+
awsLambdaInfo.setRegion(PrivilegedActionUtils.getEnv("AWS_REGION"));
7273
awsLambdaInfo.setService(new CloudProviderInfo.Service("lambda"));
7374
return awsLambdaInfo;
7475
}

0 commit comments

Comments
 (0)