| Index: mojo/public/cpp/bindings/lib/bounds_checker.cc
|
| diff --git a/mojo/public/cpp/bindings/lib/bounds_checker.cc b/mojo/public/cpp/bindings/lib/bounds_checker.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..493ce72bc97690e78c15766ecbafecfac3bc2fe9
|
| --- /dev/null
|
| +++ b/mojo/public/cpp/bindings/lib/bounds_checker.cc
|
| @@ -0,0 +1,140 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "mojo/public/cpp/bindings/lib/bounds_checker.h"
|
| +
|
| +#include <assert.h>
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "mojo/public/cpp/bindings/lib/bindings_serialization.h"
|
| +#include "mojo/public/cpp/system/core.h"
|
| +
|
| +namespace mojo {
|
| +namespace internal {
|
| +namespace {
|
| +
|
| +bool IsClaimedRangesVectorValid(const std::vector<uintptr_t>& claimed_ranges) {
|
| + if (claimed_ranges.empty())
|
| + return true;
|
| +
|
| + if (claimed_ranges.size() % 2)
|
| + return false;
|
| +
|
| + for (std::vector<uintptr_t>::const_iterator iter = claimed_ranges.begin() + 1;
|
| + iter != claimed_ranges.end();
|
| + ++iter) {
|
| + if (*(iter - 1) >= *iter)
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +BoundsChecker::BoundsChecker(const void* data, uint32_t data_num_bytes,
|
| + size_t num_handles)
|
| + : data_begin_(reinterpret_cast<uintptr_t>(data)),
|
| + data_end_(data_begin_ + data_num_bytes),
|
| + claimed_handles_(num_handles, false) {
|
| + if (data_end_ < data_begin_) {
|
| + // The calculation of |data_end_| overflowed.
|
| + // It shouldn't happen but if it does, set the range to empty so
|
| + // IsWithinBounds() and ClaimMemory() always fail.
|
| + assert(false); // Not reached.
|
| + data_end_ = data_begin_;
|
| + }
|
| +}
|
| +
|
| +BoundsChecker::~BoundsChecker() {
|
| + assert(IsClaimedRangesVectorValid(claimed_ranges_));
|
| +}
|
| +
|
| +bool BoundsChecker::ClaimMemory(const void* position, uint32_t num_bytes) {
|
| + uintptr_t begin = reinterpret_cast<uintptr_t>(position);
|
| + uintptr_t end = begin + num_bytes;
|
| +
|
| + if (!InternalIsWithinBounds(begin, end))
|
| + return false;
|
| +
|
| + if (claimed_ranges_.empty() || begin >= claimed_ranges_.back()) {
|
| + // This optimization is significant: if the layout of objects in an incoming
|
| + // message are in the same order as our validation code traverses the
|
| + // message, we will always hit this code path.
|
| + InsertOrMergeClaimedRange(claimed_ranges_.end(), begin, end);
|
| + return true;
|
| + }
|
| +
|
| + // Find |iter| which pointers to the first element that is greater than
|
| + // |begin|. (The previous if-block guarantees that such an element must exist
|
| + // if we reach this point.)
|
| + std::vector<uintptr_t>::iterator iter = std::upper_bound(
|
| + claimed_ranges_.begin(), claimed_ranges_.end(), begin);
|
| + assert(iter != claimed_ranges_.end());
|
| +
|
| + // If the corresponding index of |iter| is an odd number, |begin| lies in a
|
| + // claimed range.
|
| + if ((iter - claimed_ranges_.begin()) % 2)
|
| + return false;
|
| + // If |end| is bigger than |*iter| while |begin| is smaller, then [begin, end)
|
| + // overlaps at least one claimed range.
|
| + if (end > *iter)
|
| + return false;
|
| +
|
| + InsertOrMergeClaimedRange(iter, begin, end);
|
| + return true;
|
| +}
|
| +
|
| +bool BoundsChecker::ClaimHandle(const Handle& encoded_handle) {
|
| + uint32_t index = encoded_handle.value();
|
| + if (index == kEncodedInvalidHandleValue)
|
| + return true;
|
| +
|
| + if (index >= claimed_handles_.size() || claimed_handles_[index])
|
| + return false;
|
| +
|
| + claimed_handles_[index] = true;
|
| + return true;
|
| +}
|
| +
|
| +bool BoundsChecker::IsWithinBounds(const void* position,
|
| + uint32_t num_bytes) const {
|
| + uintptr_t begin = reinterpret_cast<uintptr_t>(position);
|
| + uintptr_t end = begin + num_bytes;
|
| +
|
| + return InternalIsWithinBounds(begin, end);
|
| +}
|
| +
|
| +const std::vector<uintptr_t>&
|
| + BoundsChecker::GetClaimedRangesForTesting() const {
|
| + return claimed_ranges_;
|
| +}
|
| +
|
| +void BoundsChecker::InsertOrMergeClaimedRange(
|
| + std::vector<uintptr_t>::iterator pos, uintptr_t begin, uintptr_t end) {
|
| + bool merge_previous = pos != claimed_ranges_.begin() &&
|
| + *(pos - 1) == begin;
|
| + bool merge_next = pos != claimed_ranges_.end() &&
|
| + *pos == end;
|
| +
|
| + if (merge_previous && merge_next) {
|
| + *(pos - 1) = *(pos + 1);
|
| + claimed_ranges_.erase(pos, pos + 2);
|
| + } else if (merge_previous) {
|
| + *(pos - 1) = end;
|
| + } else if (merge_next) {
|
| + *pos = begin;
|
| + } else {
|
| + uintptr_t pair[2] = {begin, end};
|
| + claimed_ranges_.insert(pos, pair, pair + 2);
|
| + }
|
| +}
|
| +
|
| +bool BoundsChecker::InternalIsWithinBounds(uintptr_t begin,
|
| + uintptr_t end) const {
|
| + return end > begin && begin >= data_begin_ && end <= data_end_;
|
| +}
|
| +
|
| +} // namespace internal
|
| +} // namespace mojo
|
|
|