8234466: Class loading deadlock involving X509Factory#commitEvent()

Reviewed-by: alanb, chegar, dfuchs
This commit is contained in:
Sean Coffey 2020-01-13 21:16:27 +00:00
parent ab90653aa9
commit 2c5167803a
8 changed files with 405 additions and 18 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -157,8 +157,10 @@ public class JarFile extends ZipFile {
private boolean jvInitialized;
private boolean verify;
private final Runtime.Version version; // current version
private final int versionFeature; // version.feature()
private final int versionFeature; // version.feature()
private boolean isMultiRelease; // is jar multi-release?
static final ThreadLocal<Boolean> isInitializing =
ThreadLocal.withInitial(() -> Boolean.FALSE);
// indicates if Class-Path attribute present
private boolean hasClassPathAttribute;
@ -1031,8 +1033,13 @@ public class JarFile extends ZipFile {
throw new RuntimeException(e);
}
if (jv != null && !jvInitialized) {
initializeVerifier();
jvInitialized = true;
isInitializing.set(Boolean.TRUE);
try {
initializeVerifier();
jvInitialized = true;
} finally {
isInitializing.set(Boolean.FALSE);
}
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -68,4 +68,8 @@ class JavaUtilJarAccessImpl implements JavaUtilJarAccess {
public void ensureInitialization(JarFile jar) {
jar.ensureInitialization();
}
public Boolean isInitializing() {
return JarFile.isInitializing.get();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -45,4 +45,5 @@ public interface JavaUtilJarAccess {
public List<Object> getManifestDigests(JarFile jar);
public Attributes getTrustedAttributes(Manifest man, String name);
public void ensureInitialization(JarFile jar);
public Boolean isInitializing();
}

View file

@ -25,6 +25,11 @@
package jdk.internal.event;
import jdk.internal.access.JavaUtilJarAccess;
import jdk.internal.access.SharedSecrets;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
@ -37,14 +42,24 @@ import java.util.stream.IntStream;
public final class EventHelper {
private static final JavaUtilJarAccess JUJA = SharedSecrets.javaUtilJarAccess();
private static volatile boolean loggingSecurity;
private static volatile System.Logger securityLogger;
private static final VarHandle LOGGER_HANDLE;
static {
try {
LOGGER_HANDLE =
MethodHandles.lookup().findStaticVarHandle(
EventHelper.class, "securityLogger", System.Logger.class);
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
private static final System.Logger.Level LOG_LEVEL = System.Logger.Level.DEBUG;
// helper class used for logging security related events for now
private static final String SECURITY_LOGGER_NAME = "jdk.event.security";
private static final System.Logger SECURITY_LOGGER =
System.getLogger(SECURITY_LOGGER_NAME);
private static final boolean LOGGING_SECURITY =
SECURITY_LOGGER.isLoggable(LOG_LEVEL);
public static void logTLSHandshakeEvent(Instant start,
String peerHost,
@ -52,8 +67,9 @@ public final class EventHelper {
String cipherSuite,
String protocolVersion,
long peerCertId) {
assert securityLogger != null;
String prepend = getDurationString(start);
SECURITY_LOGGER.log(LOG_LEVEL, prepend +
securityLogger.log(LOG_LEVEL, prepend +
" TLSHandshake: {0}:{1,number,#}, {2}, {3}, {4,number,#}",
peerHost, peerPort, protocolVersion, cipherSuite, peerCertId);
}
@ -61,18 +77,18 @@ public final class EventHelper {
public static void logSecurityPropertyEvent(String key,
String value) {
if (isLoggingSecurity()) {
SECURITY_LOGGER.log(LOG_LEVEL,
"SecurityPropertyModification: key:{0}, value:{1}", key, value);
}
assert securityLogger != null;
securityLogger.log(LOG_LEVEL,
"SecurityPropertyModification: key:{0}, value:{1}", key, value);
}
public static void logX509ValidationEvent(int anchorCertId,
int[] certIds) {
assert securityLogger != null;
String codes = IntStream.of(certIds)
.mapToObj(Integer::toString)
.collect(Collectors.joining(", "));
SECURITY_LOGGER.log(LOG_LEVEL,
securityLogger.log(LOG_LEVEL,
"ValidationChain: {0,number,#}, {1}", anchorCertId, codes);
}
@ -85,7 +101,8 @@ public final class EventHelper {
long certId,
long beginDate,
long endDate) {
SECURITY_LOGGER.log(LOG_LEVEL, "X509Certificate: Alg:{0}, Serial:{1}" +
assert securityLogger != null;
securityLogger.log(LOG_LEVEL, "X509Certificate: Alg:{0}, Serial:{1}" +
", Subject:{2}, Issuer:{3}, Key type:{4}, Length:{5,number,#}" +
", Cert Id:{6,number,#}, Valid from:{7}, Valid until:{8}",
algId, serialNum, subject, issuer, keyType, length,
@ -124,6 +141,14 @@ public final class EventHelper {
* @return boolean indicating whether an event should be logged
*/
public static boolean isLoggingSecurity() {
return LOGGING_SECURITY;
// Avoid a bootstrap issue where the commitEvent attempts to
// trigger early loading of System Logger but where
// the verification process still has JarFiles locked
if (securityLogger == null && !JUJA.isInitializing()) {
LOGGER_HANDLE.compareAndSet( null, System.getLogger(SECURITY_LOGGER_NAME));
loggingSecurity = securityLogger.isLoggable(LOG_LEVEL);
}
return loggingSecurity;
}
}