This is an automated email from the ASF dual-hosted git repository.

markt-asf pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/11.0.x by this push:
     new 32a12c6f12 Fix concurrency issues with MD5 digests in 
CloudMembershipProvider impls
32a12c6f12 is described below

commit 32a12c6f1237c4d7550f1abe2da0a5e6b12dd157
Author: Mark Thomas <[email protected]>
AuthorDate: Wed Jun 10 12:43:43 2026 +0100

    Fix concurrency issues with MD5 digests in CloudMembershipProvider impls
---
 .../membership/cloud/CloudMembershipProvider.java  | 32 ++++++++++++++--------
 .../membership/cloud/DNSMembershipProvider.java    |  4 +--
 .../cloud/KubernetesMembershipProvider.java        |  4 +--
 .../membership/cloud/LocalStrings.properties       |  1 +
 webapps/docs/changelog.xml                         |  4 +++
 5 files changed, 30 insertions(+), 15 deletions(-)

diff --git 
a/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java 
b/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
index 31e4812e41..0ed73d5227 100644
--- 
a/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
+++ 
b/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
@@ -25,6 +25,8 @@ import java.time.Instant;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Properties;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
 
 import org.apache.catalina.tribes.ChannelListener;
 import org.apache.catalina.tribes.Heartbeat;
@@ -39,7 +41,10 @@ import org.apache.juli.logging.LogFactory;
  * Abstract base class for cloud-based membership providers.
  */
 public abstract class CloudMembershipProvider extends MembershipProviderBase 
implements Heartbeat, ChannelListener {
+
     private static final Log log = 
LogFactory.getLog(CloudMembershipProvider.class);
+    private static final Queue<MessageDigest> queue = new 
ConcurrentLinkedQueue<>();
+
     /**
      * String manager for this class.
      */
@@ -71,10 +76,6 @@ public abstract class CloudMembershipProvider extends 
MembershipProviderBase imp
      * The time when this provider started.
      */
     protected Instant startTime;
-    /**
-     * MD5 message digest for hashing operations.
-     */
-    protected MessageDigest md5;
 
     /**
      * HTTP headers for cloud API requests.
@@ -96,14 +97,24 @@ public abstract class CloudMembershipProvider extends 
MembershipProviderBase imp
     protected long expirationTime = 5000;
 
     /**
-     * Creates a new CloudMembershipProvider and initializes the MD5 digest.
+     * Thread safe MD5 digest.
+     *
+     * @param input The bytes to digest
+     * @return The MD5 digest for the given input
      */
-    public CloudMembershipProvider() {
-        try {
-            md5 = MessageDigest.getInstance("md5");
-        } catch (NoSuchAlgorithmException e) {
-            // Ignore
+    protected static byte[] digest(byte[] input) {
+        MessageDigest md = queue.poll();
+        if (md == null) {
+            try {
+                md = MessageDigest.getInstance("MD5");
+            } catch (NoSuchAlgorithmException e) {
+                // Newer JVMs are not required to support MD5
+                throw new 
IllegalStateException(sm.getString("cloudMembershipProvider.noDigest"), e);
+            }
         }
+        byte[] result = md.digest(input);
+        queue.add(md);
+        return result;
     }
 
     /**
@@ -227,5 +238,4 @@ public abstract class CloudMembershipProvider extends 
MembershipProviderBase imp
     public boolean accept(Serializable msg, Member sender) {
         return false;
     }
-
 }
diff --git 
a/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java 
b/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java
index 5f44d5dc5c..497d0e6839 100644
--- 
a/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java
+++ 
b/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java
@@ -157,7 +157,7 @@ public class DNSMembershipProvider extends 
CloudMembershipProvider {
         if (inetAddresses != null) {
             for (InetAddress inetAddress : inetAddresses) {
                 String ip = inetAddress.getHostAddress();
-                byte[] id = md5.digest(ip.getBytes());
+                byte[] id = digest(ip.getBytes());
                 // We found ourselves, ignore
                 if (ip.equals(localIp)) {
                     // Update the UID on initial lookup
@@ -209,7 +209,7 @@ public class DNSMembershipProvider extends 
CloudMembershipProvider {
                 buf.append('.').append(host[i] & 0xff);
             }
 
-            byte[] id = md5.digest(buf.toString().getBytes());
+            byte[] id = digest(buf.toString().getBytes());
             member.setUniqueId(id);
             member.setMemberAliveTime(-1);
             updateMember(member, true);
diff --git 
a/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
 
b/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
index f2e494d77f..2d8fb72783 100644
--- 
a/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
+++ 
b/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
@@ -270,7 +270,7 @@ public class KubernetesMembershipProvider extends 
CloudMembershipProvider {
                     Member localMember = service.getLocalMember(false);
                     if (localMember.getUniqueId() == 
CloudMembershipService.INITIAL_ID &&
                             localMember instanceof MemberImpl) {
-                        byte[] id = 
md5.digest(uid.getBytes(StandardCharsets.US_ASCII));
+                        byte[] id = 
digest(uid.getBytes(StandardCharsets.US_ASCII));
                         ((MemberImpl) localMember).setUniqueId(id);
                     }
                     continue;
@@ -288,7 +288,7 @@ public class KubernetesMembershipProvider extends 
CloudMembershipProvider {
                     
log.error(sm.getString("kubernetesMembershipProvider.memberError"), ioe);
                     continue;
                 }
-                byte[] id = 
md5.digest(uid.getBytes(StandardCharsets.US_ASCII));
+                byte[] id = digest(uid.getBytes(StandardCharsets.US_ASCII));
                 member.setUniqueId(id);
                 members.add(member);
             }
diff --git 
a/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties 
b/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties
index 3af508eb2b..632f49ccc6 100644
--- a/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties
@@ -25,6 +25,7 @@ abstractStream.trustManagerError=Could not create trust 
manager for [{0}]
 certificateStream.clientCertError=Could not create key manager for [{0}] 
([{1}])
 
 cloudMembershipProvider.add=Member [{0}] added
+cloudMembershipProvider.noDigest=Unable to create an MD5 MessageDigest instance
 cloudMembershipProvider.remove=Member [{0}] disappeared
 cloudMembershipProvider.start=Namespace [{0}] set; clustering enabled
 
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 214b0d8fec..0c50cdf434 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -387,6 +387,10 @@
         Fix some concurrency issues in <code>TwoPhaseCommitInterceptor</code>.
         (markt)
       </fix>
+      <fix>
+        Fix concurrency issues generating MD5 digests in the
+        <code>CloudMembershipProvider</code> implementations. (markt)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="WebSocket">


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to