Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=42181
A. Better handling of edge conditions in chunk header processing (BZ 42181)
B. Improve chunk header parsing. Properly ignore chunk-extension suffix, not trying to parse digits contained in it. Reject chunks whose header is incorrect.

git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc5.5.x/trunk@1392254 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/STATUS.txt b/STATUS.txt
index 4b3c20c..bae7a2f 100644
--- a/STATUS.txt
+++ b/STATUS.txt
@@ -28,15 +28,6 @@
 PATCHES PROPOSED TO BACKPORT:
   [ New proposals should be added at the end of the list ]
 
-* Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=42181
-  A. Better handling of edge conditions in chunk header processing (BZ 42181)
-  B. Improve chunk header parsing. Properly ignore chunk-extension suffix,
-  not trying to parse digits contained in it. Reject chunks whose header is
-  incorrect. (backport of r423453)
-  https://issues.apache.org/bugzilla/attachment.cgi?id=29295
-  +1: kkolinko, markt, kfujino
-  -1:
-
 * Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=53830
   Better handling of Manager.randomFile default value on Windows
   https://issues.apache.org/bugzilla/attachment.cgi?id=29331
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Constants.java b/connectors/http11/src/java/org/apache/coyote/http11/Constants.java
index a963613..8455c32 100644
--- a/connectors/http11/src/java/org/apache/coyote/http11/Constants.java
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Constants.java
@@ -85,6 +85,11 @@
      */
     public static final byte COLON = (byte) ':';
 
+    /**
+     * SEMI_COLON.
+     */
+    public static final byte SEMI_COLON = (byte) ';';
+
 
     /**
      * 'A'.
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
index ec43d48..c481d26 100644
--- a/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11AprProcessor.java
@@ -113,7 +113,7 @@
         initializeFilters();
 
         // Cause loading of HexUtils
-        int foo = HexUtils.DEC[0];
+        HexUtils.getDec('0');
 
         // Cause loading of FastHttpDateFormat
         FastHttpDateFormat.getCurrentDate();
@@ -1461,7 +1461,7 @@
             int port = 0;
             int mult = 1;
             for (int i = valueL - 1; i > colonPos; i--) {
-                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
+                int charValue = HexUtils.getDec(valueB[i + valueS]);
                 if (charValue == -1) {
                     // Invalid character
                     error = true;
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java b/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
index 17999cd..ec60c01 100644
--- a/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
+++ b/connectors/http11/src/java/org/apache/coyote/http11/Http11Processor.java
@@ -111,7 +111,7 @@
         initializeFilters();
 
         // Cause loading of HexUtils
-        int foo = HexUtils.DEC[0];
+        HexUtils.getDec('0');
 
     }
 
@@ -1422,7 +1422,7 @@
             int port = 0;
             int mult = 1;
             for (int i = valueL - 1; i > colonPos; i--) {
-                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
+                int charValue = HexUtils.getDec(valueB[i + valueS]);
                 if (charValue == -1) {
                     // Invalid character
                     error = true;
diff --git a/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java b/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
index 76558da..cab325e 100644
--- a/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+++ b/connectors/http11/src/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -28,7 +28,8 @@
 import org.apache.coyote.http11.InputFilter;
 
 /**
- * Chunked input filter.
+ * Chunked input filter. Parses chunked data according to
+ * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1">http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1</a><br>
  * 
  * @author Remy Maucherat
  */
