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

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

commit ab90082aaa7e7d472d2c9a70148c26812052acfc
Author: opencode <[email protected]>
AuthorDate: Sun Oct 11 12:34:11 2026 +0200

    Hand out a copy of the static members list
    
    The accessor of the static members returned the internal list that the
    membership provider iterates from its ping thread and at start and
    stop. A caller that mutated the returned list at runtime raced that
    iteration of an unsynchronized array list, with the member list
    occasionally read in an inconsistent state and members silently
    missing from pings and start notifications.
    
    The accessor now hands out a copy, and its contract says so, leaving
    the provider with the configured list, which only the service itself
    still touches. The signature keeps the array list type, so callers
    compiled against the accessor are unaffected, while the read only
    callers in this repository keep working against the copy.
    
    Add tests pinning the contract, an accessor that returns a different
    list each call and mutations of the returned list that leave the
    provider list untouched.
---
 .../tribes/membership/StaticMembershipService.java |  7 ++++---
 .../membership/TestStaticMembershipService.java    | 22 ++++++++++++++++++++++
 2 files changed, 26 insertions(+), 3 deletions(-)

diff --git 
a/java/org/apache/catalina/tribes/membership/StaticMembershipService.java 
b/java/org/apache/catalina/tribes/membership/StaticMembershipService.java
index b4d9d3101e..2d4f8c399e 100644
--- a/java/org/apache/catalina/tribes/membership/StaticMembershipService.java
+++ b/java/org/apache/catalina/tribes/membership/StaticMembershipService.java
@@ -164,11 +164,12 @@ public class StaticMembershipService extends 
MembershipServiceBase implements St
     }
 
     /**
-     * Returns the list of static members.
-     * @return the static members list
+     * Returns a copy of the list of static members. Mutating the returned 
list has no effect on
+     * this service. The provider uses the configured list directly while it 
runs.
+     * @return a copy of the static members list
      */
     public ArrayList<StaticMember> getStaticMembers() {
-        return staticMembers;
+        return new ArrayList<>(staticMembers);
     }
 
     /**
diff --git 
a/test/org/apache/catalina/tribes/membership/TestStaticMembershipService.java 
b/test/org/apache/catalina/tribes/membership/TestStaticMembershipService.java
index ddafa78835..71d35f5c5c 100644
--- 
a/test/org/apache/catalina/tribes/membership/TestStaticMembershipService.java
+++ 
b/test/org/apache/catalina/tribes/membership/TestStaticMembershipService.java
@@ -16,6 +16,7 @@
  */
 package org.apache.catalina.tribes.membership;
 
+import java.util.ArrayList;
 import java.util.Properties;
 
 import org.junit.Assert;
@@ -120,4 +121,25 @@ public class TestStaticMembershipService {
             service.stop(level);
         }
     }
+
+    /*
+     * The accessor hands out the list that the provider iterates while it 
runs. A caller that
+     * mutated the returned list raced that iteration, so the accessor must 
hand out a copy
+     * instead of the live list.
+     */
+    @Test
+    public void testGetStaticMembersReturnsACopy() throws Exception {
+        StaticMembershipService service = new StaticMembershipService();
+        StaticMember member = new StaticMember("127.0.0.1", 4005, -1);
+        service.addStaticMember(member);
+
+        ArrayList<StaticMember> members = service.getStaticMembers();
+        Assert.assertNotSame("the accessor must not hand out the same list 
twice", members,
+                service.getStaticMembers());
+        Assert.assertTrue(members.remove(member));
+        Assert.assertFalse(members.contains(member));
+
+        // The live list that the provider reads must be untouched by the copy 
mutation
+        Assert.assertTrue(service.getStaticMembers().contains(member));
+    }
 }


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

Reply via email to