Author: markt
Date: Sat Mar 2 20:17:28 2013
New Revision: 1451933
URL: http://svn.apache.org/r1451933
Log:
Expand UTF-8 tests based on a test case written by Joe Orton for CVE-2008-2938
Note the additional failures this generates are not exploitable. They are
invalid only because they encode a valid code point in too many bytes.
Modified:
tomcat/trunk/java/org/apache/tomcat/websocket/Utf8Decoder.java
tomcat/trunk/test/org/apache/tomcat/websocket/TestUtf8.java
Modified: tomcat/trunk/java/org/apache/tomcat/websocket/Utf8Decoder.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/Utf8Decoder.java?rev=1451933&r1=1451932&r2=1451933&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/Utf8Decoder.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/Utf8Decoder.java Sat Mar 2
20:17:28 2013
@@ -169,13 +169,31 @@ public class Utf8Decoder extends Charset
return CoderResult.malformedForLength(1);
}
if (inIndexLimit - inIndex < 1 + tail) {
- // Apache Tomcat added test - detects invalid sequence as
+ // Apache Tomcat added tests - detect invalid sequences as
// early as possible
if (jchar == 0x74 && inIndexLimit > inIndex + 1) {
if ((bArr[inIndex + 1] & 0xFF) > 0x8F) {
+ // 11110100 1yyyxxxx xxxxxxxx xxxxxxxx
+ // Any non-zero y is > max code point
return CoderResult.unmappableForLength(4);
}
}
+ if (jchar == 0x60 && inIndexLimit > inIndex +1) {
+ if ((bArr[inIndex + 1] & 0x7F) == 0) {
+ // 11100000 10000000 10xxxxxx
+ // should have been
+ // 00xxxxxx
+ return CoderResult.malformedForLength(3);
+ }
+ }
+ if (jchar == 0x70 && inIndexLimit > inIndex +1) {
+ if ((bArr[inIndex + 1] & 0x7F) < 0x10) {
+ // 11110000 1000zzzz 1oyyyyyy 1oxxxxxx
+ // should have been
+ // 111ozzzz 1oyyyyyy 1oxxxxxx
+ return CoderResult.malformedForLength(4);
+ }
+ }
break;
}
for (int i = 0; i < tail; i++) {
Modified: tomcat/trunk/test/org/apache/tomcat/websocket/TestUtf8.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestUtf8.java?rev=1451933&r1=1451932&r2=1451933&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/websocket/TestUtf8.java (original)
+++ tomcat/trunk/test/org/apache/tomcat/websocket/TestUtf8.java Sat Mar 2
20:17:28 2013
@@ -38,6 +38,56 @@ public class TestUtf8 {
private static final byte[] SRC_BYTES_2 =
new byte[] {-12, -112, -128, -128};
+ // Various invalid UTF-8 sequences
+ private static final byte[][] MALFORMED = {
+ // One-byte sequences:
+ {(byte)0xFF },
+ {(byte)0xC0 },
+ {(byte)0x80 },
+
+ // Two-byte sequences:
+ {(byte)0xC0, (byte)0x80}, // U+0000 zero-padded
+ {(byte)0xC1, (byte)0xBF}, // U+007F zero-padded
+ {(byte)0xFF, (byte)0xFF}, // all ones
+ {(byte)0xE0, (byte)0x80}, // 111x first byte first nibble
+ {(byte)0xA0, (byte)0x80}, // 101x first byte first nibble
+ {(byte)0xC2, (byte)0x00}, // invalid second byte
+ {(byte)0xC2, (byte)0xC0}, // invalid second byte
+
+ // Three-byte sequences
+ {(byte)0xE0, (byte)0x80, (byte)0x80 }, // U+0000 zero-padded
+ {(byte)0xE0, (byte)0x81, (byte)0xBF }, // U+007F zero-padded
+ {(byte)0xE0, (byte)0x9F, (byte)0xBF }, // U+07FF zero-padded
+ {(byte)0xFF, (byte)0xFF, (byte)0xFF }, // all ones
+ {(byte)0xF0, (byte)0x80, (byte)0x80 }, // invalid first byte
+ {(byte)0xE0, (byte)0xC0, (byte)0x80 }, // invalid second byte
+ {(byte)0xE0, (byte)0x80, (byte)0xC0 }, // invalid third byte
+
+ // Four-byte sequences
+ {(byte)0xF0, (byte)0x80, (byte)0x80, (byte)0x80 }, // U+0000
zero-padded
+ {(byte)0xF0, (byte)0x80, (byte)0x81, (byte)0xBF }, // U+007F
zero-padded
+ {(byte)0xF0, (byte)0x80, (byte)0x9F, (byte)0xBF }, // U+007F
zero-padded
+ {(byte)0xF0, (byte)0x8F, (byte)0xBF, (byte)0xBF }, // U+07FF
zero-padded
+
+ {(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF }, // all ones
+ {(byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x80 }, // invalid
first byte
+ {(byte)0xF0, (byte)0xC0, (byte)0x80, (byte)0x80 }, // invalid
second byte
+ {(byte)0xF0, (byte)0x80, (byte)0xC0, (byte)0x80 }, // invalid
third byte
+ {(byte)0xF0, (byte)0x80, (byte)0x80, (byte)0xC0 }, // invalid
fourth byte
+
+ // Five-byte sequences
+ {(byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80 }, //
U+0000 zero-padded
+ {(byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x81, (byte)0xBF }, //
U+007F zero-padded
+ {(byte)0xF8, (byte)0x80, (byte)0x80, (byte)0x9F, (byte)0xBF }, //
U+07FF zero-padded
+ {(byte)0xF8, (byte)0x80, (byte)0x8F, (byte)0xBF, (byte)0xBF }, //
U+FFFF zero-padded
+
+ // Six-byte sequences
+ {(byte)0xFC, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x80,
(byte)0x80 }, // U+0000 zero-padded
+ {(byte)0xFC, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x81,
(byte)0xBF }, // U+007F zero-padded
+ {(byte)0xFC, (byte)0x80, (byte)0x80, (byte)0x80, (byte)0x9F,
(byte)0xBF }, // U+07FF zero-padded
+ {(byte)0xFC, (byte)0x80, (byte)0x80, (byte)0x8F, (byte)0xBF,
(byte)0xBF }, // U+FFFF zero-padded
+ };
+
@Test
public void testJvmDecoder1() {
// This should trigger an error but currently passes. Once the JVM is
@@ -76,8 +126,17 @@ public class TestUtf8 {
bb.compact();
}
- assertEquals(Boolean.valueOf(errorExpected), Boolean.valueOf(error));
- assertEquals(failPosExpected, i);
+ StringBuilder ashex = new StringBuilder(src.length * 4);
+ for (int j = 0; j < src.length; j++) {
+ if (i > 0) ashex.append(' ');
+ ashex.append(Integer.toBinaryString(src[j] & 0xff));
+ }
+
+ assertEquals(ashex.toString(),
+ Boolean.valueOf(errorExpected), Boolean.valueOf(error));
+ if (failPosExpected != -1) {
+ assertEquals(failPosExpected, i);
+ }
}
@@ -93,7 +152,7 @@ public class TestUtf8 {
}
- public void doHarmonyDecoder(byte[] src, boolean errorExpected,
+ private void doHarmonyDecoder(byte[] src, boolean errorExpected,
int failPosExpected) {
CharsetDecoder decoder = new Utf8Decoder();
@@ -113,7 +172,37 @@ public class TestUtf8 {
bb.compact();
}
- assertEquals(Boolean.valueOf(errorExpected), Boolean.valueOf(error));
- assertEquals(failPosExpected, i);
+ StringBuilder ashex = new StringBuilder(src.length * 4);
+ for (int j = 0; j < src.length; j++) {
+ if (i > 0) ashex.append(' ');
+ ashex.append(Integer.toBinaryString(src[j] & 0xff));
+ }
+
+ assertEquals(ashex.toString(),
+ Boolean.valueOf(errorExpected), Boolean.valueOf(error));
+ if (failPosExpected != -1) {
+ assertEquals(failPosExpected, i);
+ }
+ }
+
+
+ @Test
+ public void testUtf8MalformedJvm() {
+ for (int i = 0 ; i < MALFORMED.length; i++) {
+ // Known failures
+ if (i == 1 || i == 6 || i == 14 | i == 22) {
+ doJvmDecoder(MALFORMED[i], false, -1);
+ } else {
+ doJvmDecoder(MALFORMED[i], true, -1);
+ }
+ }
+ }
+
+
+ @Test
+ public void testUtf8MalformedHarmony() {
+ for (byte[] input : MALFORMED) {
+ doHarmonyDecoder(input, true, -1);
+ }
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]