Skip to content

Commit bea690a

Browse files
committed
Fix APR crash on shutdown
1 parent 9253cbb commit bea690a

File tree

3 files changed

+61
-14
lines changed

3 files changed

+61
-14
lines changed

java/org/apache/catalina/core/AprLifecycleListener.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.lang.reflect.Method;
2020
import java.util.ArrayList;
2121
import java.util.List;
22+
import java.util.concurrent.locks.Lock;
2223

2324
import org.apache.catalina.Lifecycle;
2425
import org.apache.catalina.LifecycleEvent;
@@ -99,18 +100,20 @@ public class AprLifecycleListener implements LifecycleListener {
99100

100101
private static final int FIPS_OFF = 0;
101102

102-
protected static final Object lock = new Object();
103-
104-
// Guarded by lock
103+
// Guarded APRStatus.getStatusLock()
105104
private static int referenceCount = 0;
106105
private boolean instanceInitialized = false;
107106

108107

109108
public static boolean isAprAvailable() {
110109
// https://bz.apache.org/bugzilla/show_bug.cgi?id=48613
111110
if (org.apache.tomcat.jni.AprStatus.isInstanceCreated()) {
112-
synchronized (lock) {
111+
Lock writeLock = org.apache.tomcat.jni.AprStatus.getStatusLock().writeLock();
112+
writeLock.lock();
113+
try {
113114
init();
115+
} finally {
116+
writeLock.unlock();
114117
}
115118
}
116119
return org.apache.tomcat.jni.AprStatus.isAprAvailable();
@@ -131,7 +134,9 @@ public AprLifecycleListener() {
131134
public void lifecycleEvent(LifecycleEvent event) {
132135

133136
if (Lifecycle.BEFORE_INIT_EVENT.equals(event.getType())) {
134-
synchronized (lock) {
137+
Lock writeLock = org.apache.tomcat.jni.AprStatus.getStatusLock().writeLock();
138+
writeLock.lock();
139+
try {
135140
instanceInitialized = true;
136141
if (!(event.getLifecycle() instanceof Server)) {
137142
log.warn(sm.getString("listener.notServer", event.getLifecycle().getClass().getSimpleName()));
@@ -161,9 +166,13 @@ public void lifecycleEvent(LifecycleEvent event) {
161166
log.fatal(e.getMessage(), e);
162167
throw e;
163168
}
169+
} finally {
170+
writeLock.unlock();
164171
}
165172
} else if (Lifecycle.AFTER_DESTROY_EVENT.equals(event.getType())) {
166-
synchronized (lock) {
173+
Lock writeLock = org.apache.tomcat.jni.AprStatus.getStatusLock().writeLock();
174+
writeLock.lock();
175+
try {
167176
// Instance may get destroyed without ever being initialized
168177
if (instanceInitialized) {
169178
referenceCount--;
@@ -182,6 +191,8 @@ public void lifecycleEvent(LifecycleEvent event) {
182191
ExceptionUtils.handleThrowable(throwable);
183192
log.warn(sm.getString("aprListener.aprDestroy"), throwable);
184193
}
194+
} finally {
195+
writeLock.unlock();
185196
}
186197
}
187198

java/org/apache/tomcat/jni/AprStatus.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
*/
1717
package org.apache.tomcat.jni;
1818

19+
import java.util.concurrent.locks.ReentrantReadWriteLock;
20+
1921
/**
2022
* Holds APR status without the need to load other classes.
2123
*/
@@ -25,6 +27,7 @@ public class AprStatus {
2527
private static volatile boolean useOpenSSL = true;
2628
private static volatile boolean instanceCreated = false;
2729
private static volatile int openSSLVersion = 0;
30+
private static ReentrantReadWriteLock statusLock = new ReentrantReadWriteLock();
2831

2932
public static boolean isAprInitialized() {
3033
return aprInitialized;
@@ -71,4 +74,16 @@ public static int getOpenSSLVersion() {
7174
public static void setOpenSSLVersion(int openSSLVersion) {
7275
AprStatus.openSSLVersion = openSSLVersion;
7376
}
77+
78+
/**
79+
* Code that changes the status of the APR library MUST hold the write lock while making any changes.
80+
* <p>
81+
* Code that needs the status to be consistent for an operation must hold the read lock for the duration of that
82+
* operation.
83+
*
84+
* @return The read/write lock for APR library status
85+
*/
86+
public static ReentrantReadWriteLock getStatusLock() {
87+
return statusLock;
88+
}
7489
}

java/org/apache/tomcat/util/net/openssl/OpenSSLContext.java

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.Base64;
3434
import java.util.Iterator;
3535
import java.util.List;
36+
import java.util.concurrent.locks.Lock;
3637

3738
import javax.net.ssl.KeyManager;
3839
import javax.net.ssl.SSLEngine;
@@ -46,6 +47,7 @@
4647

4748
import org.apache.juli.logging.Log;
4849
import org.apache.juli.logging.LogFactory;
50+
import org.apache.tomcat.jni.AprStatus;
4951
import org.apache.tomcat.jni.Pool;
5052
import org.apache.tomcat.jni.SSL;
5153
import org.apache.tomcat.jni.SSLConf;
@@ -592,14 +594,33 @@ public X509Certificate[] getAcceptedIssuers() {
592594
private record OpenSSLState(long aprPool, long cctx, long ctx) implements Runnable {
593595
@Override
594596
public void run() {
595-
if (ctx != 0) {
596-
SSLContext.free(ctx);
597-
}
598-
if (cctx != 0) {
599-
SSLConf.free(cctx);
600-
}
601-
if (aprPool != 0) {
602-
Pool.destroy(aprPool);
597+
/*
598+
* During shutdown there is a possibility that both the cleaner and the APR library termination code try and
599+
* free these resources. If both call free, there will be a JVM crash.
600+
*
601+
* If the cleaner frees the resources, the APR library termination won't try free them as well.
602+
*
603+
* If the APR library termination frees the resources, the cleaner MUST NOT attempt to do so.
604+
*
605+
* The locks and checks below ensure that a) the cleaner only runs if the APR library has not yet been
606+
* terminated and that the APR library status will not change while the cleaner is running.
607+
*/
608+
Lock readLock = AprStatus.getStatusLock().readLock();
609+
readLock.lock();
610+
try {
611+
if (AprStatus.isAprInitialized()) {
612+
if (ctx != 0) {
613+
SSLContext.free(ctx);
614+
}
615+
if (cctx != 0) {
616+
SSLConf.free(cctx);
617+
}
618+
if (aprPool != 0) {
619+
Pool.destroy(aprPool);
620+
}
621+
}
622+
} finally {
623+
readLock.unlock();
603624
}
604625
}
605626
}

0 commit comments

Comments
 (0)