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 033db11ff73fdb3920b3b239540e8f8d3c3f37d7
Author: opencode <[email protected]>
AuthorDate: Thu Oct 15 13:19:04 2026 +0200

    Fix ArrayIndexOutOfBoundsException in IPv6Utils.canonize()
    
    Inputs that pass the mayBeIPv6Address() heuristic but contain more than
    8 colon-separated groups (e.g. "1:2:3:4:5:6:7:8:9" or forms with more
    than two consecutive colons), or groups with more than 4 significant
    characters (e.g. "12345::"), indexed the fixed-size group store out of
    bounds and threw an undocumented ArrayIndexOutOfBoundsException. With
    the non-default access log attribute ipv6Canonical="true", such values
    (e.g. from a client-supplied Host header via the %v element) caused
    loss of the access log record plus a warning per request. canonize()
    now returns such malformed inputs unchanged, consistent with how
    non-IPv6 input is already passed through. Add tests for the
    pass-through behaviour and for the valid 8-colon forms
    ("1:2:3:4:5:6:7::") whose canonicalization must be preserved.
---
 java/org/apache/tomcat/util/net/IPv6Utils.java     | 20 ++++++++++++++
 test/org/apache/tomcat/util/net/TestIPv6Utils.java | 31 ++++++++++++++++++++++
 2 files changed, 51 insertions(+)

diff --git a/java/org/apache/tomcat/util/net/IPv6Utils.java 
b/java/org/apache/tomcat/util/net/IPv6Utils.java
index 8e82d1320c..4b4cb50142 100644
--- a/java/org/apache/tomcat/util/net/IPv6Utils.java
+++ b/java/org/apache/tomcat/util/net/IPv6Utils.java
@@ -90,6 +90,20 @@ public class IPv6Utils {
             ipv6AddressLength = ipv6Address.lastIndexOf('%');
         }
 
+        // A valid IPv6 address contains at most 8 colons: 7 separate 8 
explicit groups, and 8 are only reached
+        // when "::" compresses a single zero group next to 7 explicit groups 
(RFC 4291 allows "::" to stand for
+        // one or more zero groups). Inputs with more colons than that are not 
valid IPv6 addresses but the
+        // mayBeIPv6Address() heuristic above does not exclude them. Return 
such inputs unchanged.
+        int colonCount = 0;
+        for (int i = 0; i < ipv6AddressLength; i++) {
+            if (ipv6Address.charAt(i) == ':') {
+                colonCount++;
+            }
+        }
+        if (colonCount > MAX_NUMBER_OF_GROUPS) {
+            return ipv6Address;
+        }
+
         StringBuilder result = new StringBuilder();
         char[][] groups = new char[MAX_NUMBER_OF_GROUPS][MAX_GROUP_LENGTH];
         int groupCounter = 0;
@@ -151,6 +165,12 @@ public class IPv6Utils {
                 c = (char) (c + 32);
             }
             if (c != ':') {
+                if (groupCounter >= MAX_NUMBER_OF_GROUPS || charInGroupCounter 
>= MAX_GROUP_LENGTH) {
+                    // A ninth group with content, or a group with more than 4 
significant characters. Neither is a
+                    // valid IPv6 address but the mayBeIPv6Address() heuristic 
above does not exclude them. Return
+                    // the input unchanged.
+                    return ipv6Address;
+                }
                 groups[groupCounter][charInGroupCounter] = c;
                 if (!(groupStart && c == '0')) {
                     ++charInGroupCounter;
diff --git a/test/org/apache/tomcat/util/net/TestIPv6Utils.java 
b/test/org/apache/tomcat/util/net/TestIPv6Utils.java
index 5d667ed67b..fd909bb9c8 100644
--- a/test/org/apache/tomcat/util/net/TestIPv6Utils.java
+++ b/test/org/apache/tomcat/util/net/TestIPv6Utils.java
@@ -114,6 +114,12 @@ public class TestIPv6Utils {
         // Already ends with "::"
         Assert.assertEquals("2001:db8::", IPv6Utils.canonize("2001:db8::"));
 
+        // "::" at the start or end standing for a single zero group next to 7 
explicit groups (8 colons)
+        Assert.assertEquals("1:2:3:4:5:6:7:0", 
IPv6Utils.canonize("1:2:3:4:5:6:7::"));
+        Assert.assertEquals("0:2:3:4:5:6:7:8", 
IPv6Utils.canonize("::2:3:4:5:6:7:8"));
+        Assert.assertEquals("::", IPv6Utils.canonize("0:0:0:0:0:0:0::"));
+        Assert.assertEquals("1:2:3:4:5:6:7:0%eth0", 
IPv6Utils.canonize("1:2:3:4:5:6:7::%eth0"));
+
         // IPv4 inside IPv6
         Assert.assertEquals("::ffff:192.0.2.1", 
IPv6Utils.canonize("::ffff:192.0.2.1"));
         Assert.assertEquals("::ffff:192.0.2.1", 
IPv6Utils.canonize("0:0:0:0:0:ffff:192.0.2.1"));
@@ -141,4 +147,29 @@ public class TestIPv6Utils {
         Assert.assertEquals("www.apache.org", 
IPv6Utils.canonize("www.apache.org"));
         Assert.assertEquals("ipv6.google.com", 
IPv6Utils.canonize("ipv6.google.com"));
     }
+
+    @Test
+    public void testCanonizeTooManyGroups() {
+        // Strings with more than 8 groups pass the mayBeIPv6Address() 
heuristic but are not valid IPv6
+        // addresses. They used to trigger an ArrayIndexOutOfBoundsException. 
The input is now returned
+        // unchanged.
+        Assert.assertEquals("1:2:3:4:5:6:7:8:9", 
IPv6Utils.canonize("1:2:3:4:5:6:7:8:9"));
+        Assert.assertEquals("1:2:3:4:5:6:7::8:9", 
IPv6Utils.canonize("1:2:3:4:5:6:7::8:9"));
+        Assert.assertEquals("1:2:3:4:5:6:7:8:9:10", 
IPv6Utils.canonize("1:2:3:4:5:6:7:8:9:10"));
+        Assert.assertEquals("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
+                
IPv6Utils.canonize("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"));
+        Assert.assertEquals("1:2:3:4:5:6:7:8:9%1", 
IPv6Utils.canonize("1:2:3:4:5:6:7:8:9%1"));
+        // Additional empty groups created by more than two consecutive colons
+        Assert.assertEquals("1:2:3:4:5:6:7:8:::", 
IPv6Utils.canonize("1:2:3:4:5:6:7:8:::"));
+        Assert.assertEquals("::::a:::::", IPv6Utils.canonize("::::a:::::"));
+    }
+
+    @Test
+    public void testCanonizeGroupTooLong() {
+        // A group with more than 4 significant hex digits passes the 
mayBeIPv6Address() heuristic but is
+        // not valid IPv6. It used to trigger an 
ArrayIndexOutOfBoundsException. The input is now returned
+        // unchanged.
+        Assert.assertEquals("12345:0:0:0:0:0:0:0", 
IPv6Utils.canonize("12345:0:0:0:0:0:0:0"));
+        Assert.assertEquals("12345::", IPv6Utils.canonize("12345::"));
+    }
 }


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

Reply via email to