mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-28 15:50:26 +08:00
Add CheckedMachAddressRange and its test.
TEST=util_test CheckedMachAddressRange.* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/513453002
This commit is contained in:
parent
ff26ea6db9
commit
04842e0ca4
88
util/mac/checked_mach_address_range.cc
Normal file
88
util/mac/checked_mach_address_range.cc
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "util/mac/checked_mach_address_range.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "util/mac/process_reader.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
CheckedMachAddressRange::CheckedMachAddressRange()
|
||||
: range_32_(0, 0), is_64_bit_(false), range_ok_(true) {
|
||||
}
|
||||
|
||||
CheckedMachAddressRange::CheckedMachAddressRange(
|
||||
const ProcessReader* process_reader,
|
||||
mach_vm_address_t base,
|
||||
mach_vm_size_t size) {
|
||||
SetRange(process_reader, base, size);
|
||||
}
|
||||
|
||||
void CheckedMachAddressRange::SetRange(const ProcessReader* process_reader,
|
||||
mach_vm_address_t base,
|
||||
mach_vm_size_t size) {
|
||||
is_64_bit_ = process_reader->Is64Bit();
|
||||
if (is_64_bit_) {
|
||||
range_64_.SetRange(base, size);
|
||||
range_ok_ = true;
|
||||
} else {
|
||||
range_32_.SetRange(base, size);
|
||||
range_ok_ = base::IsValueInRangeForNumericType<uint32_t>(base) &&
|
||||
base::IsValueInRangeForNumericType<uint32_t>(size);
|
||||
}
|
||||
}
|
||||
|
||||
mach_vm_address_t CheckedMachAddressRange::Base() const {
|
||||
return is_64_bit_ ? range_64_.base() : range_32_.base();
|
||||
}
|
||||
|
||||
mach_vm_size_t CheckedMachAddressRange::Size() const {
|
||||
return is_64_bit_ ? range_64_.size() : range_32_.size();
|
||||
}
|
||||
|
||||
mach_vm_address_t CheckedMachAddressRange::End() const {
|
||||
return is_64_bit_ ? range_64_.end() : range_32_.end();
|
||||
}
|
||||
|
||||
bool CheckedMachAddressRange::IsValid() const {
|
||||
return range_ok_ && (is_64_bit_ ? range_64_.IsValid() : range_32_.IsValid());
|
||||
}
|
||||
|
||||
bool CheckedMachAddressRange::ContainsValue(mach_vm_address_t value) const {
|
||||
DCHECK(range_ok_);
|
||||
|
||||
if (is_64_bit_) {
|
||||
return range_64_.ContainsValue(value);
|
||||
}
|
||||
|
||||
if (!base::IsValueInRangeForNumericType<uint32_t>(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return range_32_.ContainsValue(value);
|
||||
}
|
||||
|
||||
bool CheckedMachAddressRange::ContainsRange(
|
||||
const CheckedMachAddressRange& that) const {
|
||||
DCHECK_EQ(is_64_bit_, that.is_64_bit_);
|
||||
DCHECK(range_ok_);
|
||||
DCHECK(that.range_ok_);
|
||||
|
||||
return is_64_bit_ ? range_64_.ContainsRange(that.range_64_)
|
||||
: range_32_.ContainsRange(that.range_32_);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
134
util/mac/checked_mach_address_range.h
Normal file
134
util/mac/checked_mach_address_range.h
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_
|
||||
#define CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include "util/numeric/checked_range.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class ProcessReader;
|
||||
|
||||
//! \brief Ensures that a range, composed of a base and a size, does not
|
||||
//! overflow the pointer type of the process it describes a range in.
|
||||
//!
|
||||
//! This class checks bases of type `mach_vm_address_t` and sizes of type
|
||||
//! `mach_vm_address_t` against a process whose pointer type can be determined
|
||||
//! from its ProcessReader.
|
||||
//!
|
||||
//! Aside from varying the overall range on the basis of a process’ pointer type
|
||||
//! width, this class functions very similarly to CheckedRange.
|
||||
class CheckedMachAddressRange {
|
||||
public:
|
||||
//! \brief Initializes a default range.
|
||||
//!
|
||||
//! The default range has base 0, size 0, and appears to be from a 32-bit
|
||||
//! process.
|
||||
CheckedMachAddressRange();
|
||||
|
||||
//! \brief Initializes a range.
|
||||
//!
|
||||
//! See SetRange().
|
||||
CheckedMachAddressRange(const ProcessReader* process_reader,
|
||||
mach_vm_address_t base,
|
||||
mach_vm_size_t size);
|
||||
|
||||
//! \brief Sets a range’s fields.
|
||||
//!
|
||||
//! \param[in] process_reader The ProcessReader that can read the process that
|
||||
//! \a base is a pointer to.
|
||||
//! \param[in] base The range’s base address.
|
||||
//! \param[in] size The range’s size.
|
||||
void SetRange(const ProcessReader* process_reader,
|
||||
mach_vm_address_t base,
|
||||
mach_vm_size_t size);
|
||||
|
||||
//! \brief The range’s base address.
|
||||
mach_vm_address_t Base() const;
|
||||
|
||||
//! \brief The range’s size.
|
||||
mach_vm_size_t Size() const;
|
||||
|
||||
//! \brief The range’s end address (its base address plus its size).
|
||||
mach_vm_address_t End() const;
|
||||
|
||||
//! \brief Returns the validity of the address range.
|
||||
//!
|
||||
//! \return `true` if the address range is valid, `false` otherwise.
|
||||
//!
|
||||
//! An address range is valid if its size can be converted to the address
|
||||
//! range’s data type without data loss, and if its end (base plus size) can
|
||||
//! be computed without overflowing its data type.
|
||||
bool IsValid() const;
|
||||
|
||||
//! \brief Returns whether the address range contains another address.
|
||||
//!
|
||||
//! \param[in] value The (possibly) contained address.
|
||||
//!
|
||||
//! \return `true` if the address range contains \a value, `false` otherwise.
|
||||
//!
|
||||
//! An address range contains a value if the value is greater than or equal to
|
||||
//! its base address, and less than its end address (base address plus size).
|
||||
//!
|
||||
//! This method must only be called if IsValid() would return `true`.
|
||||
bool ContainsValue(const mach_vm_address_t value) const;
|
||||
|
||||
//! \brief Returns whether the address range contains another address range.
|
||||
//!
|
||||
//! \param[in] that The (possibly) contained address range.
|
||||
//!
|
||||
//! \return `true` if `this` address range, the containing address range,
|
||||
//! contains \a that, the contained address range. `false` otherwise.
|
||||
//!
|
||||
//! An address range contains another address range when the contained address
|
||||
//! range’s base is greater than or equal to the containing address range’s
|
||||
//! base, and the contained address range’s end is less than or equal to the
|
||||
//! containing address range’s end.
|
||||
//!
|
||||
//! This method should only be called on two CheckedMachAddressRange objects
|
||||
//! sharing the same ProcessReader.
|
||||
//!
|
||||
//! This method must only be called if IsValid() would return `true` for both
|
||||
//! CheckedMachAddressRange objects involved.
|
||||
bool ContainsRange(const CheckedMachAddressRange& that) const;
|
||||
|
||||
private:
|
||||
// The field of the union that is expressed is determined by is_64_bit_.
|
||||
union {
|
||||
CheckedRange<uint32_t> range_32_;
|
||||
CheckedRange<uint64_t> range_64_;
|
||||
};
|
||||
|
||||
// Determines which field of the union is expressed.
|
||||
bool is_64_bit_;
|
||||
|
||||
// Whether the base and size were valid for their data type when set. This is
|
||||
// always true when is_64_bit_ is true because the underlying data types are
|
||||
// 64 bits wide and there is no possibility for range and size to overflow.
|
||||
// When is_64_bit_ is false, range_ok_ will be false if SetRange() was passed
|
||||
// a base or size that overflowed the underlying 32-bit data type. This field
|
||||
// is necessary because the interface exposes mach_vm_address_t and
|
||||
// mach_vm_size_t uniformly, but these types are too wide for the underlying
|
||||
// pointer and size types in 32-bit processes.
|
||||
bool range_ok_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CheckedMachAddressRange);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_MAC_CHECKED_MACH_ADDRESS_RANGE_H_
|
256
util/mac/checked_mach_address_range_test.cc
Normal file
256
util/mac/checked_mach_address_range_test.cc
Normal file
@ -0,0 +1,256 @@
|
||||
// Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "util/mac/checked_mach_address_range.h"
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "util/mac/process_reader.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace crashpad;
|
||||
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
const bool kValid64Invalid32 = true;
|
||||
#else
|
||||
const bool kValid64Invalid32 = false;
|
||||
#endif
|
||||
|
||||
TEST(CheckedMachAddressRange, IsValid) {
|
||||
const struct TestData {
|
||||
mach_vm_address_t base;
|
||||
mach_vm_size_t size;
|
||||
bool valid;
|
||||
} kTestData[] = {
|
||||
{0, 0, true},
|
||||
{0, 1, true},
|
||||
{0, 2, true},
|
||||
{0, 0x7fffffff, true},
|
||||
{0, 0x80000000, true},
|
||||
{0, 0xfffffffe, true},
|
||||
{0, 0xffffffff, true},
|
||||
{0, 0xffffffffffffffff, kValid64Invalid32},
|
||||
{1, 0, true},
|
||||
{1, 1, true},
|
||||
{1, 2, true},
|
||||
{1, 0x7fffffff, true},
|
||||
{1, 0x80000000, true},
|
||||
{1, 0xfffffffe, true},
|
||||
{1, 0xffffffff, kValid64Invalid32},
|
||||
{1, 0xfffffffffffffffe, kValid64Invalid32},
|
||||
{1, 0xffffffffffffffff, false},
|
||||
{0x7fffffff, 0, true},
|
||||
{0x7fffffff, 1, true},
|
||||
{0x7fffffff, 2, true},
|
||||
{0x7fffffff, 0x7fffffff, true},
|
||||
{0x7fffffff, 0x80000000, true},
|
||||
{0x7fffffff, 0xfffffffe, kValid64Invalid32},
|
||||
{0x7fffffff, 0xffffffff, kValid64Invalid32},
|
||||
{0x80000000, 0, true},
|
||||
{0x80000000, 1, true},
|
||||
{0x80000000, 2, true},
|
||||
{0x80000000, 0x7fffffff, true},
|
||||
{0x80000000, 0x80000000, kValid64Invalid32},
|
||||
{0x80000000, 0xfffffffe, kValid64Invalid32},
|
||||
{0x80000000, 0xffffffff, kValid64Invalid32},
|
||||
{0xfffffffe, 0, true},
|
||||
{0xfffffffe, 1, true},
|
||||
{0xfffffffe, 2, kValid64Invalid32},
|
||||
{0xfffffffe, 0x7fffffff, kValid64Invalid32},
|
||||
{0xfffffffe, 0x80000000, kValid64Invalid32},
|
||||
{0xfffffffe, 0xfffffffe, kValid64Invalid32},
|
||||
{0xfffffffe, 0xffffffff, kValid64Invalid32},
|
||||
{0xffffffff, 0, true},
|
||||
{0xffffffff, 1, kValid64Invalid32},
|
||||
{0xffffffff, 2, kValid64Invalid32},
|
||||
{0xffffffff, 0x7fffffff, kValid64Invalid32},
|
||||
{0xffffffff, 0x80000000, kValid64Invalid32},
|
||||
{0xffffffff, 0xfffffffe, kValid64Invalid32},
|
||||
{0xffffffff, 0xffffffff, kValid64Invalid32},
|
||||
{0x7fffffffffffffff, 0, kValid64Invalid32},
|
||||
{0x7fffffffffffffff, 1, kValid64Invalid32},
|
||||
{0x7fffffffffffffff, 2, kValid64Invalid32},
|
||||
{0x7fffffffffffffff, 0x7fffffffffffffff, kValid64Invalid32},
|
||||
{0x7fffffffffffffff, 0x8000000000000000, kValid64Invalid32},
|
||||
{0x7fffffffffffffff, 0x8000000000000001, false},
|
||||
{0x7fffffffffffffff, 0xfffffffffffffffe, false},
|
||||
{0x7fffffffffffffff, 0xffffffffffffffff, false},
|
||||
{0x8000000000000000, 0, kValid64Invalid32},
|
||||
{0x8000000000000000, 1, kValid64Invalid32},
|
||||
{0x8000000000000000, 2, kValid64Invalid32},
|
||||
{0x8000000000000000, 0x7fffffffffffffff, kValid64Invalid32},
|
||||
{0x8000000000000000, 0x8000000000000000, false},
|
||||
{0x8000000000000000, 0x8000000000000001, false},
|
||||
{0x8000000000000000, 0xfffffffffffffffe, false},
|
||||
{0x8000000000000000, 0xffffffffffffffff, false},
|
||||
{0xfffffffffffffffe, 0, kValid64Invalid32},
|
||||
{0xfffffffffffffffe, 1, kValid64Invalid32},
|
||||
{0xfffffffffffffffe, 2, false},
|
||||
{0xffffffffffffffff, 0, kValid64Invalid32},
|
||||
{0xffffffffffffffff, 1, false},
|
||||
};
|
||||
|
||||
ProcessReader process_reader;
|
||||
ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
|
||||
|
||||
for (size_t index = 0; index < arraysize(kTestData); ++index) {
|
||||
const TestData& testcase = kTestData[index];
|
||||
SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx",
|
||||
index,
|
||||
testcase.base,
|
||||
testcase.size));
|
||||
|
||||
CheckedMachAddressRange range(
|
||||
&process_reader, testcase.base, testcase.size);
|
||||
EXPECT_EQ(testcase.valid, range.IsValid());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CheckedMachAddressRange, ContainsValue) {
|
||||
const struct TestData {
|
||||
mach_vm_address_t value;
|
||||
bool valid;
|
||||
} kTestData[] = {
|
||||
{0, false},
|
||||
{1, false},
|
||||
{0x1fff, false},
|
||||
{0x2000, true},
|
||||
{0x2001, true},
|
||||
{0x2ffe, true},
|
||||
{0x2fff, true},
|
||||
{0x3000, false},
|
||||
{0x3001, false},
|
||||
{0x7fffffff, false},
|
||||
{0x80000000, false},
|
||||
{0x80000001, false},
|
||||
{0x80001fff, false},
|
||||
{0x80002000, false},
|
||||
{0x80002001, false},
|
||||
{0x80002ffe, false},
|
||||
{0x80002fff, false},
|
||||
{0x80003000, false},
|
||||
{0x80003001, false},
|
||||
{0xffffcfff, false},
|
||||
{0xffffdfff, false},
|
||||
{0xffffefff, false},
|
||||
{0xffffffff, false},
|
||||
{0x100000000, false},
|
||||
{0xffffffffffffffff, false},
|
||||
};
|
||||
|
||||
ProcessReader process_reader;
|
||||
ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
|
||||
|
||||
CheckedMachAddressRange parent_range(&process_reader, 0x2000, 0x1000);
|
||||
ASSERT_TRUE(parent_range.IsValid());
|
||||
|
||||
for (size_t index = 0; index < arraysize(kTestData); ++index) {
|
||||
const TestData& testcase = kTestData[index];
|
||||
SCOPED_TRACE(
|
||||
base::StringPrintf("index %zu, value 0x%llx", index, testcase.value));
|
||||
|
||||
EXPECT_EQ(testcase.valid, parent_range.ContainsValue(testcase.value));
|
||||
}
|
||||
|
||||
CheckedMachAddressRange parent_range_64(&process_reader, 0x100000000, 0x1000);
|
||||
ASSERT_EQ(kValid64Invalid32, parent_range_64.IsValid());
|
||||
if (parent_range_64.IsValid()) {
|
||||
EXPECT_FALSE(parent_range_64.ContainsValue(0xffffffff));
|
||||
EXPECT_TRUE(parent_range_64.ContainsValue(0x100000000));
|
||||
EXPECT_TRUE(parent_range_64.ContainsValue(0x100000001));
|
||||
EXPECT_TRUE(parent_range_64.ContainsValue(0x100000fff));
|
||||
EXPECT_FALSE(parent_range_64.ContainsValue(0x100001000));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CheckedMachAddressRange, ContainsRange) {
|
||||
const struct TestData {
|
||||
mach_vm_address_t base;
|
||||
mach_vm_size_t size;
|
||||
bool valid;
|
||||
} kTestData[] = {
|
||||
{0, 0, false},
|
||||
{0, 1, false},
|
||||
{0x2000, 0x1000, true},
|
||||
{0, 0x2000, false},
|
||||
{0x3000, 0x1000, false},
|
||||
{0x1800, 0x1000, false},
|
||||
{0x2800, 0x1000, false},
|
||||
{0x2000, 0x800, true},
|
||||
{0x2800, 0x800, true},
|
||||
{0x2400, 0x800, true},
|
||||
{0x2800, 0, true},
|
||||
{0x2000, 0xffffdfff, false},
|
||||
{0x2800, 0xffffd7ff, false},
|
||||
{0x3000, 0xffffcfff, false},
|
||||
{0xfffffffe, 1, false},
|
||||
{0xffffffff, 0, false},
|
||||
{0x1fff, 0, false},
|
||||
{0x2000, 0, true},
|
||||
{0x2001, 0, true},
|
||||
{0x2fff, 0, true},
|
||||
{0x3000, 0, true},
|
||||
{0x3001, 0, false},
|
||||
{0x1fff, 1, false},
|
||||
{0x2000, 1, true},
|
||||
{0x2001, 1, true},
|
||||
{0x2fff, 1, true},
|
||||
{0x3000, 1, false},
|
||||
{0x3001, 1, false},
|
||||
};
|
||||
|
||||
ProcessReader process_reader;
|
||||
ASSERT_TRUE(process_reader.Initialize(mach_task_self()));
|
||||
|
||||
CheckedMachAddressRange parent_range(&process_reader, 0x2000, 0x1000);
|
||||
ASSERT_TRUE(parent_range.IsValid());
|
||||
|
||||
for (size_t index = 0; index < arraysize(kTestData); ++index) {
|
||||
const TestData& testcase = kTestData[index];
|
||||
SCOPED_TRACE(base::StringPrintf("index %zu, base 0x%llx, size 0x%llx",
|
||||
index,
|
||||
testcase.base,
|
||||
testcase.size));
|
||||
|
||||
CheckedMachAddressRange child_range(
|
||||
&process_reader, testcase.base, testcase.size);
|
||||
ASSERT_TRUE(child_range.IsValid());
|
||||
EXPECT_EQ(testcase.valid, parent_range.ContainsRange(child_range));
|
||||
}
|
||||
|
||||
CheckedMachAddressRange parent_range_64(&process_reader, 0x100000000, 0x1000);
|
||||
ASSERT_EQ(kValid64Invalid32, parent_range_64.IsValid());
|
||||
if (parent_range_64.IsValid()) {
|
||||
CheckedMachAddressRange child_range_64(&process_reader, 0xffffffff, 2);
|
||||
EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));
|
||||
|
||||
child_range_64.SetRange(&process_reader, 0x100000000, 2);
|
||||
EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));
|
||||
|
||||
child_range_64.SetRange(&process_reader, 0x100000ffe, 2);
|
||||
EXPECT_TRUE(parent_range_64.ContainsRange(child_range_64));
|
||||
|
||||
child_range_64.SetRange(&process_reader, 0x100000fff, 2);
|
||||
EXPECT_FALSE(parent_range_64.ContainsRange(child_range_64));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
@ -32,6 +32,8 @@
|
||||
'file/file_writer.h',
|
||||
'file/string_file_writer.cc',
|
||||
'file/string_file_writer.h',
|
||||
'mac/checked_mach_address_range.cc',
|
||||
'mac/checked_mach_address_range.h',
|
||||
'mac/launchd.h',
|
||||
'mac/launchd.mm',
|
||||
'mac/mac_util.cc',
|
||||
@ -118,6 +120,7 @@
|
||||
],
|
||||
'sources': [
|
||||
'file/string_file_writer_test.cc',
|
||||
'mac/checked_mach_address_range_test.cc',
|
||||
'mac/launchd_test.mm',
|
||||
'mac/mac_util_test.mm',
|
||||
'mac/process_reader_test.cc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user