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:
|
// to debug, do this:
|
||||||
//Serialize.write((new java.io.FileOutputStream("/Users/dwrensha/Desktop/test.dat")).getChannel(),
|
//Serialize.write((new java.io.FileOutputStream("/Users/dwrensha/Desktop/test.dat")).getChannel(),
|
||||||
// message);
|
// message);
|
||||||
|
|
|
@ -114,6 +114,7 @@ public final class BuilderArena implements Arena {
|
||||||
segment.buffer.rewind();
|
segment.buffer.rewind();
|
||||||
ByteBuffer slice = segment.buffer.slice();
|
ByteBuffer slice = segment.buffer.slice();
|
||||||
slice.limit(segment.currentSize() * Constants.BYTES_PER_WORD);
|
slice.limit(segment.currentSize() * Constants.BYTES_PER_WORD);
|
||||||
|
slice.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
result[ii] = slice;
|
result[ii] = slice;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -44,9 +44,8 @@ public final class ReaderArena implements Arena {
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void checkReadLimit(int numBytes) {
|
public final void checkReadLimit(int numBytes) {
|
||||||
// TODO worry about thread safety?
|
|
||||||
if (numBytes > limit) {
|
if (numBytes > limit) {
|
||||||
return;
|
throw new DecodeException("Read limit exceeded.");
|
||||||
} else {
|
} else {
|
||||||
limit -= numBytes;
|
limit -= numBytes;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1071,6 +1071,13 @@ final class WireHelpers {
|
||||||
if (wordsPerElement * elementCount > wordCount) {
|
if (wordsPerElement * elementCount > wordCount) {
|
||||||
throw new DecodeException("INLINE_COMPOSITE list's elements overrun its word count.");
|
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,
|
return setListPointer(dstSegment, dstOffset,
|
||||||
new ListReader(resolved.segment,
|
new ListReader(resolved.segment,
|
||||||
ptr * Constants.BYTES_PER_WORD,
|
ptr * Constants.BYTES_PER_WORD,
|
||||||
|
@ -1088,6 +1095,12 @@ final class WireHelpers {
|
||||||
|
|
||||||
resolved.segment.arena.checkReadLimit(wordCount);
|
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,
|
return setListPointer(dstSegment, dstOffset,
|
||||||
new ListReader(resolved.segment,
|
new ListReader(resolved.segment,
|
||||||
resolved.ptr * Constants.BYTES_PER_WORD,
|
resolved.ptr * Constants.BYTES_PER_WORD,
|
||||||
|
@ -1134,7 +1147,8 @@ final class WireHelpers {
|
||||||
|
|
||||||
FollowFarsResult resolved = followFars(ref, refTarget, segment);
|
FollowFarsResult resolved = followFars(ref, refTarget, segment);
|
||||||
|
|
||||||
switch (ListPointer.elementSize(resolved.ref)) {
|
byte elementSize = ListPointer.elementSize(resolved.ref);
|
||||||
|
switch (elementSize) {
|
||||||
case ElementSize.INLINE_COMPOSITE : {
|
case ElementSize.INLINE_COMPOSITE : {
|
||||||
int wordCount = ListPointer.inlineCompositeWordCount(resolved.ref);
|
int wordCount = ListPointer.inlineCompositeWordCount(resolved.ref);
|
||||||
|
|
||||||
|
@ -1149,6 +1163,12 @@ final class WireHelpers {
|
||||||
|
|
||||||
// TODO check that elemements do not overrun word count
|
// 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
|
// TODO check whether the size is compatible
|
||||||
|
|
||||||
return factory.constructReader(resolved.segment,
|
return factory.constructReader(resolved.segment,
|
||||||
|
@ -1166,10 +1186,17 @@ final class WireHelpers {
|
||||||
//# such structs.
|
//# such structs.
|
||||||
int dataSize = ElementSize.dataBitsPerElement(ListPointer.elementSize(resolved.ref));
|
int dataSize = ElementSize.dataBitsPerElement(ListPointer.elementSize(resolved.ref));
|
||||||
int pointerCount = ElementSize.pointersPerElement(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;
|
int step = dataSize + pointerCount * Constants.BITS_PER_POINTER;
|
||||||
|
|
||||||
resolved.segment.arena.checkReadLimit(
|
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
|
//# Verify that the elements are at least as large as
|
||||||
//# the expected type. Note that if we expected
|
//# the expected type. Note that if we expected
|
||||||
|
|
Loading…
Reference in a new issue