@@ -128,7 +129,7 @@
 
         if (remaining <= 0) {
             if (!parseChunkHeader()) {
-                throw new IOException("Invalid chunk");
+                throw new IOException("Invalid chunk header");
             }
             if (endChunk) {
                 parseEndChunk();
@@ -236,6 +237,14 @@
 
     /**
      * Parse the header of a chunk.
+     * A chunk header can look like one of the following:<br />
+     * A10CRLF<br />
+     * F23;chunk-extension to be ignoredCRLF
+     *
+     * <p>
+     * The letters before CRLF or ';' (whatever comes first) must be valid hex
+     * digits. We should not parse F23IAMGONNAMESSTHISUP34CRLF as a valid
+     * header according to the spec.
      */
     protected boolean parseChunkHeader()
         throws IOException {
@@ -243,6 +252,7 @@
         int result = 0;
         boolean eol = false;
         boolean readDigit = false;
+        boolean trailer = false;
 
         while (!eol) {
 
@@ -254,11 +264,19 @@
             if (buf[pos] == Constants.CR) {
             } else if (buf[pos] == Constants.LF) {
                 eol = true;
-            } else {
-                if (HexUtils.DEC[buf[pos]] != -1) {
+            } else if (buf[pos] == Constants.SEMI_COLON) {
+                trailer = true;
+            } else if (!trailer) { 
+                //don't read data after the trailer
+                int charValue = HexUtils.getDec(buf[pos]);
+                if (charValue != -1) {
                     readDigit = true;
                     result *= 16;
-                    result += HexUtils.DEC[buf[pos]];
+                    result += charValue;
+                } else {
+                    //we shouldn't allow invalid, non hex characters
+                    //in the chunked header
+                    return false;
                 }
             }
 
diff --git a/connectors/jk/java/org/apache/coyote/ajp/AjpAprProcessor.java b/connectors/jk/java/org/apache/coyote/ajp/AjpAprProcessor.java
index fa70319..527546a 100644
--- a/connectors/jk/java/org/apache/coyote/ajp/AjpAprProcessor.java
+++ b/connectors/jk/java/org/apache/coyote/ajp/AjpAprProcessor.java
@@ -103,7 +103,7 @@
         outputBuffer = ByteBuffer.allocateDirect(packetSize * 2);
 
         // Cause loading of HexUtils
-        int foo = HexUtils.DEC[0];
+        HexUtils.getDec('0');
 
         // Cause loading of HttpMessages
         HttpMessages.getMessage(200);
@@ -935,7 +935,7 @@
             int port = 0;
             int mult = 1;
             for (int i = valueL - 1; i > colonPos; i--) {
-                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
+                int charValue = HexUtils.getDec(valueB[i + valueS]);
                 if (charValue == -1) {
                     // Invalid character
                     error = true;
diff --git a/connectors/jk/java/org/apache/jk/common/HandlerRequest.java b/connectors/jk/java/org/apache/jk/common/HandlerRequest.java
index 9701acf..4af7bbd 100644
--- a/connectors/jk/java/org/apache/jk/common/HandlerRequest.java
+++ b/connectors/jk/java/org/apache/jk/common/HandlerRequest.java
@@ -674,7 +674,7 @@
             int port = 0;
             int mult = 1;
             for (int i = valueL - 1; i > colonPos; i--) {
-                int charValue = HexUtils.DEC[(int) valueB[i + valueS]];
+                int charValue = HexUtils.getDec(valueB[i + valueS]);
                 if (charValue == -1) {
                     // Invalid character
                     throw new CharConversionException("Invalid char in port: " + valueB[i + valueS]); 
diff --git a/connectors/util/java/org/apache/tomcat/util/buf/HexUtils.java b/connectors/util/java/org/apache/tomcat/util/buf/HexUtils.java
index 192988a..80637ab 100644
--- a/connectors/util/java/org/apache/tomcat/util/buf/HexUtils.java
+++ b/connectors/util/java/org/apache/tomcat/util/buf/HexUtils.java
@@ -36,6 +36,7 @@
 
     /**
      *  Table for HEX to DEC byte translation.
+     *  @deprecated Use {@link #getDec(int)}
      */
     public static final int[] DEC = {
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -76,6 +77,15 @@
     // --------------------------------------------------------- Static Methods
 
 
+    public static int getDec(int index){
+        try {
+            return DEC[index];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            return -1;
+        }
+    }
+
+
     /**
      * Convert a String of hexadecimal digits into the corresponding
      * byte array by encoding each two hexadecimal digits as a byte.
@@ -154,21 +164,21 @@
 	// assert valid data
 	int len;
 	if(hex.length < 4 ) return 0;
-	if( DEC[hex[0]]<0 )
+	if( getDec(hex[0])<0 )
 	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
-	len = DEC[hex[0]];
+	len = getDec(hex[0]);
 	len = len << 4;
-	if( DEC[hex[1]]<0 )
+	if( getDec(hex[1])<0 )
 	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
-	len += DEC[hex[1]];
+	len += getDec(hex[1]);
 	len = len << 4;
-	if( DEC[hex[2]]<0 )
+	if( getDec(hex[2])<0 )
 	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
-	len += DEC[hex[2]];
+	len += getDec(hex[2]);
 	len = len << 4;
-	if( DEC[hex[3]]<0 )
+	if( getDec(hex[3])<0 )
 	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
-	len += DEC[hex[3]];
+	len += getDec(hex[3]);
 	return len;
     }
 
diff --git a/container/catalina/src/share/org/apache/catalina/util/HexUtils.java b/container/catalina/src/share/org/apache/catalina/util/HexUtils.java
index 685e63b..e277fcd 100644
--- a/container/catalina/src/share/org/apache/catalina/util/HexUtils.java
+++ b/container/catalina/src/share/org/apache/catalina/util/HexUtils.java
@@ -30,7 +30,10 @@
 public final class HexUtils {
     // Code from Ajp11, from Apache's JServ
 
-    // Table for HEX to DEC byte translation
+    /**
+     *  Table for HEX to DEC byte translation.
+     *  @deprecated Use {@link #getDec(int)}
+     */
     public static final int[] DEC = {
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@@ -59,6 +62,18 @@
         StringManager.getManager("org.apache.catalina.util");
 
 
+    // --------------------------------------------------------- Static Methods
+
+
+    public static int getDec(int index){
+        try {
+            return DEC[index];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            return -1;
+        }
+    }
+
+
     /**
      * Convert a String of hexadecimal digits into the corresponding
      * byte array by encoding each two hexadecimal digits as a byte.
@@ -137,21 +152,21 @@
         // assert valid data
         int len;
         if(hex.length < 4 ) return 0;
-        if( DEC[hex[0]]<0 )
+        if( getDec(hex[0])<0 )
             throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
-        len = DEC[hex[0]];
+        len = getDec(hex[0]);
         len = len << 4;
-        if( DEC[hex[1]]<0 )
+        if( getDec(hex[1])<0 )
             throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
-        len += DEC[hex[1]];
+        len += getDec(hex[1]);
         len = len << 4;
-        if( DEC[hex[2]]<0 )
+        if( getDec(hex[2])<0 )
             throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
-        len += DEC[hex[2]];
+        len += getDec(hex[2]);
         len = len << 4;
-        if( DEC[hex[3]]<0 )
+        if( getDec(hex[3])<0 )
             throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
-        len += DEC[hex[3]];
+        len += getDec(hex[3]);
         return len;
     }
 
diff --git a/container/webapps/docs/changelog.xml b/container/webapps/docs/changelog.xml
index 53bba36..fc9f848 100644
--- a/container/webapps/docs/changelog.xml
+++ b/container/webapps/docs/changelog.xml
@@ -82,6 +82,12 @@
       <add>
         Implement the maxHeaderCount for the HTTP connectors. (kkolinko)
       </add>
+      <fix>
+        <bug>42181</bug>: Better handling of edge conditions in chunk header
+        processing. Improve chunk header parsing. Properly ignore
+        chunk-extension suffix, not trying to parse digits contained in it.
+        Reject chunks whose header is incorrect. (kkolinko)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Webapps">