JETTY-1122 handle multi-byte utf8 buffer overflow better

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@979 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Greg Wilkins 2009-10-07 22:52:06 +00:00
parent 17b94b6dc0
commit 0f01841df1
4 changed files with 206 additions and 92 deletions

View File

@ -9,6 +9,7 @@ jetty-7.0.1-SNAPSHOT
+ 289960 start.jar assumes command line args are configs
+ JETTY-937 More JVM bug work arounds. Insert pause if all else fails
+ JETTY-1114 unsynchronised WebAppClassloader.getResource(String)
+ JETTY-1122 Handle multi-byte utf that causes buffer overflow
+ Fixed XSS issue in CookieDump demo servlet.
+ 291340 Race condition in onException() notifications
+ 291589 Update jetty-rewrite demo

View File

@ -43,7 +43,6 @@ public abstract class AbstractGenerator implements Generator
public final static int STATE_END = 4;
public static final byte[] NO_BYTES = {};
public static final int MAX_OUTPUT_CHARS = 512;
// data

View File

@ -31,6 +31,8 @@ import org.eclipse.jetty.util.StringUtil;
*/
public class HttpWriter extends Writer
{
public static final int MAX_OUTPUT_CHARS = 512;
private static final int WRITE_CONV = 0;
private static final int WRITE_ISO1 = 1;
private static final int WRITE_UTF8 = 2;
@ -68,7 +70,7 @@ public class HttpWriter extends Writer
_out._characterEncoding = encoding;
if (_out._bytes==null)
_out._bytes = new ByteArrayOutputStream2(AbstractGenerator.MAX_OUTPUT_CHARS);
_out._bytes = new ByteArrayOutputStream2(MAX_OUTPUT_CHARS);
}
/* ------------------------------------------------------------ */
@ -86,16 +88,16 @@ public class HttpWriter extends Writer
/* ------------------------------------------------------------ */
public void write (String s,int offset, int length) throws IOException
{
while (length > AbstractGenerator.MAX_OUTPUT_CHARS)
while (length > MAX_OUTPUT_CHARS)
{
write(s, offset, AbstractGenerator.MAX_OUTPUT_CHARS);
offset += AbstractGenerator.MAX_OUTPUT_CHARS;
length -= AbstractGenerator.MAX_OUTPUT_CHARS;
write(s, offset, MAX_OUTPUT_CHARS);
offset += MAX_OUTPUT_CHARS;
length -= MAX_OUTPUT_CHARS;
}
if (_out._chars==null)
{
_out._chars = new char[AbstractGenerator.MAX_OUTPUT_CHARS];
_out._chars = new char[MAX_OUTPUT_CHARS];
}
char[] chars = _out._chars;
s.getChars(offset, offset + length, chars, 0);
@ -110,8 +112,8 @@ public class HttpWriter extends Writer
while (length > 0)
{
out._bytes.reset();
int chars = length>AbstractGenerator.MAX_OUTPUT_CHARS?AbstractGenerator.MAX_OUTPUT_CHARS:length;
int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;
switch (_writeMode)
{
case WRITE_CONV:
@ -145,10 +147,10 @@ public class HttpWriter extends Writer
{
byte[] buffer=out._bytes.getBuf();
int bytes=out._bytes.getCount();
if (bytes+chars>buffer.length)
chars=buffer.length-bytes;
for (int i = 0; i < chars; i++)
{
int code = s[offset+i];
@ -158,89 +160,83 @@ public class HttpWriter extends Writer
// 1b
buffer[bytes++]=(byte)(code);
}
else if((code&0xfffff800)==0)
{
// 2b
if (bytes+2>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xc0|(code>>6));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
if (bytes+chars-i-1>buffer.length)
chars-=1;
}
else if((code&0xffff0000)==0)
{
// 3b
if (bytes+3>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xe0|(code>>12));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
if (bytes+chars-i-1>buffer.length)
chars-=2;
}
else if((code&0xff200000)==0)
{
// 4b
if (bytes+4>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xf0|(code>>18));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
if (bytes+chars-i-1>buffer.length)
chars-=3;
}
else if((code&0xf4000000)==0)
{
// 5b
if (bytes+5>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xf8|(code>>24));
buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
if (bytes+chars-i-1>buffer.length)
chars-=4;
}
else if((code&0x80000000)==0)
{
// 6b
if (bytes+6>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xfc|(code>>30));
buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
if (bytes+chars-i-1>buffer.length)
chars-=5;
}
else
{
buffer[bytes++]=(byte)('?');
if((code&0xfffff800)==0)
{
// 2b
if (bytes+2>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xc0|(code>>6));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xffff0000)==0)
{
// 3b
if (bytes+3>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xe0|(code>>12));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xff200000)==0)
{
// 4b
if (bytes+4>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xf0|(code>>18));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0xf4000000)==0)
{
// 5b
if (bytes+5>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xf8|(code>>24));
buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else if((code&0x80000000)==0)
{
// 6b
if (bytes+6>buffer.length)
{
chars=i;
break;
}
buffer[bytes++]=(byte)(0xfc|(code>>30));
buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
buffer[bytes++]=(byte)(0x80|(code&0x3f));
}
else
{
buffer[bytes++]=(byte)('?');
}
if (bytes==buffer.length)
{
chars=i+1;
break;
}
}
}
out._bytes.setCount(bytes);

View File

@ -0,0 +1,118 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import org.eclipse.jetty.http.AbstractGenerator;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.Buffers;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.io.SimpleBuffers;
import org.eclipse.jetty.util.StringUtil;
import junit.framework.TestCase;
public class HttpWriterTest extends TestCase
{
HttpWriter _writer;
ByteArrayBuffer _bytes;
/* ------------------------------------------------------------ */
@Override
protected void setUp() throws Exception
{
_bytes = new ByteArrayBuffer(2048);
Buffers buffers = new SimpleBuffers(new ByteArrayBuffer(1024),new ByteArrayBuffer(1024));
ByteArrayEndPoint endp = new ByteArrayEndPoint();
AbstractGenerator generator = new AbstractGenerator(buffers,endp)
{
@Override
public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
{
}
@Override
public long flushBuffer() throws IOException
{
return 0;
}
@Override
public int prepareUncheckedAddContent() throws IOException
{
return 1024;
}
public void addContent(Buffer content, boolean last) throws IOException
{
_bytes.put(content);
content.clear();
}
public boolean addContent(byte b) throws IOException
{
return false;
}
};
HttpOutput httpOut = new HttpOutput(generator,60000);
_writer = new HttpWriter(httpOut);
}
private void assertArrayEquals(byte[] b1, byte[] b2)
{
assertEquals(b1.length,b2.length);
for (int i=0;i<b1.length;i++)
assertEquals(""+i,b1[i],b2[i]);
}
public void testSimpleUTF8() throws Exception
{
_writer.setCharacterEncoding(StringUtil.__UTF8);
_writer.write("Now is the time");
assertArrayEquals("Now is the time".getBytes(StringUtil.__UTF8),_bytes.asArray());
}
public void testUTF8() throws Exception
{
_writer.setCharacterEncoding(StringUtil.__UTF8);
_writer.write("How now rown cow");
assertArrayEquals("How now rown cow".getBytes(StringUtil.__UTF8),_bytes.asArray());
}
public void testMultiByteOverflowUTF8() throws Exception
{
_writer.setCharacterEncoding(StringUtil.__UTF8);
final String singleByteStr = "a";
final String multiByteDuplicateStr = "";
int remainSize = 1;
int multiByteStrByteLength = multiByteDuplicateStr.getBytes("UTF-8").length;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < HttpWriter.MAX_OUTPUT_CHARS - multiByteStrByteLength; i++) {
sb.append(singleByteStr);
}
sb.append(multiByteDuplicateStr);
for (int i = 0; i < remainSize; i++) {
sb.append(singleByteStr);
}
char[] buf = new char[HttpWriter.MAX_OUTPUT_CHARS * 3];
int length = HttpWriter.MAX_OUTPUT_CHARS - multiByteStrByteLength + remainSize + 1;
sb.toString().getChars(0, length, buf, 0);
_writer.write(buf, 0, length);
assertEquals(sb.toString(),new String(_bytes.asArray(),StringUtil.__UTF8));
}
public void testISO8859() throws Exception
{
_writer.setCharacterEncoding(StringUtil.__ISO_8859_1);
_writer.write("How now rown cow");
assertEquals("How now ?rown cow",new String(_bytes.asArray(),StringUtil.__ISO_8859_1));
}
}