diff --git a/compiler/src/test/scala/org/capnproto/EncodingTest.scala b/compiler/src/test/scala/org/capnproto/EncodingTest.scala index 6b2c3c8..88f766a 100644 --- a/compiler/src/test/scala/org/capnproto/EncodingTest.scala +++ b/compiler/src/test/scala/org/capnproto/EncodingTest.scala @@ -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); diff --git a/runtime/src/main/java/org/capnproto/BuilderArena.java b/runtime/src/main/java/org/capnproto/BuilderArena.java index f8f0864..4c6a2d8 100644 --- a/runtime/src/main/java/org/capnproto/BuilderArena.java +++ b/runtime/src/main/java/org/capnproto/BuilderArena.java @@ -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; diff --git a/runtime/src/main/java/org/capnproto/ReaderArena.java b/runtime/src/main/java/org/capnproto/ReaderArena.java index 94aa3af..3d7fd98 100644 --- a/runtime/src/main/java/org/capnproto/ReaderArena.java +++ b/runtime/src/main/java/org/capnproto/ReaderArena.java @@ -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; } diff --git a/runtime/src/main/java/org/capnproto/WireHelpers.java b/runtime/src/main/java/org/capnproto/WireHelpers.java index 22a1871..25189de 100644 --- a/runtime/src/main/java/org/capnproto/WireHelpers.java +++ b/runtime/src/main/java/org/capnproto/WireHelpers.java @@ -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