Skip to content

Commit 24a05d4

Browse files
committed
Fix deadlock in RestrictedSecurity mode caused by JAR verification and checkHashValues()
This commit updates isJarVerifierInStackTrace() to inspect all thread stacks, resolving the deadlock that occurred in ProfileParser.checkHashValues(). In the failing scenario, one thread (Thread-1) calls SecureRandom.getInstance(), which triggers checkHashValues() in RestrictedSecurity mode. This method attempts to obtain a MessageDigest from provider OpenJCEPlusFIPS, requiring the corresponding module to be loaded. However, another thread (Thread-2) is already holding the loader lock while performing signature verification for a signed class. Thread-1 is then blocked waiting for that module to load, while Thread-2 waits for the MessageDigest initialization to complete, resulting in a circular deadlock. Previously, isJarVerifierInStackTrace() only checked the current thread’s stack to decide whether to skip check hash. It has now been updated to scan the stack traces of all threads, but except for the VM service threads, allowing it to detect active JAR verification in other non-VM threads and prevent potential cross-thread deadlocks. Signed-off-by: Tao Liu <[email protected]>
1 parent db2cf8e commit 24a05d4

File tree

3 files changed

+20
-15
lines changed

3 files changed

+20
-15
lines changed

closed/src/java.base/share/classes/openj9/internal/security/RestrictedSecurity.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.util.Objects;
4444
import java.util.Properties;
4545
import java.util.Set;
46+
import java.util.concurrent.atomic.AtomicInteger;
4647
import java.util.regex.Matcher;
4748
import java.util.regex.Pattern;
4849
import java.util.stream.Collectors;
@@ -82,6 +83,7 @@ public final class RestrictedSecurity {
8283

8384
private static RestrictedSecurityProperties restricts;
8485

86+
private static final AtomicInteger hashPauseCount = new AtomicInteger(0);
8587
private static final Set<String> unmodifiableProperties = new HashSet<>();
8688

8789
static {
@@ -135,18 +137,6 @@ private RestrictedSecurity() {
135137
super();
136138
}
137139

138-
private static boolean isJarVerifierInStackTrace() {
139-
java.util.function.Predicate<Class<?>> isJarVerifier =
140-
clazz -> "java.util.jar.JarVerifier".equals(clazz.getName())
141-
&& "java.base".equals(clazz.getModule().getName());
142-
143-
java.util.function.Function<Stream<StackWalker.StackFrame>, Boolean> matcher =
144-
stream -> stream.map(StackWalker.StackFrame::getDeclaringClass)
145-
.anyMatch(isJarVerifier);
146-
147-
return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).walk(matcher);
148-
}
149-
150140
/**
151141
* Check loaded profiles' hash values.
152142
*
@@ -164,13 +154,21 @@ private static void checkHashValues(boolean fromProviders) {
164154
if (fromProviders) {
165155
enableCheckHashes = true;
166156
}
167-
if (enableCheckHashes && !isJarVerifierInStackTrace()) {
157+
if (enableCheckHashes && (hashPauseCount.get() == 0)) {
168158
profileParser = null;
169159
parser.checkHashValues();
170160
}
171161
}
172162
}
173163

164+
public static void pauseHashCheck() {
165+
hashPauseCount.incrementAndGet();
166+
}
167+
168+
public static void resumeHashCheck() {
169+
hashPauseCount.decrementAndGet();
170+
}
171+
174172
/**
175173
* Check if restricted security mode is enabled.
176174
*
@@ -254,7 +252,6 @@ public static boolean isServiceAllowed(Service service) {
254252
*/
255253
public static boolean canServiceBeRegistered(Service service) {
256254
if (securityEnabled) {
257-
checkHashValues(false);
258255
return restricts.isRestrictedServiceAllowed(service, false);
259256
}
260257
return true;

src/java.base/share/classes/java/util/jar/JarFile.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,16 @@
2323
* questions.
2424
*/
2525

26+
/*
27+
* ===========================================================================
28+
* (c) Copyright IBM Corp. 2025, 2025 All Rights Reserved
29+
* ===========================================================================
30+
*/
31+
2632
package java.util.jar;
2733

2834
import jdk.internal.access.SharedSecrets;
35+
import openj9.internal.security.RestrictedSecurity;
2936
import jdk.internal.access.JavaUtilZipFileAccess;
3037
import jdk.internal.misc.ThreadTracker;
3138
import sun.security.util.ManifestEntryVerifier;
@@ -1045,10 +1052,12 @@ synchronized void ensureInitialization() {
10451052
}
10461053
if (jv != null && !jvInitialized) {
10471054
Object key = beginInit();
1055+
RestrictedSecurity.pauseHashCheck();
10481056
try {
10491057
initializeVerifier();
10501058
jvInitialized = true;
10511059
} finally {
1060+
RestrictedSecurity.resumeHashCheck();
10521061
endInit(key);
10531062
}
10541063
}

test/jdk/ProblemList-FIPS140_3_OpenJcePlus.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,6 @@ java/security/SecureRandom/Serialize.java https://github.com/eclipse-openj9/open
309309
java/security/SecureRandom/SerializedSeedTest.java https://github.com/eclipse-openj9/openj9/issues/20978 generic-all
310310
java/security/SecureRandom/ThreadSafe.java https://github.com/eclipse-openj9/openj9/issues/20978 generic-all
311311
java/security/Security/CaseInsensitiveAlgNames.java https://github.com/eclipse-openj9/openj9/issues/20978 generic-all
312-
java/security/Security/ClassLoaderDeadlock/Deadlock.java https://github.com/eclipse-openj9/openj9/issues/21919 generic-all
313312
java/security/Security/ConfigFileTest.java https://github.com/eclipse-openj9/openj9/issues/20978 generic-all
314313
java/security/Security/ProviderFiltering.java https://github.com/eclipse-openj9/openj9/issues/20978 generic-all
315314
java/security/Security/removing/RemoveProviderByIdentity.java https://github.com/eclipse-openj9/openj9/issues/20978 generic-all

0 commit comments

Comments
 (0)