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

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

commit 93feb7deb46aba3839e98a944ea6bbea4cf9ffa7
Author: opencode <[email protected]>
AuthorDate: Fri Oct 9 14:09:02 2026 +0200

    Validate FragmentationInterceptor maxSize and expire configuration
    
    setMaxSize(0) made frag() divide by zero, throwing ArithmeticException
    on every message above the threshold, and a negative max size produced
    a negative fragment count and a NegativeArraySizeException. A
    non-positive expire made FragCollection.expired() true immediately, so
    the heartbeat removed every fragment collection and fragmented
    messages were silently never reassembled.
    
    Reject values below one in both setters, mirroring the validation
    style of EncryptInterceptor.setReplayWindowTime. As a related guard,
    fail a send with a ChannelException when the configured max size would
    require more fragments than the receiver accepts via MAX_FRAGMENTS,
    instead of sending fragments that are guaranteed to be discarded.
---
 .../interceptors/FragmentationInterceptor.java     | 12 +++++
 .../group/interceptors/LocalStrings.properties     |  3 ++
 .../interceptors/TestFragmentationInterceptor.java | 61 ++++++++++++++++++++++
 3 files changed, 76 insertions(+)

diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
 
b/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
index ec550c411e..509942496b 100644
--- 
a/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
+++ 
b/java/org/apache/catalina/tribes/group/interceptors/FragmentationInterceptor.java
@@ -171,6 +171,12 @@ public class FragmentationInterceptor extends 
ChannelInterceptorBase implements
         int size = msg.getMessage().getLength();
 
         int count = ((size / maxSize) + (size % maxSize == 0 ? 0 : 1));
+        if (count > MAX_FRAGMENTS) {
+            // The receiver rejects fragment counts above MAX_FRAGMENTS, so 
fail here
+            // rather than sending fragments that are guaranteed to be 
discarded.
+            throw new 
ChannelException(sm.getString("fragmentationInterceptor.fragments.exceed-max",
+                    Integer.toString(size), Integer.toString(count), 
Integer.toString(MAX_FRAGMENTS)));
+        }
         ChannelMessage[] messages = new ChannelMessage[count];
         int remaining = size;
         for (int i = 0; i < count; i++) {
@@ -229,11 +235,17 @@ public class FragmentationInterceptor extends 
ChannelInterceptorBase implements
 
     @Override
     public void setMaxSize(int maxSize) {
+        if (maxSize < 1) {
+            throw new 
IllegalArgumentException(sm.getString("fragmentationInterceptor.maxSize.tooSmall"));
+        }
         this.maxSize = maxSize;
     }
 
     @Override
     public void setExpire(long expire) {
+        if (expire < 1) {
+            throw new 
IllegalArgumentException(sm.getString("fragmentationInterceptor.expire.tooSmall"));
+        }
         this.expire = expire;
     }
 
diff --git 
a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties 
b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
index 49a2a52fa6..df34613fb0 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
@@ -32,13 +32,16 @@ encryptInterceptor.replayWindowMessageCount.tooSmall=Replay 
window message count
 encryptInterceptor.replayWindowTime.tooSmall=Replay window time must be at 
least 1 millisecond
 encryptInterceptor.tcpFailureDetector.ordering=EncryptInterceptor must be 
upstream of TcpFailureDetector. Please re-order EncryptInterceptor to be listed 
before TcpFailureDetector in your channel interceptor pipeline.
 
+fragmentationInterceptor.expire.tooSmall=Expire must be at least 1 millisecond
 fragmentationInterceptor.fragment.discarded=Received fragment failed 
validation. The fragment is discarded.
 fragmentationInterceptor.fragment.too-short=Received fragment with too little 
data, length [{0}].
+fragmentationInterceptor.fragments.exceed-max=Unable to fragment a message of 
[{0}] bytes into [{1}] fragments, the maximum number of fragments is [{2}].
 fragmentationInterceptor.fragments.missing=Fragments are missing.
 fragmentationInterceptor.fragments.too-large=The combined length [{0}] of the 
received fragments exceeds the maximum supported message size.
 fragmentationInterceptor.heartbeat.failed=Unable to perform heartbeat clean up 
in the frag interceptor
 fragmentationInterceptor.invalid.frag.count=Received invalid fragment count 
[{0}]. The fragment is discarded.
 fragmentationInterceptor.invalid.frag.nr=Received fragment number [{0}] for a 
message with [{1}] fragments. The fragment is discarded.
+fragmentationInterceptor.maxSize.tooSmall=Max size must be at least 1 byte
 
 gzipInterceptor.compress.failed=Unable to compress byte contents
 gzipInterceptor.decompress.failed=Unable to decompress byte contents
diff --git 
a/test/org/apache/catalina/tribes/group/interceptors/TestFragmentationInterceptor.java
 
b/test/org/apache/catalina/tribes/group/interceptors/TestFragmentationInterceptor.java
index b9f70730f8..6fa862d429 100644
--- 
a/test/org/apache/catalina/tribes/group/interceptors/TestFragmentationInterceptor.java
+++ 
b/test/org/apache/catalina/tribes/group/interceptors/TestFragmentationInterceptor.java
@@ -82,6 +82,67 @@ public class TestFragmentationInterceptor {
         return Arrays.copyOfRange(msg.getMessage().getBytesDirect(), 0, len);
     }
 
+    @Test
+    public void testSetMaxSizeRejectsInvalidValues() {
+        FragmentationInterceptor interceptor = new FragmentationInterceptor();
+
+        try {
+            interceptor.setMaxSize(0);
+            Assert.fail("A max size of zero would make fragmentation divide by 
zero");
+        } catch (IllegalArgumentException expected) {
+            // Expected
+        }
+
+        try {
+            interceptor.setMaxSize(-1);
+            Assert.fail("A negative max size would make fragmentation allocate 
a negative sized array");
+        } catch (IllegalArgumentException expected) {
+            // Expected
+        }
+
+        interceptor.setMaxSize(1024);
+        Assert.assertEquals(1024, interceptor.getMaxSize());
+    }
+
+    @Test
+    public void testSetExpireRejectsInvalidValues() {
+        FragmentationInterceptor interceptor = new FragmentationInterceptor();
+
+        try {
+            interceptor.setExpire(0);
+            Assert.fail("A non-positive expire would discard fragment 
collections before reassembly can complete");
+        } catch (IllegalArgumentException expected) {
+            // Expected
+        }
+
+        try {
+            interceptor.setExpire(-1);
+            Assert.fail("A negative expire would discard fragment collections 
before reassembly can complete");
+        } catch (IllegalArgumentException expected) {
+            // Expected
+        }
+
+        interceptor.setExpire(60000);
+        Assert.assertEquals(60000, interceptor.getExpire());
+    }
+
+    @Test
+    public void testFragExceedingMaxFragmentsFails() throws Exception {
+        Member destination = new MemberImpl("localhost", 4000, -1);
+        FragmentationInterceptor sender = new FragmentationInterceptor();
+        sender.setMaxSize(1);
+        sender.setChannel(new GroupChannel());
+        sender.setNext(new CapturingSender());
+
+        byte[] payload = new byte[FragmentationInterceptor.MAX_FRAGMENTS + 1];
+        try {
+            sender.sendMessage(new Member[] { destination }, 
createMessage(payload, destination), null);
+            Assert.fail("Fragmenting into more than MAX_FRAGMENTS fragments 
should fail");
+        } catch (ChannelException expected) {
+            // Expected
+        }
+    }
+
     @Test
     public void testFragmentRoundTrip() throws Exception {
         Member destination = new MemberImpl("localhost", 4000, -1);


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

Reply via email to