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

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


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

commit 72ea04c13a3e9a1dc5d50b4f281a64c9cba5b952
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 0d2b0888f0..46ff974194 100644
--- 
a/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
+++ 
b/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
@@ -27,6 +27,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;
@@ -41,7 +43,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.
      */
@@ -73,10 +78,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.
@@ -98,14 +99,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;
     }
 
     /**
@@ -229,5 +240,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 1d810b27df..3e4907389f 100644
--- 
a/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java
+++ 
b/java/org/apache/catalina/tribes/membership/cloud/DNSMembershipProvider.java
@@ -156,7 +156,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
@@ -208,7 +208,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 28d75b02df..83d6206ca8 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 9cfc45e4d7..38bed9c45d 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -372,6 +372,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