Disable agent if we may append wrong jar to bootclasspath (DataDog/dd-trace-java#1561)
MODIFY
java-agent/src/main/java/io/opentelemetry/auto/bootstrap/AgentBootstrap.java
java-agent/src/main/java/io/opentelemetry/auto/bootstrap/AgentBootstrap.java
@@ -55,6 +55,7 @@ import java.util.regex.Pattern;
* </ul>
*/
public class AgentBootstrap {
+ private static final Class<?> thisClass = AgentBootstrap.class;
public static void premain(final String agentArgs, final Instrumentation inst) {
agentmain(agentArgs, inst);
@@ -71,24 +72,26 @@ public class AgentBootstrap {
startMethod.invoke(null, inst, bootstrapURL);
} catch (final Throwable ex) {
// Don't rethrow. We don't have a log manager here, so just print.
+ System.err.println("ERROR " + thisClass.getName());
ex.printStackTrace();
}
}
private static synchronized URL installBootstrapJar(final Instrumentation inst)
throws IOException, URISyntaxException {
- URL bootstrapURL = null;
+ URL javaAgentJarURL = null;
// First try Code Source
- final CodeSource codeSource = AgentBootstrap.class.getProtectionDomain().getCodeSource();
+ final CodeSource codeSource = thisClass.getProtectionDomain().getCodeSource();
if (codeSource != null) {
- bootstrapURL = codeSource.getLocation();
- final File bootstrapFile = new File(bootstrapURL.toURI());
+ javaAgentJarURL = codeSource.getLocation();
+ final File bootstrapFile = new File(javaAgentJarURL.toURI());
if (!bootstrapFile.isDirectory()) {
+ checkJarManifestMainClassIsThis(javaAgentJarURL);
inst.appendToBootstrapClassLoaderSearch(new JarFile(bootstrapFile));
- return bootstrapURL;
+ return javaAgentJarURL;
}
}
@@ -129,20 +132,21 @@ public class AgentBootstrap {
if (!(javaagentFile.exists() || javaagentFile.isFile())) {
throw new RuntimeException("Unable to find javaagent file: " + javaagentFile);
}
- bootstrapURL = javaagentFile.toURI().toURL();
+ javaAgentJarURL = javaagentFile.toURI().toURL();
+ checkJarManifestMainClassIsThis(javaAgentJarURL);
inst.appendToBootstrapClassLoaderSearch(new JarFile(javaagentFile));
- return bootstrapURL;
+ return javaAgentJarURL;
}
private static List<String> getVMArgumentsThroughReflection() {
try {
// Try Oracle-based
final Class managementFactoryHelperClass =
- AgentBootstrap.class.getClassLoader().loadClass("sun.management.ManagementFactoryHelper");
+ thisClass.getClassLoader().loadClass("sun.management.ManagementFactoryHelper");
final Class vmManagementClass =
- AgentBootstrap.class.getClassLoader().loadClass("sun.management.VMManagement");
+ thisClass.getClassLoader().loadClass("sun.management.VMManagement");
Object vmManagement;
@@ -161,7 +165,7 @@ public class AgentBootstrap {
} catch (final ReflectiveOperationException e) {
try { // Try IBM-based.
- final Class VMClass = AgentBootstrap.class.getClassLoader().loadClass("com.ibm.oti.vm.VM");
+ final Class VMClass = thisClass.getClassLoader().loadClass("com.ibm.oti.vm.VM");
final String[] argArray = (String[]) VMClass.getMethod("getVMArgs").invoke(null);
return Arrays.asList(argArray);
} catch (final ReflectiveOperationException e1) {
@@ -174,6 +178,27 @@ public class AgentBootstrap {
}
}
+ private static boolean checkJarManifestMainClassIsThis(final URL jarUrl) throws IOException {
+ final URL manifestUrl = new URL("jar:" + jarUrl + "!/META-INF/MANIFEST.MF");
+ final String mainClassLine = "Main-Class: " + thisClass.getCanonicalName();
+ try (final BufferedReader reader =
+ new BufferedReader(
+ new InputStreamReader(manifestUrl.openStream(), StandardCharsets.UTF_8))) {
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.equals(mainClassLine)) {
+ return true;
+ }
+ }
+ }
+ throw new RuntimeException(
+ "opentelemetry-auto is not installed, because class '"
+ + thisClass.getCanonicalName()
+ + "' is located in '"
+ + jarUrl
+ + "'. Make sure you don't have this .class file anywhere, besides opentelemetry-auto.jar");
+ }
+
/**
* Main entry point.
*
@@ -198,7 +223,7 @@ public class AgentBootstrap {
try (final BufferedReader reader =
new BufferedReader(
new InputStreamReader(
- AgentBootstrap.class.getResourceAsStream("/opentelemetry-auto.version"),
+ thisClass.getResourceAsStream("/opentelemetry-auto.version"),
StandardCharsets.UTF_8))) {
for (int c = reader.read(); c != -1; c = reader.read()) {

MODIFY
java-agent/src/test/groovy/io/opentelemetry/auto/AgentLoadedIntoBootstrapTest.groovy
java-agent/src/test/groovy/io/opentelemetry/auto/AgentLoadedIntoBootstrapTest.groovy
*/
package io.opentelemetry.auto
+import io.opentelemetry.auto.bootstrap.AgentBootstrap
import io.opentelemetry.auto.test.IntegrationTestUtils
import jvmbootstraptest.AgentLoadedChecker
+import jvmbootstraptest.MyClassLoaderIsNotBootstrap
import spock.lang.Specification
import spock.lang.Timeout
@@ -31,4 +33,33 @@ class AgentLoadedIntoBootstrapTest extends Specification {
, [:]
, true) == 0
}
+
+ // this tests the case where someone adds the contents of opentelemetry-auto.jar by mistake to
+ // their application's "uber.jar"
+ //
+ // the reason this can cause issues is because we locate the agent jar based on the CodeSource of
+ // the AgentBootstrap class, and then we add that jar file to the bootstrap class path
+ //
+ // but if we find the AgentBootstrap class in an uber jar file, and we add that (whole) uber jar
+ // file to the bootstrap class loader, that can cause some applications to break, as there's a
+ // lot of application and library code that doesn't handle getClassLoader() returning null
+ // (e.g. https://github.com/qos-ch/logback/pull/291)
+ def "application uber jar should not be added to the bootstrap class loader"() {
+ setup:
+ def mainClassName = MyClassLoaderIsNotBootstrap.getName()
+ def pathToJar = IntegrationTestUtils.createJarWithClasses(mainClassName,
+ MyClassLoaderIsNotBootstrap,
+ AgentBootstrap).getPath()
+
+ expect:
+ IntegrationTestUtils.runOnSeparateJvm(mainClassName
+ , "" as String[]
+ , "" as String[]
+ , [:]
+ , pathToJar as String
+ , true) == 0
+
+ cleanup:
+ new File(pathToJar).delete()
+ }
}

MODIFY
java-agent/src/test/java/io/opentelemetry/auto/test/IntegrationTestUtils.java
java-agent/src/test/java/io/opentelemetry/auto/test/IntegrationTestUtils.java
@@ -174,6 +174,18 @@ public class IntegrationTestUtils {
throw new RuntimeException("Agent jar not found");
}
+ public static int runOnSeparateJvm(
+ final String mainClassName,
+ final String[] jvmArgs,
+ final String[] mainMethodArgs,
+ final Map<String, String> envVars,
+ final boolean printOutputStreams)
+ throws Exception {
+ final String classPath = System.getProperty("java.class.path");
+ return runOnSeparateJvm(
+ mainClassName, jvmArgs, mainMethodArgs, envVars, classPath, printOutputStreams);
+ }
+
/**
* On a separate JVM, run the main method for a given class.
*
@@ -187,11 +199,11 @@ public class IntegrationTestUtils {
final String[] jvmArgs,
final String[] mainMethodArgs,
final Map<String, String> envVars,
+ final String classpath,
final boolean printOutputStreams)
throws Exception {
final String separator = System.getProperty("file.separator");
- final String classpath = System.getProperty("java.class.path");
final String path = System.getProperty("java.home") + separator + "bin" + separator + "java";
final List<String> vmArgsList = new ArrayList<>(Arrays.asList(jvmArgs));

ADD
java-agent/src/test/java/jvmbootstraptest/MyClassLoaderIsNotBootstrap.java
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed 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 jvmbootstraptest;
+
+public class MyClassLoaderIsNotBootstrap {
+ public static void main(final String[] args) {
+ if (MyClassLoaderIsNotBootstrap.class.getClassLoader() == null) {
+ throw new RuntimeException("Application level class was loaded by bootstrap classloader");
+ }
+ }
+}

Disable agent if we may append wrong jar to bootclasspath (DataDog/dd-trace-java#1561)