Prevent CPU amplification attack.
This commit is contained in:
parent
a127b08191
commit
6cd61ff149
4 changed files with 60 additions and 4 deletions
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue