Skip to content

Commit c58164b

Browse files
authored
Probe for existence of IBMSASL or ACCP security providers. (#8276)
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 a85ec5f commit c58164b

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)