CVE-2015-2312 (Other, published 2017-08-09T18:29Z)
Description: value: Sandstorm Cap'n Proto before 0.4.1.1 and 0.5.x before 0.5.1.1 allows remote peers to cause a denial of service (CPU and possibly general resource consumption) via a list with a large number of elements.
Severity: HIGH (CVSS v3 base score: 7.5)
Weakness: CWE-400 \u2014 Uncontrolled Resource Consumption
The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
Repository: https://github.com/capnproto/capnproto
Files changed:
CONTRIBUTORS
c++/src/capnp/arena.h
c++/src/capnp/encoding-test.c++
c++/src/capnp/layout.c++
Commit message: SECURITY: CPU usage amplification attack.

Details: https://github.com/sandstorm-io/capnproto/tree/master/security-advisories/2014-03-02-0-all-cpu-amplification.md


Vulnerable code:
      KJ_REQUIRE(boundsCheck(segment, ptr, ptr +
                     roundBitsUpToWords(ElementCount64(ref->listRef.elementCount()) * step)),
      return ListReader(segment, ptr, ref->listRef.elementCount(), step,

Fixed code:
Ben Laurie <ben@links.org>: discovered and responsibly disclosed security bugs
  KJ_ALWAYS_INLINE(bool amplifiedRead(WordCount virtualAmount));
  // Indicates that the reader should pretend that `virtualAmount` additional data was read even
  // though no actual pointer was traversed. This is used e.g. when reading a struct list pointer
  // where the element sizes are zero -- the sender could set the list size arbitrarily high and
  // cause the receiver to iterate over this list even though the message itself is small, so we
  // need to defend agaisnt DoS attacks based on this.

inline bool SegmentReader::amplifiedRead(WordCount virtualAmount) {
  return readLimiter->canRead(virtualAmount, arena);
}

TEST(Encoding, VoidListAmplification) {
  MallocMessageBuilder builder;
  builder.initRoot<test::TestAnyPointer>().getAnyPointerField().initAs<List<Void>>(1u << 28);

  auto segments = builder.getSegmentsForOutput();
  EXPECT_EQ(1, segments.size());
  EXPECT_LT(segments[0].size(), 16);  // quite small for such a big list!

  SegmentArrayMessageReader reader(builder.getSegmentsForOutput());
  auto root = reader.getRoot<test::TestAnyPointer>().getAnyPointerField();
  EXPECT_NONFATAL_FAILURE(root.getAs<List<TestAllTypes>>());

  MallocMessageBuilder copy;
  EXPECT_NONFATAL_FAILURE(copy.setRoot(reader.getRoot<AnyPointer>()));
}

TEST(Encoding, EmptyStructListAmplification) {
  MallocMessageBuilder builder;
  builder.initRoot<test::TestAnyPointer>().getAnyPointerField()
      .initAs<List<test::TestEmptyStruct>>(1u << 28);

  auto segments = builder.getSegmentsForOutput();
  EXPECT_EQ(1, segments.size());
  EXPECT_LT(segments[0].size(), 16);  // quite small for such a big list!

  SegmentArrayMessageReader reader(builder.getSegmentsForOutput());
  auto root = reader.getRoot<test::TestAnyPointer>().getAnyPointerField();
  EXPECT_NONFATAL_FAILURE(root.getAs<List<TestAllTypes>>());
}

  static KJ_ALWAYS_INLINE(bool amplifiedRead(SegmentReader* segment, WordCount virtualAmount)) {
    // If segment is null, this is an unchecked message, so we don't do read limiter checks.
    return segment == nullptr || segment->amplifiedRead(virtualAmount);
  }

          if (wordsPerElement * (1 * ELEMENTS) == 0 * WORDS) {
            // Watch out for lists of zero-sized structs, which can claim to be arbitrarily large
            // without having sent actual data.
            KJ_REQUIRE(amplifiedRead(srcSegment, elementCount * (1 * WORDS / ELEMENTS)),
                       "Message contains amplified list pointer.") {
              goto useDefault;
            }
          }

          if (elementSize == ElementSize::VOID) {
            // Watch out for lists of void, which can claim to be arbitrarily large without having
            // sent actual data.
            KJ_REQUIRE(amplifiedRead(srcSegment, elementCount * (1 * WORDS / ELEMENTS)),
                       "Message contains amplified list pointer.") {
              goto useDefault;
            }
          }

      if (wordsPerElement * (1 * ELEMENTS) == 0 * WORDS) {
        // Watch out for lists of zero-sized structs, which can claim to be arbitrarily large
        // without having sent actual data.
        KJ_REQUIRE(amplifiedRead(segment, size * (1 * WORDS / ELEMENTS)),
                   "Message contains amplified list pointer.") {
          goto useDefault;
        }
      }

      ElementCount elementCount = ref->listRef.elementCount();
      WordCount wordCount = roundBitsUpToWords(ElementCount64(elementCount) * step);
      KJ_REQUIRE(boundsCheck(segment, ptr, ptr + wordCount),
      if (elementSize == ElementSize::VOID) {
        // Watch out for lists of void, which can claim to be arbitrarily large without having sent
        // actual data.
        KJ_REQUIRE(amplifiedRead(segment, elementCount * (1 * WORDS / ELEMENTS)),
                   "Message contains amplified list pointer.") {
          goto useDefault;
        }
      }

      return ListReader(segment, ptr, elementCount, step,