This is an automated email from the ASF dual-hosted git repository. remm pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/main by this push: new 714236a Switch again to shared scope for the context 714236a is described below commit 714236af3458f030ec598660fa5fa886cd63dbd0 Author: remm <r...@apache.org> AuthorDate: Mon Dec 20 17:22:58 2021 +0100 Switch again to shared scope for the context Following further feedback from Panama. The problem is the implicit scope isn't explicitly closed on JVM shutdown at the moment. OTOH, this change (once everything is done properly, there were simply too many places when convenient references could be kept, preventing GC) to a shared scope works fine both with GC (like the implicit scope does) and JVM shutdown (the explicit clean call on shutdown does the cleanup there). The engine should be able to keep its implicit scope for optimal safety (GC will likely come soon enough for engines). --- .../util/net/openssl/panama/OpenSSLContext.java | 67 +++++++++++++--------- webapps/docs/changelog.xml | 4 ++ 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/modules/openssl-java17/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java b/modules/openssl-java17/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java index be94914..2755a0d 100644 --- a/modules/openssl-java17/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java +++ b/modules/openssl-java17/src/main/java/org/apache/tomcat/util/net/openssl/panama/OpenSSLContext.java @@ -30,6 +30,8 @@ import java.io.File; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; +import java.lang.ref.Cleaner; +import java.lang.ref.Cleaner.Cleanable; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.SecureRandom; @@ -72,6 +74,8 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { private static final StringManager netSm = StringManager.getManager(AbstractEndpoint.class); private static final StringManager sm = StringManager.getManager(OpenSSLContext.class); + private static final Cleaner cleaner = Cleaner.create(); + private static final String defaultProtocol = "TLS"; private static final int SSL_AIDX_RSA = 0; @@ -167,7 +171,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { } private final ContextState state; - private final ResourceScope contextScope; + private final Cleanable cleanable; private static String[] getCiphers(MemoryAddress sslCtx) { MemoryAddress sk = SSL_CTX_get_ciphers(sslCtx); @@ -198,7 +202,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { this.sslHostConfig = certificate.getSSLHostConfig(); this.certificate = certificate; - contextScope = ResourceScope.newImplicitScope(); + ResourceScope contextScope = ResourceScope.newSharedScope(); MemoryAddress sslCtx = MemoryAddress.NULL; MemoryAddress confCtx = MemoryAddress.NULL; @@ -335,7 +339,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { } catch(Exception e) { throw new SSLException(sm.getString("openssl.errorSSLCtxInit"), e); } finally { - state = new ContextState(sslCtx, confCtx, negotiableProtocolsBytes); + state = new ContextState(contextScope, sslCtx, confCtx, negotiableProtocolsBytes); /* * When an SSLHostConfig is replaced at runtime, it is not possible to * call destroy() on the associated OpenSSLContext since it is likely @@ -347,7 +351,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { * and the implicit scope will ensure that the associated native * resources are cleaned up. */ - contextScope.addCloseAction(state); + cleanable = cleaner.register(this, state); if (!success) { destroy(); @@ -368,6 +372,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { @Override public void destroy() { + cleanable.clean(); } @@ -554,7 +559,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { } // List the ciphers that the client is permitted to negotiate - if (SSL_CTX_set_cipher_list(state.sslCtx, CLinker.toCString(sslHostConfig.getCiphers(), contextScope)) <= 0) { + if (SSL_CTX_set_cipher_list(state.sslCtx, CLinker.toCString(sslHostConfig.getCiphers(), state.contextScope)) <= 0) { log.warn(sm.getString("engine.failedCipherSuite", sslHostConfig.getCiphers())); } @@ -590,18 +595,18 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { // Set int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) callback MemoryAddress openSSLCallbackVerify = CLinker.getInstance().upcallStub(openSSLCallbackVerifyHandle, - openSSLCallbackVerifyFunctionDescriptor, contextScope); + openSSLCallbackVerifyFunctionDescriptor, state.contextScope); // Leave this just in case but in Tomcat this is always set again by the engine SSL_CTX_set_verify(state.sslCtx, value, openSSLCallbackVerify); // Trust and certificate verification - var allocator = SegmentAllocator.ofScope(contextScope); + var allocator = SegmentAllocator.ofScope(state.contextScope); if (tms != null) { // Client certificate verification based on custom trust managers state.x509TrustManager = chooseTrustManager(tms); MemoryAddress openSSLCallbackCertVerify = CLinker.getInstance().upcallStub(openSSLCallbackCertVerifyHandle, - openSSLCallbackCertVerifyFunctionDescriptor, contextScope); + openSSLCallbackCertVerifyFunctionDescriptor, state.contextScope); SSL_CTX_set_cert_verify_callback(state.sslCtx, openSSLCallbackCertVerify, state.sslCtx); // Pass along the DER encoded certificates of the accepted client @@ -627,9 +632,9 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { // SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()), // SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath())); MemorySegment caCertificateFileNative = sslHostConfig.getCaCertificateFile() != null - ? CLinker.toCString(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()), contextScope) : null; + ? CLinker.toCString(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificateFile()), state.contextScope) : null; MemorySegment caCertificatePathNative = sslHostConfig.getCaCertificatePath() != null - ? CLinker.toCString(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath()), contextScope) : null; + ? CLinker.toCString(SSLHostConfig.adjustRelativePath(sslHostConfig.getCaCertificatePath()), state.contextScope) : null; if (SSL_CTX_load_verify_locations(state.sslCtx, caCertificateFileNative == null ? MemoryAddress.NULL : caCertificateFileNative, caCertificatePathNative == null ? MemoryAddress.NULL : caCertificatePathNative) <= 0) { @@ -654,7 +659,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { // No CA certificates configured. Reject all client certificates. MemoryAddress openSSLCallbackCertVerify = CLinker.getInstance().upcallStub(openSSLCallbackCertVerifyHandle, - openSSLCallbackCertVerifyFunctionDescriptor, contextScope); + openSSLCallbackCertVerifyFunctionDescriptor, state.contextScope); SSL_CTX_set_cert_verify_callback(state.sslCtx, openSSLCallbackCertVerify, MemoryAddress.NULL); } @@ -663,7 +668,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { // MemoryAddress in, int inlen, MemoryAddress arg MemoryAddress openSSLCallbackAlpnSelectProto = CLinker.getInstance().upcallStub(openSSLCallbackAlpnSelectProtoHandle, - openSSLCallbackAlpnSelectProtoFunctionDescriptor, contextScope); + openSSLCallbackAlpnSelectProtoFunctionDescriptor, state.contextScope); SSL_CTX_set_alpn_select_cb(state.sslCtx, openSSLCallbackAlpnSelectProto, state.sslCtx); // Skip NPN (annoying and likely not useful anymore) //SSLContext.setNpnProtos(state.ctx, protocolsArray, SSL.SSL_SELECTOR_FAILURE_NO_ADVERTISE); @@ -965,7 +970,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { private void addCertificate(SSLHostConfigCertificate certificate) throws Exception { - var allocator = SegmentAllocator.ofScope(contextScope); + var allocator = SegmentAllocator.ofScope(state.contextScope); int index = getCertificateIndex(certificate); // Load Server key and certificate if (certificate.getCertificateFile() != null) { @@ -974,9 +979,9 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { // SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), // SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()), // certificate.getCertificateKeyPassword(), getCertificateIndex(certificate)); - var certificateFileNative = CLinker.toCString(SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), contextScope); + var certificateFileNative = CLinker.toCString(SSLHostConfig.adjustRelativePath(certificate.getCertificateFile()), state.contextScope); var certificateKeyFileNative = (certificate.getCertificateKeyFile() == null) ? certificateFileNative - : CLinker.toCString(SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()), contextScope); + : CLinker.toCString(SSLHostConfig.adjustRelativePath(certificate.getCertificateKeyFile()), state.contextScope); MemoryAddress bio; MemoryAddress cert = MemoryAddress.NULL; MemoryAddress key = MemoryAddress.NULL; @@ -1000,7 +1005,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { int passwordLength = 0; String callbackPassword = certificate.getCertificateKeyPassword(); if (callbackPassword != null && callbackPassword.length() > 0) { - MemorySegment password = CLinker.toCString(callbackPassword, contextScope); + MemorySegment password = CLinker.toCString(callbackPassword, state.contextScope); passwordAddress = password.address(); passwordLength = (int) (password.byteSize() - 1); } @@ -1104,7 +1109,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { } // Try to read DH parameters from the (first) SSLCertificateFile if (index == SSL_AIDX_RSA) { - bio = BIO_new_file(certificateFileNative, CLinker.toCString("r", contextScope)); + bio = BIO_new_file(certificateFileNative, CLinker.toCString("r", state.contextScope)); var dh = PEM_read_bio_DHparams(bio, MemoryAddress.NULL, MemoryAddress.NULL, MemoryAddress.NULL); BIO_free(bio); // # define SSL_CTX_set_tmp_dh(sslCtx,dh) \ @@ -1115,7 +1120,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { } } // Similarly, try to read the ECDH curve name from SSLCertificateFile... - bio = BIO_new_file(certificateFileNative, CLinker.toCString("r", contextScope)); + bio = BIO_new_file(certificateFileNative, CLinker.toCString("r", state.contextScope)); var ecparams = PEM_read_bio_ECPKParameters(bio, MemoryAddress.NULL, MemoryAddress.NULL, MemoryAddress.NULL); BIO_free(bio); if (!MemoryAddress.NULL.equals(ecparams)) { @@ -1129,12 +1134,12 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { } // Set callback for DH parameters MemoryAddress openSSLCallbackTmpDH = CLinker.getInstance().upcallStub(openSSLCallbackTmpDHHandle, - openSSLCallbackTmpDHFunctionDescriptor, contextScope); + openSSLCallbackTmpDHFunctionDescriptor, state.contextScope); SSL_CTX_set_tmp_dh_callback(state.sslCtx, openSSLCallbackTmpDH); // Set certificate chain file if (certificate.getCertificateChainFile() != null) { var certificateChainFileNative = - CLinker.toCString(SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), contextScope); + CLinker.toCString(SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), state.contextScope); // SSLContext.setCertificateChainFile(state.ctx, // SSLHostConfig.adjustRelativePath(certificate.getCertificateChainFile()), false); if (SSL_CTX_use_certificate_chain_file(state.sslCtx, certificateChainFileNative) <= 0) { @@ -1151,7 +1156,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { if (sslHostConfig.getCertificateRevocationListFile() != null) { MemoryAddress x509Lookup = X509_STORE_add_lookup(certificateStore, X509_LOOKUP_file()); var certificateRevocationListFileNative = - CLinker.toCString(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListFile()), contextScope); + CLinker.toCString(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListFile()), state.contextScope); //X509_LOOKUP_ctrl(lookup,X509_L_FILE_LOAD,file,type,NULL) if (X509_LOOKUP_ctrl(x509Lookup, X509_L_FILE_LOAD(), certificateRevocationListFileNative, X509_FILETYPE_PEM(), MemoryAddress.NULL) <= 0) { @@ -1161,7 +1166,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { if (sslHostConfig.getCertificateRevocationListPath() != null) { MemoryAddress x509Lookup = X509_STORE_add_lookup(certificateStore, X509_LOOKUP_hash_dir()); var certificateRevocationListPathNative = - CLinker.toCString(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListPath()), contextScope); + CLinker.toCString(SSLHostConfig.adjustRelativePath(sslHostConfig.getCertificateRevocationListPath()), state.contextScope); //X509_LOOKUP_ctrl(lookup,X509_L_ADD_DIR,path,type,NULL) if (X509_LOOKUP_ctrl(x509Lookup, X509_L_ADD_DIR(), certificateRevocationListPathNative, X509_FILETYPE_PEM(), MemoryAddress.NULL) <= 0) { @@ -1217,7 +1222,7 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { } // Set callback for DH parameters MemoryAddress openSSLCallbackTmpDH = CLinker.getInstance().upcallStub(openSSLCallbackTmpDHHandle, - openSSLCallbackTmpDHFunctionDescriptor, contextScope); + openSSLCallbackTmpDHFunctionDescriptor, state.contextScope); SSL_CTX_set_tmp_dh_callback(state.sslCtx, openSSLCallbackTmpDH); for (int i = 1; i < chain.length; i++) { //SSLContext.addChainCertificateRaw(state.ctx, chain[i].getEncoded()); @@ -1362,14 +1367,16 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { private static class ContextState implements Runnable { + private final ResourceScope contextScope; private final MemoryAddress sslCtx; private final MemoryAddress confCtx; private final List<byte[]> negotiableProtocols; private X509TrustManager x509TrustManager = null; - private ContextState(MemoryAddress sslCtx, MemoryAddress confCtx, List<byte[]> negotiableProtocols) { + private ContextState(ResourceScope contextScope, MemoryAddress sslCtx, MemoryAddress confCtx, List<byte[]> negotiableProtocols) { states.put(Long.valueOf(sslCtx.toRawLongValue()), this); + this.contextScope = contextScope; this.sslCtx = sslCtx; this.confCtx = confCtx; this.negotiableProtocols = negotiableProtocols; @@ -1377,10 +1384,14 @@ public class OpenSSLContext implements org.apache.tomcat.util.net.SSLContext { @Override public void run() { - states.remove(Long.valueOf(sslCtx.toRawLongValue())); - SSL_CTX_free(sslCtx); - if (!MemoryAddress.NULL.equals(confCtx)) { - SSL_CONF_CTX_free(confCtx); + try { + states.remove(Long.valueOf(sslCtx.toRawLongValue())); + SSL_CTX_free(sslCtx); + if (!MemoryAddress.NULL.equals(confCtx)) { + SSL_CONF_CTX_free(confCtx); + } + } finally { + contextScope.close(); } } } diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 9e3f380..4153ffd 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -129,6 +129,10 @@ Revert the previous fix for <bug>65714</bug> and implement a more comprehensive fix. (markt) </fix> + <fix> + Allow freeing up context on JVM shutdown in the OpenSSL Panama module + by properly using a shared scope. (remm) + </fix> </changelog> </subsection> <subsection name="Jasper"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org