Prevent CPU amplification attack.

This commit is contained in:
David Renshaw 2015-03-03 10:46:42 -05:00
parent a127b08191
commit 6cd61ff149
4 changed files with 60 additions and 4 deletions

View file

@ -379,6 +379,35 @@ class EncodingSuite extends FunSuite {
}
}
test("VoidListAmplification") {
val builder = new MessageBuilder();
builder.initRoot(TestAnyPointer.factory).getAnyPointerField().initAs(PrimitiveList.Void.factory, 1 << 28);
val segments = builder.getSegmentsForOutput();
segments.length should equal (1);
val reader = new MessageReader(segments, ReaderOptions.DEFAULT_READER_OPTIONS);
val root = reader.getRoot(TestAnyPointer.factory);
a [DecodeException] should be thrownBy
root.getAnyPointerField().getAs(new StructList.Factory(TestAllTypes.factory));
}
test("EmptyStructListAmplification") {
val builder = new MessageBuilder();
builder.initRoot(TestAnyPointer.factory).getAnyPointerField()
.initAs(new StructList.Factory(TestEmptyStruct.factory), (1 << 29) - 1);
val segments = builder.getSegmentsForOutput();
segments.length should equal (1);
val reader = new MessageReader(segments, ReaderOptions.DEFAULT_READER_OPTIONS);
val root = reader.getRoot(TestAnyPointer.factory);
a [DecodeException] should be thrownBy
root.getAnyPointerField().getAs(new StructList.Factory(TestAllTypes.factory));
}
// to debug, do this:
//Serialize.write((new java.io.FileOutputStream("/Users/dwrensha/Desktop/test.dat")).getChannel(),
// message);

View file

@ -114,6 +114,7 @@ public final class BuilderArena implements Arena {
segment.buffer.rewind();
ByteBuffer slice = segment.buffer.slice();
slice.limit(segment.currentSize() * Constants.BYTES_PER_WORD);
slice.order(ByteOrder.LITTLE_ENDIAN);
result[ii] = slice;
}
return result;

View file

@ -44,9 +44,8 @@ public final class ReaderArena implements Arena {
}
public final void checkReadLimit(int numBytes) {
// TODO worry about thread safety?
if (numBytes > limit) {
return;
throw new DecodeException("Read limit exceeded.");
} else {
limit -= numBytes;
}

View file

@ -1071,6 +1071,13 @@ final class WireHelpers {
if (wordsPerElement * elementCount > wordCount) {
throw new DecodeException("INLINE_COMPOSITE list's elements overrun its word count.");
}
if (wordsPerElement == 0) {
// Watch out for lists of zero-sized structs, which can claim to be arbitrarily
// large without having sent actual data.
resolved.segment.arena.checkReadLimit(elementCount);
}
return setListPointer(dstSegment, dstOffset,
new ListReader(resolved.segment,
ptr * Constants.BYTES_PER_WORD,
@ -1088,6 +1095,12 @@ final class WireHelpers {
resolved.segment.arena.checkReadLimit(wordCount);
if (elementSize == ElementSize.VOID) {
// Watch out for lists of void, which can claim to be arbitrarily large without
// having sent actual data.
resolved.segment.arena.checkReadLimit(elementCount);
}
return setListPointer(dstSegment, dstOffset,
new ListReader(resolved.segment,
resolved.ptr * Constants.BYTES_PER_WORD,
@ -1134,7 +1147,8 @@ final class WireHelpers {
FollowFarsResult resolved = followFars(ref, refTarget, segment);
switch (ListPointer.elementSize(resolved.ref)) {
byte elementSize = ListPointer.elementSize(resolved.ref);
switch (elementSize) {
case ElementSize.INLINE_COMPOSITE : {
int wordCount = ListPointer.inlineCompositeWordCount(resolved.ref);
@ -1149,6 +1163,12 @@ final class WireHelpers {
// TODO check that elemements do not overrun word count
if (wordsPerElement == 0) {
// Watch out for lists of zero-sized structs, which can claim to be arbitrarily
// large without having sent actual data.
resolved.segment.arena.checkReadLimit(size);
}
// TODO check whether the size is compatible
return factory.constructReader(resolved.segment,
@ -1166,10 +1186,17 @@ final class WireHelpers {
//# such structs.
int dataSize = ElementSize.dataBitsPerElement(ListPointer.elementSize(resolved.ref));
int pointerCount = ElementSize.pointersPerElement(ListPointer.elementSize(resolved.ref));
int elementCount = ListPointer.elementCount(resolved.ref);
int step = dataSize + pointerCount * Constants.BITS_PER_POINTER;
resolved.segment.arena.checkReadLimit(
roundBitsUpToWords(ListPointer.elementCount(resolved.ref) * step));
roundBitsUpToWords(elementCount * step));
if (elementSize == ElementSize.VOID) {
// Watch out for lists of void, which can claim to be arbitrarily large without
// having sent actual data.
resolved.segment.arena.checkReadLimit(elementCount);
}
//# Verify that the elements are at least as large as
//# the expected type. Note that if we expected