Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ endif::[]
* Prevent random `NullPointerException` when span compression is not possible - {pull}2859[#2859]
* Fix security manager issues on OpenJDK 17, with errors like: `java.lang.UnsupportedOperationException: Could not access Unsafe class` -
{pull}2874[#2874]
* Fix security manager compatibility with Tomcat - {pull}2871[#2871]

[[release-notes-1.x]]
=== Java Agent version 1.x
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -45,7 +46,7 @@ public ElementMatcher.Junction<ClassLoader> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -169,7 +170,7 @@ private static void initInstrumentation(ElasticApmTracer tracer, Instrumentation
@Nonnull
private static Iterable<ElasticApmInstrumentation> loadInstrumentations(ElasticApmTracer tracer) {
List<ClassLoader> pluginClassLoaders = new ArrayList<>();
pluginClassLoaders.add(ElasticApmAgent.class.getClassLoader());
pluginClassLoaders.add(PrivilegedActionUtils.getClassLoader(ElasticApmAgent.class));
pluginClassLoaders.addAll(createExternalPluginClassLoaders(tracer.getConfig(CoreConfiguration.class).getPluginsDir()));
final List<ElasticApmInstrumentation> instrumentations = DependencyInjectingServiceLoader.load(ElasticApmInstrumentation.class, pluginClassLoaders, tracer);
for (MethodMatcher traceMethod : tracer.getConfig(CoreConfiguration.class).getTraceMethods()) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}

Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -782,7 +783,7 @@ public static void ensureInstrumented(Class<?> classToInstrument, Collection<Cla
ElasticApmInstrumentation apmInstrumentation = instantiate(instrumentationClass);
mapInstrumentationCL2adviceClassName(
apmInstrumentation.getAdviceClassName(),
instrumentationClass.getClassLoader());
PrivilegedActionUtils.getClassLoader(instrumentationClass));
ElementMatcher.Junction<? super TypeDescription> typeMatcher = getTypeMatcher(classToInstrument, apmInstrumentation.getMethodMatcher(), none());
if (typeMatcher != null && isIncluded(apmInstrumentation, config)) {
agentBuilder = applyAdvice(tracer, agentBuilder, apmInstrumentation, typeMatcher.and(apmInstrumentation.getTypeMatcher()));
Expand Down Expand Up @@ -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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -354,10 +356,25 @@ 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) {

if (System.getSecurityManager() == null) {
return internalBootstrap(lookup, adviceMethodName, adviceMethodType, args);
}

// callsite resolution needs privileged access to call Class#getClassLoader() and MethodHandles$Lookup#findStatic
return AccessController.doPrivileged(new PrivilegedAction<ConstantCallSite>() {
@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
*/
package co.elastic.apm.agent.bci.classloading;

import co.elastic.apm.agent.util.PrivilegedActionUtils;
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;

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

public IndyPluginClassLoader(@Nullable ClassLoader targetClassLoader, ClassLoader agentClassLoader, Map<String, byte[]> typeDefinitions) {
// See getResource on why we're using PersistenceHandler.LATENT over PersistenceHandler.MANIFEST
super(getParent(targetClassLoader, agentClassLoader), true, typeDefinitions, PersistenceHandler.LATENT);
super(getParent(targetClassLoader, agentClassLoader),
true,
typeDefinitions,
PrivilegedActionUtils.getProtectionDomain(agentClassLoader.getClass()), // inherit protection domain from agent CL
PersistenceHandler.LATENT,
PackageDefinitionStrategy.Trivial.INSTANCE);
}


private static ClassLoader getParent(@Nullable ClassLoader targetClassLoader, ClassLoader agentClassLoader) {
if (targetClassLoader == null) {
// the MultipleParentClassLoader doesn't support null values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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<String,String> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
*/
package co.elastic.apm.agent.configuration.source;

import co.elastic.apm.agent.util.PrivilegedActionUtils;
import org.stagemonitor.configuration.source.SimpleSource;

import javax.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
Expand Down Expand Up @@ -72,7 +74,7 @@ static Properties getPropertiesFromFilesystem(String location) {
}

Properties props = new Properties();
try (InputStream input = new FileInputStream(location)) {
try (InputStream input = PrivilegedActionUtils.newFileInputStream(new File(location))) {
props.load(input);
return props;
} catch (FileNotFoundException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -74,7 +75,7 @@ private static void checkClassloader() {
return;
}

String agentLocation = GlobalTracer.class.getProtectionDomain().getCodeSource().getLocation().getFile();
String agentLocation = PrivilegedActionUtils.getProtectionDomain(GlobalTracer.class).getCodeSource().getLocation().getFile();
if (!agentLocation.endsWith(".jar")) {
// agent is not packaged, thus we assume running tests
classloaderCheckOk = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ static String getActualLogFile(@Nullable String agentHome, String logFile) {
try {
logFile = new File(logFile).getAbsolutePath();
final File logDir = new File(logFile).getParentFile();

// already privileged, thus will work as expected when security manager is enabled and agent permission set
if (!logDir.exists()) {
logDir.mkdirs();
}
Expand All @@ -103,7 +105,7 @@ static String getActualLogFile(@Nullable String agentHome, String logFile) {
canWrite = false;
}

if(!canWrite){
if (!canWrite) {
System.err.println("[elastic-apm-agent] WARN Log file " + logFile + " is not writable. Falling back to System.out.");
return SYSTEM_OUT;
}
Expand Down Expand Up @@ -174,8 +176,8 @@ private AppenderComponentBuilder createConsoleAppender(ConfigurationBuilder<Buil
private LayoutComponentBuilder createLayout(ConfigurationBuilder<BuiltConfiguration> builder, LogFormat logFormat) {
if (logFormat == LogFormat.PLAIN_TEXT) {
return builder
.newLayout("PatternLayout")
.addAttribute("pattern", "%d [%thread] %-5level %logger{36} - %msg{nolookups}%n");
.newLayout("PatternLayout")
.addAttribute("pattern", "%d [%thread] %-5level %logger{36} - %msg{nolookups}%n");
} else {
String serviceName = getValue(CoreConfiguration.SERVICE_NAME, sources, ServiceInfo.autoDetected().getServiceName());
return builder.newLayout("EcsLayout")
Expand Down
Loading