| Index: util/mach/task_memory_test.cc
|
| diff --git a/util/mach/task_memory_test.cc b/util/mach/task_memory_test.cc
|
| index 6438089affe44ddf88add63fd1949ed7b6b9c975..3ee813abb3d9b9eec6c488bd91b8558f1b302c4c 100644
|
| --- a/util/mach/task_memory_test.cc
|
| +++ b/util/mach/task_memory_test.cc
|
| @@ -15,11 +15,14 @@
|
| #include "util/mach/task_memory.h"
|
|
|
| #include <mach/mach.h>
|
| +#include <string.h>
|
|
|
| #include <algorithm>
|
| #include <string>
|
|
|
| +#include "base/mac/scoped_mach_port.h"
|
| #include "base/mac/scoped_mach_vm.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| #include "gtest/gtest.h"
|
| #include "util/test/mac/mach_errors.h"
|
|
|
| @@ -42,42 +45,59 @@ TEST(TaskMemory, ReadSelf) {
|
| }
|
|
|
| TaskMemory memory(mach_task_self());
|
| +
|
| + // This tests using both the Read() and ReadMapped() interfaces.
|
| std::string result(kSize, '\0');
|
| + scoped_ptr<TaskMemory::MappedMemory> mapped;
|
|
|
| // Ensure that the entire region can be read.
|
| ASSERT_TRUE(memory.Read(address, kSize, &result[0]));
|
| EXPECT_EQ(0, memcmp(region, &result[0], kSize));
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize)));
|
| + EXPECT_EQ(0, memcmp(region, mapped->data(), kSize));
|
|
|
| // Ensure that a read of length 0 succeeds and doesn’t touch the result.
|
| result.assign(kSize, '\0');
|
| std::string zeroes = result;
|
| ASSERT_TRUE(memory.Read(address, 0, &result[0]));
|
| EXPECT_EQ(zeroes, result);
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(address, 0)));
|
|
|
| // Ensure that a read starting at an unaligned address works.
|
| ASSERT_TRUE(memory.Read(address + 1, kSize - 1, &result[0]));
|
| EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 1));
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
|
| + EXPECT_EQ(0, memcmp(region + 1, mapped->data(), kSize - 1));
|
|
|
| // Ensure that a read ending at an unaligned address works.
|
| ASSERT_TRUE(memory.Read(address, kSize - 1, &result[0]));
|
| EXPECT_EQ(0, memcmp(region, &result[0], kSize - 1));
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize - 1)));
|
| + EXPECT_EQ(0, memcmp(region, mapped->data(), kSize - 1));
|
|
|
| // Ensure that a read starting and ending at unaligned addresses works.
|
| ASSERT_TRUE(memory.Read(address + 1, kSize - 2, &result[0]));
|
| EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 2));
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 2)));
|
| + EXPECT_EQ(0, memcmp(region + 1, mapped->data(), kSize - 2));
|
|
|
| // Ensure that a read of exactly one page works.
|
| ASSERT_TRUE(memory.Read(address + PAGE_SIZE, PAGE_SIZE, &result[0]));
|
| EXPECT_EQ(0, memcmp(region + PAGE_SIZE, &result[0], PAGE_SIZE));
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE, PAGE_SIZE)));
|
| + EXPECT_EQ(0, memcmp(region + PAGE_SIZE, mapped->data(), PAGE_SIZE));
|
|
|
| // Ensure that a read of a single byte works.
|
| ASSERT_TRUE(memory.Read(address + 2, 1, &result[0]));
|
| EXPECT_EQ(region[2], result[0]);
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(address + 2, 1)));
|
| + EXPECT_EQ(region[2], reinterpret_cast<const char*>(mapped->data())[0]);
|
|
|
| // Ensure that a read of length zero works and doesn’t touch the data.
|
| result[0] = 'M';
|
| ASSERT_TRUE(memory.Read(address + 3, 0, &result[0]));
|
| EXPECT_EQ('M', result[0]);
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(address + 3, 0)));
|
| }
|
|
|
| TEST(TaskMemory, ReadSelfUnmapped) {
|
| @@ -109,6 +129,15 @@ TEST(TaskMemory, ReadSelfUnmapped) {
|
| EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));
|
| EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));
|
|
|
| + // Do the same thing with the ReadMapped() interface.
|
| + scoped_ptr<TaskMemory::MappedMemory> mapped;
|
| + EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));
|
| + EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
|
| + EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));
|
| + EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));
|
| + EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));
|
| + EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));
|
| +
|
| // Repeat the test with an unmapped page instead of an unreadable one. This
|
| // portion of the test may be flaky in the presence of other threads, if
|
| // another thread maps something in the region that is deallocated here.
|
| @@ -122,6 +151,14 @@ TEST(TaskMemory, ReadSelfUnmapped) {
|
| EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0]));
|
| EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0]));
|
| EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0]));
|
| +
|
| + // Do the same thing with the ReadMapped() interface.
|
| + EXPECT_FALSE((mapped = memory.ReadMapped(address, kSize)));
|
| + EXPECT_FALSE((mapped = memory.ReadMapped(address + 1, kSize - 1)));
|
| + EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE, 1)));
|
| + EXPECT_FALSE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 2)));
|
| + EXPECT_TRUE((mapped = memory.ReadMapped(address, PAGE_SIZE)));
|
| + EXPECT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE - 1, 1)));
|
| }
|
|
|
| // This function consolidates the cast from a char* to mach_vm_address_t in one
|
| @@ -383,4 +420,135 @@ TEST(TaskMemory, ReadCStringSizeLimited_StringLong) {
|
| &memory, &string_long[0], string_long.size(), &result));
|
| }
|
|
|
| +bool IsAddressMapped(vm_address_t address) {
|
| + vm_address_t region_address = address;
|
| + vm_size_t region_size;
|
| + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
|
| + vm_region_basic_info_64 info;
|
| + mach_port_t object;
|
| + kern_return_t kr = vm_region_64(mach_task_self(),
|
| + ®ion_address,
|
| + ®ion_size,
|
| + VM_REGION_BASIC_INFO_64,
|
| + reinterpret_cast<vm_region_info_t>(&info),
|
| + &count,
|
| + &object);
|
| + if (kr == KERN_SUCCESS) {
|
| + // |object| will be MACH_PORT_NULL (10.9.4 xnu-2422.110.17/osfmk/vm/vm_map.c
|
| + // vm_map_region()), but the interface acts as if it might carry a send
|
| + // right, so treat it as documented.
|
| + base::mac::ScopedMachSendRight object_owner(object);
|
| +
|
| + return address >= region_address && address <= region_address + region_size;
|
| + }
|
| +
|
| + if (kr == KERN_INVALID_ADDRESS) {
|
| + return false;
|
| + }
|
| +
|
| + ADD_FAILURE() << MachErrorMessage(kr, "vm_region_64");;
|
| + return false;
|
| +}
|
| +
|
| +TEST(TaskMemory, MappedMemoryDeallocates) {
|
| + // This tests that once a TaskMemory::MappedMemory object is destroyed, it
|
| + // releases the mapped memory that it owned. Technically, this test is not
|
| + // valid because after the mapping is released, something else (on another
|
| + // thread) might wind up mapped in the same address. In the test environment,
|
| + // hopefully there are either no other threads or they’re all quiescent, so
|
| + // nothing else should wind up mapped in the address.
|
| +
|
| + TaskMemory memory(mach_task_self());
|
| + scoped_ptr<TaskMemory::MappedMemory> mapped;
|
| +
|
| + static const char kTestBuffer[] = "hello!";
|
| + mach_vm_address_t test_address =
|
| + reinterpret_cast<mach_vm_address_t>(&kTestBuffer);
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer))));
|
| + EXPECT_EQ(0, memcmp(kTestBuffer, mapped->data(), sizeof(kTestBuffer)));
|
| +
|
| + vm_address_t mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
|
| + EXPECT_TRUE(IsAddressMapped(mapped_address));
|
| +
|
| + mapped.reset();
|
| + EXPECT_FALSE(IsAddressMapped(mapped_address));
|
| +
|
| + // This is the same but with a big buffer that’s definitely larger than a
|
| + // single page. This makes sure that the whole mapped region winds up being
|
| + // deallocated.
|
| + const size_t kBigSize = 4 * PAGE_SIZE;
|
| + scoped_ptr<char[]> big_buffer(new char[kBigSize]);
|
| + test_address = reinterpret_cast<mach_vm_address_t>(&big_buffer[0]);
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize)));
|
| +
|
| + mapped_address = reinterpret_cast<vm_address_t>(mapped->data());
|
| + vm_address_t mapped_last_address = mapped_address + kBigSize - 1;
|
| + EXPECT_TRUE(IsAddressMapped(mapped_address));
|
| + EXPECT_TRUE(IsAddressMapped(mapped_address + PAGE_SIZE));
|
| + EXPECT_TRUE(IsAddressMapped(mapped_last_address));
|
| +
|
| + mapped.reset();
|
| + EXPECT_FALSE(IsAddressMapped(mapped_address));
|
| + EXPECT_FALSE(IsAddressMapped(mapped_address + PAGE_SIZE));
|
| + EXPECT_FALSE(IsAddressMapped(mapped_last_address));
|
| +}
|
| +
|
| +TEST(TaskMemory, MappedMemoryReadCString) {
|
| + // This tests the behavior of TaskMemory::MappedMemory::ReadCString().
|
| + TaskMemory memory(mach_task_self());
|
| + scoped_ptr<TaskMemory::MappedMemory> mapped;
|
| +
|
| + static const char kTestBuffer[] = "0\0" "2\0" "45\0" "789";
|
| + const mach_vm_address_t kTestAddress =
|
| + reinterpret_cast<mach_vm_address_t>(&kTestBuffer);
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10)));
|
| +
|
| + std::string string;
|
| + ASSERT_TRUE(mapped->ReadCString(0, &string));
|
| + EXPECT_EQ("0", string);
|
| + ASSERT_TRUE(mapped->ReadCString(1, &string));
|
| + EXPECT_EQ("", string);
|
| + ASSERT_TRUE(mapped->ReadCString(2, &string));
|
| + EXPECT_EQ("2", string);
|
| + ASSERT_TRUE(mapped->ReadCString(3, &string));
|
| + EXPECT_EQ("", string);
|
| + ASSERT_TRUE(mapped->ReadCString(4, &string));
|
| + EXPECT_EQ("45", string);
|
| + ASSERT_TRUE(mapped->ReadCString(5, &string));
|
| + EXPECT_EQ("5", string);
|
| + ASSERT_TRUE(mapped->ReadCString(6, &string));
|
| + EXPECT_EQ("", string);
|
| +
|
| + // kTestBuffer’s NUL terminator was not read, so these will see an
|
| + // unterminated string and fail.
|
| + EXPECT_FALSE(mapped->ReadCString(7, &string));
|
| + EXPECT_FALSE(mapped->ReadCString(8, &string));
|
| + EXPECT_FALSE(mapped->ReadCString(9, &string));
|
| +
|
| + // This is out of the range of what was read, so it will fail.
|
| + EXPECT_FALSE(mapped->ReadCString(10, &string));
|
| + EXPECT_FALSE(mapped->ReadCString(11, &string));
|
| +
|
| + // Read it again, this time with a length long enough to include the NUL
|
| + // terminator.
|
| + ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 11)));
|
| +
|
| + ASSERT_TRUE(mapped->ReadCString(6, &string));
|
| + EXPECT_EQ("", string);
|
| +
|
| + // These should now succeed.
|
| + ASSERT_TRUE(mapped->ReadCString(7, &string));
|
| + EXPECT_EQ("789", string);
|
| + ASSERT_TRUE(mapped->ReadCString(8, &string));
|
| + EXPECT_EQ("89", string);
|
| + ASSERT_TRUE(mapped->ReadCString(9, &string));
|
| + EXPECT_EQ("9", string);
|
| + EXPECT_TRUE(mapped->ReadCString(10, &string));
|
| + EXPECT_EQ("", string);
|
| +
|
| + // These are still out of range.
|
| + EXPECT_FALSE(mapped->ReadCString(11, &string));
|
| + EXPECT_FALSE(mapped->ReadCString(12, &string));
|
| +}
|
| +
|
| } // namespace
|
|
|