Skip to content

Commit 23d1d39

Browse files
committed
Probe for existence of IBMSASL or ACCP security providers.
If these providers exist in the running JDK then complete registration of other products on a different thread after premain to avoid eagerly loading 'java.util.logging' before main. A longer delay is added when we know a custom logging manager will be installed. Includes a fix for a race condition on IBM Java 8 where resuming Appsec/IAST could affect initial remote discovery.
1 parent 19da739 commit 23d1d39

File tree

2 files changed

+45
-16
lines changed

2 files changed

+45
-16
lines changed

communication/src/main/java/datadog/communication/ddagent/SharedCommunicationObjects.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import datadog.remoteconfig.DefaultConfigurationPoller;
1212
import datadog.trace.api.Config;
1313
import datadog.trace.util.AgentTaskScheduler;
14+
import java.security.Security;
1415
import java.util.ArrayList;
1516
import java.util.List;
1617
import java.util.concurrent.TimeUnit;
@@ -59,6 +60,7 @@ public void createRemaining(Config config) {
5960
}
6061
}
6162

63+
/** Registers a callback to be called when remote communications resume. */
6264
public void whenReady(Runnable callback) {
6365
if (paused) {
6466
synchronized (pausedComponents) {
@@ -71,8 +73,15 @@ public void whenReady(Runnable callback) {
7173
callback.run(); // not paused, run immediately
7274
}
7375

76+
/** Resumes remote communications including any paused callbacks. */
7477
public void resume() {
7578
paused = false;
79+
// attempt discovery first to avoid potential race condition on IBM Java8
80+
if (null != featuresDiscovery) {
81+
featuresDiscovery.discoverIfOutdated();
82+
} else {
83+
Security.getProviders(); // fallback to preloading provider extensions
84+
}
7685
synchronized (pausedComponents) {
7786
for (Runnable callback : pausedComponents) {
7887
try {

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package datadog.trace.bootstrap;
22

33
import static datadog.trace.api.ConfigDefaults.DEFAULT_STARTUP_LOGS_ENABLED;
4-
import static datadog.trace.api.Platform.getRuntimeVendor;
54
import static datadog.trace.api.Platform.isJavaVersionAtLeast;
65
import static datadog.trace.api.Platform.isOracleJDK8;
76
import static datadog.trace.bootstrap.Library.WILDFLY;
@@ -329,7 +328,7 @@ public void run() {
329328
if (appUsingCustomJMXBuilder) {
330329
log.debug("Custom JMX builder detected. Delaying JMXFetch initialization.");
331330
registerMBeanServerBuilderCallback(new StartJmxCallback(jmxStartDelay));
332-
// one minute fail-safe in case nothing touches JMX and and callback isn't triggered
331+
// one minute fail-safe in case nothing touches JMX and callback isn't triggered
333332
scheduleJmxStart(60 + jmxStartDelay);
334333
} else if (appUsingCustomLogManager) {
335334
log.debug("Custom logger detected. Delaying JMXFetch initialization.");
@@ -339,20 +338,31 @@ public void run() {
339338
}
340339
}
341340

342-
boolean delayOkHttp = appUsingCustomLogManager && okHttpMayIndirectlyLoadJUL();
343-
344341
/*
345342
* Similar thing happens with DatadogTracer on (at least) zulu-8 because it uses OkHttp which indirectly loads JFR
346343
* events which in turn loads LogManager. This is not a problem on newer JDKs because there JFR uses different
347344
* logging facility. Likewise on IBM JDKs OkHttp may indirectly load 'IBMSASL' which in turn loads LogManager.
348345
*/
346+
boolean delayOkHttp = !ciVisibilityEnabled && okHttpMayIndirectlyLoadJUL();
347+
boolean waitForJUL = appUsingCustomLogManager && delayOkHttp;
348+
int okHttpDelayMillis;
349+
if (waitForJUL) {
350+
okHttpDelayMillis = 1_000;
351+
} else if (delayOkHttp) {
352+
okHttpDelayMillis = 100;
353+
} else {
354+
okHttpDelayMillis = 0;
355+
}
356+
349357
InstallDatadogTracerCallback installDatadogTracerCallback =
350-
new InstallDatadogTracerCallback(initTelemetry, inst, delayOkHttp);
351-
if (delayOkHttp) {
358+
new InstallDatadogTracerCallback(initTelemetry, inst, okHttpDelayMillis);
359+
if (waitForJUL) {
352360
log.debug("Custom logger detected. Delaying Datadog Tracer initialization.");
353361
registerLogManagerCallback(installDatadogTracerCallback);
362+
} else if (okHttpDelayMillis > 0) {
363+
installDatadogTracerCallback.run(); // complete on different thread (after premain)
354364
} else {
355-
installDatadogTracerCallback.execute();
365+
installDatadogTracerCallback.execute(); // complete on primordial thread in premain
356366
}
357367

358368
/*
@@ -362,7 +372,7 @@ public void run() {
362372
if (profilingEnabled && !isOracleJDK8()) {
363373
StaticEventLogger.begin("Profiling");
364374

365-
if (delayOkHttp) {
375+
if (waitForJUL) {
366376
log.debug("Custom logger detected. Delaying Profiling initialization.");
367377
registerLogManagerCallback(new StartProfilingAgentCallback(inst));
368378
} else {
@@ -499,18 +509,18 @@ protected static class InstallDatadogTracerCallback extends ClassLoadCallBack {
499509
private final Instrumentation instrumentation;
500510
private final Object sco;
501511
private final Class<?> scoClass;
502-
private final boolean delayOkHttp;
512+
private final int okHttpDelayMillis;
503513

504514
public InstallDatadogTracerCallback(
505515
InitializationTelemetry initTelemetry,
506516
Instrumentation instrumentation,
507-
boolean delayOkHttp) {
508-
this.delayOkHttp = delayOkHttp;
517+
int okHttpDelayMillis) {
518+
this.okHttpDelayMillis = okHttpDelayMillis;
509519
this.instrumentation = instrumentation;
510520
try {
511521
scoClass =
512522
AGENT_CLASSLOADER.loadClass("datadog.communication.ddagent.SharedCommunicationObjects");
513-
sco = scoClass.getConstructor(boolean.class).newInstance(delayOkHttp);
523+
sco = scoClass.getConstructor(boolean.class).newInstance(okHttpDelayMillis > 0);
514524
} catch (ClassNotFoundException
515525
| NoSuchMethodException
516526
| InstantiationException
@@ -530,7 +540,7 @@ public AgentThread agentThread() {
530540

531541
@Override
532542
public void execute() {
533-
if (delayOkHttp) {
543+
if (okHttpDelayMillis > 0) {
534544
resumeRemoteComponents();
535545
}
536546

@@ -550,7 +560,7 @@ private void resumeRemoteComponents() {
550560
try {
551561
// remote components were paused for custom log-manager/jmx-builder
552562
// add small delay before resuming remote I/O to help stabilization
553-
Thread.sleep(1_000);
563+
Thread.sleep(okHttpDelayMillis);
554564
scoClass.getMethod("resume").invoke(sco);
555565
} catch (InterruptedException ignore) {
556566
} catch (Throwable e) {
@@ -1339,15 +1349,25 @@ private static String ddGetEnv(final String sysProp) {
13391349
}
13401350

13411351
private static boolean okHttpMayIndirectlyLoadJUL() {
1342-
if ("IBM Corporation".equals(getRuntimeVendor())) {
1343-
return true; // IBM JDKs ship with 'IBMSASL' which will load JUL when OkHttp accesses TLS
1352+
if (isIBMSASLInstalled() || isACCPInstalled()) {
1353+
return true; // 'IBMSASL' and 'ACCP' crypto providers can load JUL when OkHttp accesses TLS
13441354
}
13451355
if (isJavaVersionAtLeast(9)) {
13461356
return false; // JDKs since 9 have reworked JFR to use a different logging facility, not JUL
13471357
}
13481358
return isJFRSupported(); // assume OkHttp will indirectly load JUL via its JFR events
13491359
}
13501360

1361+
private static boolean isIBMSASLInstalled() {
1362+
return ClassLoader.getSystemResource("com/ibm/security/sasl/IBMSASL.class") != null;
1363+
}
1364+
1365+
private static boolean isACCPInstalled() {
1366+
return ClassLoader.getSystemResource(
1367+
"com/amazon/corretto/crypto/provider/AmazonCorrettoCryptoProvider.class")
1368+
!= null;
1369+
}
1370+
13511371
private static boolean isJFRSupported() {
13521372
// FIXME: this is quite a hack because there maybe jfr classes on classpath somehow that have
13531373
// nothing to do with JDK - but this should be safe because only thing this does is to delay

0 commit comments

Comments
 (0)