mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
[ios] New class ScopedVMMap
This CL introduces a new class ScopedVMMap, a fork of ScopedVMRead which maps the memory using vm_remap() instead of reading it. This is useful for Annotations which use ScopedSpinGuard to protect reads from simultaneous writes; the in-process intermediate dump handler can try to take the spin guard when reading such an Annotation and skip reading it if it the spin guard could not be obtained. Change-Id: I60d7a48d1ba4e5d2dfdb44307b78b4d9ffb73560 Bug: crashpad:437 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4114550 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Ben Hamilton <benhamilton@google.com>
This commit is contained in:
parent
8071d3019e
commit
28354d11c3
@ -403,6 +403,8 @@ crashpad_static_library("util") {
|
||||
"ios/scoped_background_task.mm",
|
||||
"ios/scoped_vm_read.cc",
|
||||
"ios/scoped_vm_read.h",
|
||||
"ios/scoped_vm_map.cc",
|
||||
"ios/scoped_vm_map.h",
|
||||
]
|
||||
}
|
||||
|
||||
@ -837,6 +839,7 @@ source_set("util_test") {
|
||||
"ios/ios_intermediate_dump_reader_test.cc",
|
||||
"ios/ios_intermediate_dump_writer_test.cc",
|
||||
"ios/scoped_vm_read_test.cc",
|
||||
"ios/scoped_vm_map_test.cc",
|
||||
]
|
||||
|
||||
sources -= [
|
||||
|
86
util/ios/scoped_vm_map.cc
Normal file
86
util/ios/scoped_vm_map.cc
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2023 The Crashpad Authors
|
||||
//
|
||||
// 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/ios/scoped_vm_map.h"
|
||||
|
||||
#include "util/ios/raw_logging.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
ScopedVMMapInternal::ScopedVMMapInternal()
|
||||
: data_(0),
|
||||
region_start_(0),
|
||||
region_size_(0),
|
||||
cur_protection_(VM_PROT_NONE) {}
|
||||
|
||||
ScopedVMMapInternal::~ScopedVMMapInternal() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool ScopedVMMapInternal::Map(const void* data, const size_t data_length) {
|
||||
Reset();
|
||||
|
||||
vm_address_t data_address = reinterpret_cast<vm_address_t>(data);
|
||||
vm_address_t page_region_address = trunc_page(data_address);
|
||||
region_size_ = round_page(data_address - page_region_address + data_length);
|
||||
if (region_size_ < data_length) {
|
||||
CRASHPAD_RAW_LOG("ScopedVMMap data_length overflow");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Since region_start_ is 0, vm_remap() will choose an address to which the
|
||||
// memory will be mapped and store the mapped address in region_start_ on
|
||||
// success.
|
||||
vm_prot_t max_protection;
|
||||
kern_return_t kr = vm_remap(mach_task_self(),
|
||||
®ion_start_,
|
||||
region_size_,
|
||||
0,
|
||||
TRUE,
|
||||
mach_task_self(),
|
||||
page_region_address,
|
||||
FALSE,
|
||||
&cur_protection_,
|
||||
&max_protection,
|
||||
VM_INHERIT_DEFAULT);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
// It's expected that this will sometimes fail. Don't log here.
|
||||
return false;
|
||||
}
|
||||
|
||||
data_ = region_start_ + (data_address - page_region_address);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScopedVMMapInternal::Reset() {
|
||||
if (!region_start_) {
|
||||
return;
|
||||
}
|
||||
|
||||
kern_return_t kr =
|
||||
vm_deallocate(mach_task_self(), region_start_, region_size_);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
CRASHPAD_RAW_LOG_ERROR(kr, "vm_deallocate");
|
||||
}
|
||||
|
||||
data_ = 0;
|
||||
region_start_ = 0;
|
||||
region_size_ = 0;
|
||||
cur_protection_ = VM_PROT_NONE;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
125
util/ios/scoped_vm_map.h
Normal file
125
util/ios/scoped_vm_map.h
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright 2023 The Crashpad Authors
|
||||
//
|
||||
// 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_IOS_SCOPED_VM_MAP_H_
|
||||
#define CRASHPAD_UTIL_IOS_SCOPED_VM_MAP_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
//! \brief Non-templated internal class to be used by ScopedVMMap.
|
||||
//!
|
||||
//! Note: RUNS-DURING-CRASH.
|
||||
class ScopedVMMapInternal {
|
||||
public:
|
||||
ScopedVMMapInternal();
|
||||
|
||||
ScopedVMMapInternal(const ScopedVMMapInternal&) = delete;
|
||||
ScopedVMMapInternal& operator=(const ScopedVMMapInternal&) = delete;
|
||||
|
||||
~ScopedVMMapInternal();
|
||||
|
||||
//! \brief Releases any previously mapped data and vm_remaps \a data. Logs an
|
||||
//! error on failure.
|
||||
//!
|
||||
//! \param[in] data Memory to be mapped by vm_remap.
|
||||
//! \param[in] data_length Length of \a data.
|
||||
//!
|
||||
//! \return `true` if all the data was mapped. Logs an error and returns false
|
||||
//! on failure.
|
||||
bool Map(const void* data, size_t data_length);
|
||||
|
||||
//! \brief Returns the current protection for the memory in the region.
|
||||
vm_prot_t CurrentProtection() const { return cur_protection_; }
|
||||
|
||||
vm_address_t data() const { return data_; }
|
||||
|
||||
private:
|
||||
//! \brief Deallocates any resources allocated by this object and resets it
|
||||
//! to its original state.
|
||||
void Reset();
|
||||
|
||||
// The address within region_start_ at which the mapped data is available.
|
||||
vm_address_t data_;
|
||||
|
||||
// The region returned by vm_remap().
|
||||
vm_address_t region_start_;
|
||||
|
||||
// The size of the region returned by vm_remap().
|
||||
vm_size_t region_size_;
|
||||
|
||||
// The current protection for the memory region.
|
||||
vm_prot_t cur_protection_;
|
||||
};
|
||||
|
||||
//! \brief A scoped wrapper for calls to `vm_remap` and `vm_deallocate`. Allows
|
||||
//! in-process handler to safely read and write memory (modulo its
|
||||
//! protection level) for the intermediate dump.
|
||||
//!
|
||||
//! Note: RUNS-DURING-CRASH.
|
||||
template <typename T>
|
||||
class ScopedVMMap {
|
||||
public:
|
||||
ScopedVMMap() : internal_() {}
|
||||
|
||||
ScopedVMMap(const ScopedVMMap&) = delete;
|
||||
ScopedVMMap& operator=(const ScopedVMMap&) = delete;
|
||||
|
||||
~ScopedVMMap() {}
|
||||
|
||||
//! \brief Releases any previously mapped data and vm_remaps data.
|
||||
//!
|
||||
//! \param[in] data Memory to be mapped by vm_remap.
|
||||
//! \param[in] count Length of \a data.
|
||||
//!
|
||||
//! \return `true` if all \a data was mapped. Returns false on failure.
|
||||
bool Map(const void* data, size_t count = 1) {
|
||||
size_t data_length = count * sizeof(T);
|
||||
return internal_.Map(data, data_length);
|
||||
}
|
||||
|
||||
//! \brief Releases any previously mapped data and vm_remaps address.
|
||||
//!
|
||||
//! Before reading or writing the memory, check `CurrentProtection()` to
|
||||
//! ensure the data is readable or writable.
|
||||
//!
|
||||
//! \param[in] address Address of memory to be mapped by vm_remap.
|
||||
//! \param[in] count Length of \a data.
|
||||
//!
|
||||
//! \return `true` if all of \a address was mapped. Returns false on failure.
|
||||
bool Map(vm_address_t address, size_t count = 1) {
|
||||
return Map(reinterpret_cast<T*>(address), count);
|
||||
}
|
||||
|
||||
//! \brief Returns the pointer to memory safe to read and write (respecting
|
||||
//! the CurrentProtection() level) during the in-process crash handler.
|
||||
T* operator->() const { return get(); }
|
||||
|
||||
//! \brief Returns the pointer to memory safe to read and write (respecting
|
||||
//! the CurrentProtection() level) during the in-process crash handler.
|
||||
T* get() const { return reinterpret_cast<T*>(internal_.data()); }
|
||||
|
||||
//! \brief Returns the current protection level of the mapped memory.
|
||||
vm_prot_t CurrentProtection() const { return internal_.CurrentProtection(); }
|
||||
|
||||
private:
|
||||
ScopedVMMapInternal internal_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_IOS_SCOPED_VM_MAP_H_
|
94
util/ios/scoped_vm_map_test.cc
Normal file
94
util/ios/scoped_vm_map_test.cc
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2023 The Crashpad Authors
|
||||
//
|
||||
// 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/ios/scoped_vm_map.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "base/mac/scoped_mach_vm.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/mac/mach_errors.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
TEST(ScopedVMMapTest, BasicFunctionality) {
|
||||
// bad data or count.
|
||||
internal::ScopedVMMap<vm_address_t> vmmap_bad;
|
||||
EXPECT_FALSE(vmmap_bad.Map(nullptr, 100));
|
||||
EXPECT_FALSE(vmmap_bad.Map(reinterpret_cast<void*>(0x1000), 100));
|
||||
vm_address_t invalid_address = 1;
|
||||
EXPECT_FALSE(vmmap_bad.Map(&invalid_address, 1000000000));
|
||||
EXPECT_FALSE(vmmap_bad.Map(&invalid_address, -1));
|
||||
|
||||
vm_address_t valid_address = reinterpret_cast<vm_address_t>(this);
|
||||
EXPECT_FALSE(vmmap_bad.Map(&valid_address, 1000000000));
|
||||
EXPECT_FALSE(vmmap_bad.Map(&valid_address, -1));
|
||||
|
||||
// array
|
||||
static constexpr char map_me[] = "map me";
|
||||
internal::ScopedVMMap<char> vmmap_string;
|
||||
ASSERT_TRUE(vmmap_string.Map(map_me, strlen(map_me)));
|
||||
EXPECT_STREQ(vmmap_string.get(), map_me);
|
||||
EXPECT_TRUE(vmmap_string.CurrentProtection() & VM_PROT_READ);
|
||||
|
||||
// struct
|
||||
timeval time_of_day;
|
||||
EXPECT_TRUE(gettimeofday(&time_of_day, nullptr) == 0);
|
||||
internal::ScopedVMMap<timeval> vmmap_time;
|
||||
ASSERT_TRUE(vmmap_time.Map(&time_of_day));
|
||||
constexpr vm_prot_t kReadWrite = VM_PROT_READ | VM_PROT_WRITE;
|
||||
EXPECT_EQ(vmmap_time.CurrentProtection() & kReadWrite, kReadWrite);
|
||||
EXPECT_EQ(vmmap_time->tv_sec, time_of_day.tv_sec);
|
||||
EXPECT_EQ(vmmap_time->tv_usec, time_of_day.tv_usec);
|
||||
|
||||
// reset.
|
||||
timeval time_of_day2;
|
||||
EXPECT_TRUE(gettimeofday(&time_of_day2, nullptr) == 0);
|
||||
ASSERT_TRUE(vmmap_time.Map(&time_of_day2));
|
||||
EXPECT_EQ(vmmap_time.CurrentProtection() & kReadWrite, kReadWrite);
|
||||
EXPECT_EQ(vmmap_time->tv_sec, time_of_day2.tv_sec);
|
||||
EXPECT_EQ(vmmap_time->tv_usec, time_of_day2.tv_usec);
|
||||
}
|
||||
|
||||
TEST(ScopedVMMapTest, MissingMiddleVM) {
|
||||
char* region;
|
||||
vm_size_t page_size = getpagesize();
|
||||
vm_size_t region_size = page_size * 3;
|
||||
kern_return_t kr = vm_allocate(mach_task_self(),
|
||||
reinterpret_cast<vm_address_t*>(®ion),
|
||||
region_size,
|
||||
VM_FLAGS_ANYWHERE);
|
||||
ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate");
|
||||
|
||||
base::mac::ScopedMachVM vm_owner(reinterpret_cast<vm_address_t>(region),
|
||||
region_size);
|
||||
|
||||
internal::ScopedVMMap<char> vmmap_missing_middle;
|
||||
ASSERT_TRUE(vmmap_missing_middle.Map(region, region_size));
|
||||
|
||||
// Dealloc middle page.
|
||||
kr = vm_deallocate(mach_task_self(),
|
||||
reinterpret_cast<vm_address_t>(region + page_size),
|
||||
page_size);
|
||||
ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate");
|
||||
|
||||
EXPECT_FALSE(vmmap_missing_middle.Map(region, region_size));
|
||||
ASSERT_TRUE(vmmap_missing_middle.Map(region, page_size));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -20,25 +20,15 @@ namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
ScopedVMReadInternal::ScopedVMReadInternal()
|
||||
: data_(0), vm_read_data_(0), vm_read_data_count_(0) {}
|
||||
: data_(0), region_start_(0), region_size_(0) {}
|
||||
|
||||
ScopedVMReadInternal::~ScopedVMReadInternal() {
|
||||
if (data_) {
|
||||
kern_return_t kr =
|
||||
vm_deallocate(mach_task_self(), vm_read_data_, vm_read_data_count_);
|
||||
if (kr != KERN_SUCCESS)
|
||||
CRASHPAD_RAW_LOG_ERROR(kr, "vm_deallocate");
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
|
||||
bool ScopedVMReadInternal::Read(const void* data, const size_t data_length) {
|
||||
if (data_) {
|
||||
kern_return_t kr =
|
||||
vm_deallocate(mach_task_self(), vm_read_data_, vm_read_data_count_);
|
||||
if (kr != KERN_SUCCESS)
|
||||
CRASHPAD_RAW_LOG_ERROR(kr, "vm_deallocate");
|
||||
data_ = 0;
|
||||
}
|
||||
Reset();
|
||||
|
||||
vm_address_t data_address = reinterpret_cast<vm_address_t>(data);
|
||||
vm_address_t page_region_address = trunc_page(data_address);
|
||||
vm_size_t page_region_size =
|
||||
@ -50,16 +40,30 @@ bool ScopedVMReadInternal::Read(const void* data, const size_t data_length) {
|
||||
kern_return_t kr = vm_read(mach_task_self(),
|
||||
page_region_address,
|
||||
page_region_size,
|
||||
&vm_read_data_,
|
||||
&vm_read_data_count_);
|
||||
®ion_start_,
|
||||
®ion_size_);
|
||||
|
||||
if (kr == KERN_SUCCESS) {
|
||||
data_ = vm_read_data_ + (data_address - page_region_address);
|
||||
return true;
|
||||
} else {
|
||||
if (kr != KERN_SUCCESS) {
|
||||
// It's expected that this will sometimes fail. Don't log here.
|
||||
return false;
|
||||
}
|
||||
|
||||
data_ = region_start_ + (data_address - page_region_address);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScopedVMReadInternal::Reset() {
|
||||
if (!region_start_) {
|
||||
return;
|
||||
}
|
||||
kern_return_t kr =
|
||||
vm_deallocate(mach_task_self(), region_start_, region_size_);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
CRASHPAD_RAW_LOG_ERROR(kr, "vm_deallocate");
|
||||
}
|
||||
region_start_ = 0;
|
||||
region_size_ = 0;
|
||||
data_ = 0;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -46,14 +46,18 @@ class ScopedVMReadInternal {
|
||||
vm_address_t data() const { return data_; }
|
||||
|
||||
private:
|
||||
// The address of the requested data.
|
||||
//! \brief Deallocates any resources allocated by this object and resets it
|
||||
//! to its original state.
|
||||
void Reset();
|
||||
|
||||
// The address within region_start_ at which the the data is available.
|
||||
vm_address_t data_;
|
||||
|
||||
// The rounded down page boundary of the requested data.
|
||||
vm_address_t vm_read_data_;
|
||||
// The region returned by vm_read().
|
||||
vm_address_t region_start_;
|
||||
|
||||
// The size of the pages that were actually read.
|
||||
mach_msg_type_number_t vm_read_data_count_;
|
||||
// The size of the region returned by vm_read().
|
||||
mach_msg_type_number_t region_size_;
|
||||
};
|
||||
|
||||
//! \brief A scoped wrapper for calls to `vm_read` and `vm_deallocate`. Allows
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "base/mac/scoped_mach_vm.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/mac/mach_errors.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
@ -26,43 +27,47 @@ namespace {
|
||||
TEST(ScopedVMReadTest, BasicFunctionality) {
|
||||
// bad data or count.
|
||||
internal::ScopedVMRead<vm_address_t> vmread_bad;
|
||||
ASSERT_FALSE(vmread_bad.Read(nullptr, 100));
|
||||
ASSERT_FALSE(vmread_bad.Read(reinterpret_cast<void*>(0x1000), 100));
|
||||
vm_address_t address = 1;
|
||||
ASSERT_FALSE(vmread_bad.Read(&address, 1000000000));
|
||||
ASSERT_FALSE(vmread_bad.Read(&address, -1));
|
||||
EXPECT_FALSE(vmread_bad.Read(nullptr, 100));
|
||||
EXPECT_FALSE(vmread_bad.Read(reinterpret_cast<void*>(0x1000), 100));
|
||||
vm_address_t invalid_address = 1;
|
||||
EXPECT_FALSE(vmread_bad.Read(&invalid_address, 1000000000));
|
||||
EXPECT_FALSE(vmread_bad.Read(&invalid_address, -1));
|
||||
|
||||
vm_address_t valid_address = reinterpret_cast<vm_address_t>(this);
|
||||
EXPECT_FALSE(vmread_bad.Read(&valid_address, 1000000000));
|
||||
EXPECT_FALSE(vmread_bad.Read(&valid_address, -1));
|
||||
// array
|
||||
constexpr char read_me[] = "read me";
|
||||
internal::ScopedVMRead<char> vmread_string;
|
||||
ASSERT_TRUE(vmread_string.Read(read_me, strlen(read_me)));
|
||||
EXPECT_STREQ(read_me, vmread_string.get());
|
||||
EXPECT_STREQ(vmread_string.get(), read_me);
|
||||
|
||||
// struct
|
||||
timeval time_of_day;
|
||||
EXPECT_TRUE(gettimeofday(&time_of_day, nullptr) == 0);
|
||||
internal::ScopedVMRead<timeval> vmread_time;
|
||||
ASSERT_TRUE(vmread_time.Read(&time_of_day));
|
||||
EXPECT_EQ(time_of_day.tv_sec, vmread_time->tv_sec);
|
||||
EXPECT_EQ(time_of_day.tv_usec, vmread_time->tv_usec);
|
||||
EXPECT_EQ(vmread_time->tv_sec, time_of_day.tv_sec);
|
||||
EXPECT_EQ(vmread_time->tv_usec, time_of_day.tv_usec);
|
||||
|
||||
// reset.
|
||||
timeval time_of_day2;
|
||||
EXPECT_TRUE(gettimeofday(&time_of_day2, nullptr) == 0);
|
||||
ASSERT_TRUE(vmread_time.Read(&time_of_day2));
|
||||
EXPECT_EQ(time_of_day2.tv_sec, vmread_time->tv_sec);
|
||||
EXPECT_EQ(time_of_day2.tv_usec, vmread_time->tv_usec);
|
||||
EXPECT_EQ(vmread_time->tv_sec, time_of_day2.tv_sec);
|
||||
EXPECT_EQ(vmread_time->tv_usec, time_of_day2.tv_usec);
|
||||
}
|
||||
|
||||
TEST(ScopedVMReadTest, MissingMiddleVM) {
|
||||
char* region;
|
||||
vm_size_t page_size = getpagesize();
|
||||
vm_size_t region_size = page_size * 3;
|
||||
ASSERT_EQ(vm_allocate(mach_task_self(),
|
||||
reinterpret_cast<vm_address_t*>(®ion),
|
||||
region_size,
|
||||
VM_FLAGS_ANYWHERE),
|
||||
0);
|
||||
kern_return_t kr = vm_allocate(mach_task_self(),
|
||||
reinterpret_cast<vm_address_t*>(®ion),
|
||||
region_size,
|
||||
VM_FLAGS_ANYWHERE);
|
||||
ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate");
|
||||
|
||||
base::mac::ScopedMachVM vm_owner(reinterpret_cast<vm_address_t>(region),
|
||||
region_size);
|
||||
|
||||
@ -70,12 +75,12 @@ TEST(ScopedVMReadTest, MissingMiddleVM) {
|
||||
ASSERT_TRUE(vmread_missing_middle.Read(region, region_size));
|
||||
|
||||
// Dealloc middle page.
|
||||
ASSERT_EQ(vm_deallocate(mach_task_self(),
|
||||
reinterpret_cast<vm_address_t>(region + page_size),
|
||||
page_size),
|
||||
0);
|
||||
kr = vm_deallocate(mach_task_self(),
|
||||
reinterpret_cast<vm_address_t>(region + page_size),
|
||||
page_size);
|
||||
ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate");
|
||||
|
||||
ASSERT_FALSE(vmread_missing_middle.Read(region, region_size));
|
||||
EXPECT_FALSE(vmread_missing_middle.Read(region, region_size));
|
||||
ASSERT_TRUE(vmread_missing_middle.Read(region, page_size));
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user