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:
parent
17b94b6dc0
commit
0f01841df1
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 Brown cow");
|
||||
assertArrayEquals("How now Brown cow".getBytes(StringUtil.__UTF8),_bytes.asArray());
|
||||
}
|
||||
|
||||
public void testMultiByteOverflowUTF8() throws Exception
|
||||
{
|
||||
_writer.setCharacterEncoding(StringUtil.__UTF8);
|
||||
final String singleByteStr = "a";
|
||||
final String multiByteDuplicateStr = "B";
|
||||
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 Brown cow");
|
||||
assertEquals("How now ?rown cow",new String(_bytes.asArray(),StringUtil.__ISO_8859_1));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue