Commit message: SECURITY: Prevent compiler from eliding bounds checks.

Details: https://github.com/sandstorm-io/capnproto/blob/master/security-advisories/2017-04-17-0-apple-32bit-elides-bounds-check.md
CVE: CVE-2017-7892
CWE: CWE-20
Vulnerability description: Sandstorm Cap'n Proto before 0.5.3.1 allows remote crashes related to a compiler optimization. A remote attacker can trigger a segfault in a 32-bit libcapnp application because Cap'n Proto relies on pointer arithmetic calculations that overflow. An example compiler with optimization that elides a bounds check in such calculations is Apple LLVM version 8.1.0 (clang-802.0.41). The attack vector is a crafted far pointer within a message.

Project: capnproto
File: arena.h

inline SegmentWordCount SegmentReader::getSize() {
  return assumeBits<SEGMENT_WORD_COUNT_BITS>(ptr.size()) * WORDS;
}

Project: capnproto
File: arena.h

inline ReadLimiter::ReadLimiter(WordCount64 limit): limit(unbound(limit / WORDS)) {}

Project: capnproto
File: arena.h

inline SegmentReader::SegmentReader(Arena* arena, SegmentId id, const word* ptr,
                                    SegmentWordCount size, ReadLimiter* readLimiter)
    : arena(arena), id(id), ptr(kj::arrayPtr(ptr, unbound(size / WORDS))),
      readLimiter(readLimiter) {}

Project: capnproto
File: arena.h

inline SegmentWordCount SegmentReader::getOffsetTo(const word* ptr) {
  KJ_IREQUIRE(this->ptr.begin() <= ptr && ptr <= this->ptr.end());
  return intervalLength(this->ptr.begin(), ptr, MAX_SEGMENT_WORDS);
}

Project: capnproto
File: arena.h

inline bool ReadLimiter::canRead(WordCount64 amount, Arena* arena) {
  // Be careful not to store an underflowed value into `limit`, even if multiple threads are
  // decrementing it.
  uint64_t current = limit;
  if (KJ_UNLIKELY(unbound(amount / WORDS) > current)) {
    arena->reportReadLimitReached();
    return false;
  } else {
    limit = current - unbound(amount / WORDS);
    return true;
  }
}

Project: capnproto
File: arena.h

inline word* SegmentBuilder::getPtrUnchecked(SegmentWordCount offset) {
  return const_cast<word*>(ptr.begin() + offset);
}

Project: capnproto
File: arena.h

  inline CapTableBuilder* getLocalCapTable() {
    // Return a CapTableBuilder that merely implements local loopback. That is, you can set
    // capabilities, then read the same capabilities back, but there is no intent ever to transmit
    // these capabilities. A MessageBuilder that isn't imbued with some other CapTable uses this
    // by default.
    //
    // TODO(cleanup): It's sort of a hack that this exists. In theory, perhaps, unimbued
    //   MessageBuilders should throw exceptions on any attempt to access capability fields, like
    //   unimbued MessageReaders do. However, lots of code exists which uses MallocMessageBuilder
    //   as a temporary holder for data to be copied in and out (without being serialized), and it
    //   is expected that such data can include capabilities, which is admittedly reasonable.
    //   Therefore, all MessageBuilders must have a cap table by default. Arguably we should
    //   deprecate this usage and instead define a new helper type for this exact purpose.

    return &localCapTable;
  }

Project: capnproto
File: arena.h

inline SegmentBuilder::SegmentBuilder(
    BuilderArena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
    ReadLimiter* readLimiter)
    : SegmentReader(arena, id, ptr, size, readLimiter),
      // const_cast is safe here because the member won't ever be dereferenced because it appears
      // to point to the end of the segment anyway.
      pos(const_cast<word*>(ptr + size)), readOnly(true) {}

Project: capnproto
File: arena.h

inline void ReadLimiter::reset(WordCount64 limit) { this->limit = unbound(limit / WORDS); }

Project: capnproto
File: arena.h

inline SegmentBuilder::SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr),
                                      ReadLimiter* readLimiter)
    : SegmentReader(arena, id, nullptr, ZERO * WORDS, readLimiter),
      pos(nullptr), readOnly(false) {}

Project: capnproto
File: arena.h

inline bool SegmentBuilder::tryExtend(word* from, word* to) {
  // Careful about overflow.
  if (pos == from && to <= ptr.end() && to >= from) {
    pos = to;
    return true;
  } else {
    return false;
  }
}

Project: capnproto
File: arena.h

inline bool SegmentReader::containsInterval(const void* from, const void* to) {
  uintptr_t start = reinterpret_cast<uintptr_t>(from) - reinterpret_cast<uintptr_t>(ptr.begin());
  uintptr_t end = reinterpret_cast<uintptr_t>(to) - reinterpret_cast<uintptr_t>(ptr.begin());
  uintptr_t bound = ptr.size() * sizeof(capnp::word);

  return start <= bound && end <= bound && start <= end &&
      readLimiter->canRead(
          intervalLength(reinterpret_cast<const byte*>(from),
                         reinterpret_cast<const byte*>(to),
                         MAX_SEGMENT_WORDS * BYTES_PER_WORD)
              / BYTES_PER_WORD,
          arena);
}

Project: capnproto
File: arena.h

inline word* SegmentBuilder::allocate(SegmentWordCount amount) {
  if (intervalLength(pos, ptr.end(), MAX_SEGMENT_WORDS) < amount) {
    // Not enough space in the segment for this allocation.
    return nullptr;
  } else {
    // Success.
    word* result = pos;
    pos = pos + amount;
    return result;
  }
}

Project: capnproto
File: arena.h

inline SegmentBuilder::SegmentBuilder(
    BuilderArena* arena, SegmentId id, word* ptr, SegmentWordCount size,
    ReadLimiter* readLimiter, SegmentWordCount wordsUsed)
    : SegmentReader(arena, id, ptr, size, readLimiter),
      pos(ptr + wordsUsed), readOnly(false) {}

Project: capnproto
File: arena.h

inline void SegmentBuilder::reset() {
  word* start = getPtrUnchecked(ZERO * WORDS);
  memset(start, 0, (pos - start) * sizeof(word));
  pos = start;
}