// 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/mach/task_memory.h" #include #include #include #include #include #include "base/mac/scoped_mach_port.h" #include "base/mac/scoped_mach_vm.h" #include "gtest/gtest.h" #include "test/mac/mach_errors.h" #include "util/misc/from_pointer_cast.h" namespace crashpad { namespace test { namespace { TEST(TaskMemory, ReadSelf) { vm_address_t address = 0; constexpr vm_size_t kSize = 4 * PAGE_SIZE; kern_return_t kr = vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate"); base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); char* region = reinterpret_cast(address); for (size_t index = 0; index < kSize; ++index) { region[index] = (index % 256) ^ ((index >> 8) % 256); } TaskMemory memory(mach_task_self()); // This tests using both the Read() and ReadMapped() interfaces. std::string result(kSize, '\0'); std::unique_ptr mapped; // Ensure that the entire region can be read. ASSERT_TRUE(memory.Read(address, kSize, &result[0])); EXPECT_EQ(memcmp(region, &result[0], kSize), 0); ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize))); EXPECT_EQ(memcmp(region, mapped->data(), kSize), 0); // 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(result, zeroes); 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(memcmp(region + 1, &result[0], kSize - 1), 0); ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 1))); EXPECT_EQ(memcmp(region + 1, mapped->data(), kSize - 1), 0); // Ensure that a read ending at an unaligned address works. ASSERT_TRUE(memory.Read(address, kSize - 1, &result[0])); EXPECT_EQ(memcmp(region, &result[0], kSize - 1), 0); ASSERT_TRUE((mapped = memory.ReadMapped(address, kSize - 1))); EXPECT_EQ(memcmp(region, mapped->data(), kSize - 1), 0); // Ensure that a read starting and ending at unaligned addresses works. ASSERT_TRUE(memory.Read(address + 1, kSize - 2, &result[0])); EXPECT_EQ(memcmp(region + 1, &result[0], kSize - 2), 0); ASSERT_TRUE((mapped = memory.ReadMapped(address + 1, kSize - 2))); EXPECT_EQ(memcmp(region + 1, mapped->data(), kSize - 2), 0); // Ensure that a read of exactly one page works. ASSERT_TRUE(memory.Read(address + PAGE_SIZE, PAGE_SIZE, &result[0])); EXPECT_EQ(memcmp(region + PAGE_SIZE, &result[0], PAGE_SIZE), 0); ASSERT_TRUE((mapped = memory.ReadMapped(address + PAGE_SIZE, PAGE_SIZE))); EXPECT_EQ(memcmp(region + PAGE_SIZE, mapped->data(), PAGE_SIZE), 0); // Ensure that a read of a single byte works. ASSERT_TRUE(memory.Read(address + 2, 1, &result[0])); EXPECT_EQ(result[0], region[2]); ASSERT_TRUE((mapped = memory.ReadMapped(address + 2, 1))); EXPECT_EQ(reinterpret_cast(mapped->data())[0], region[2]); // 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(result[0], 'M'); ASSERT_TRUE((mapped = memory.ReadMapped(address + 3, 0))); } TEST(TaskMemory, ReadSelfUnmapped) { vm_address_t address = 0; constexpr vm_size_t kSize = 2 * PAGE_SIZE; kern_return_t kr = vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate"); base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); char* region = reinterpret_cast(address); for (size_t index = 0; index < kSize; ++index) { // Don’t include any NUL bytes, because ReadCString stops when it encounters // a NUL. region[index] = (index % 255) + 1; } kr = vm_protect( mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_protect"); TaskMemory memory(mach_task_self()); std::string result(kSize, '\0'); EXPECT_FALSE(memory.Read(address, kSize, &result[0])); EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0])); EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0])); 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. std::unique_ptr 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. kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate"); vm_owner.reset(address, PAGE_SIZE); EXPECT_FALSE(memory.Read(address, kSize, &result[0])); EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0])); EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0])); 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 // location when reading from the current task. bool ReadCStringSelf(TaskMemory* memory, const char* pointer, std::string* result) { return memory->ReadCString(FromPointerCast(pointer), result); } TEST(TaskMemory, ReadCStringSelf) { TaskMemory memory(mach_task_self()); std::string result; const char kConstCharEmpty[] = ""; ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharEmpty, &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kConstCharEmpty); const char kConstCharShort[] = "A short const char[]"; ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharShort, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kConstCharShort); static const char kStaticConstCharEmpty[] = ""; ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharEmpty, &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kStaticConstCharEmpty); static const char kStaticConstCharShort[] = "A short static const char[]"; ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharShort, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kStaticConstCharShort); constexpr char kConstexprCharEmpty[] = ""; ASSERT_TRUE(ReadCStringSelf(&memory, kConstexprCharEmpty, &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kConstexprCharEmpty); constexpr char kConstexprCharShort[] = "A short constexpr char[]"; ASSERT_TRUE(ReadCStringSelf(&memory, kConstexprCharShort, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kConstexprCharShort); static constexpr char kStaticConstexprCharEmpty[] = ""; ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstexprCharEmpty, &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kStaticConstexprCharEmpty); static constexpr char kStaticConstexprCharShort[] = "A short static constexpr char[]"; ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstexprCharShort, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kStaticConstexprCharShort); std::string string_short("A short std::string in a function"); ASSERT_TRUE(ReadCStringSelf(&memory, &string_short[0], &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, string_short); std::string string_long; constexpr size_t kStringLongSize = 4 * PAGE_SIZE; for (size_t index = 0; index < kStringLongSize; ++index) { // Don’t include any NUL bytes, because ReadCString stops when it encounters // a NUL. string_long.append(1, (index % 255) + 1); } ASSERT_EQ(string_long.size(), kStringLongSize); ASSERT_TRUE(ReadCStringSelf(&memory, &string_long[0], &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result.size(), kStringLongSize); EXPECT_EQ(result, string_long); } TEST(TaskMemory, ReadCStringSelfUnmapped) { vm_address_t address = 0; constexpr vm_size_t kSize = 2 * PAGE_SIZE; kern_return_t kr = vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_allocate"); base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); char* region = reinterpret_cast(address); for (size_t index = 0; index < kSize; ++index) { // Don’t include any NUL bytes, because ReadCString stops when it encounters // a NUL. region[index] = (index % 255) + 1; } kr = vm_protect( mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_protect"); TaskMemory memory(mach_task_self()); std::string result; EXPECT_FALSE(memory.ReadCString(address, &result)); // Make sure that if the string is NUL-terminated within the mapped memory // region, it can be read properly. char terminator_or_not = '\0'; std::swap(region[PAGE_SIZE - 1], terminator_or_not); ASSERT_TRUE(memory.ReadCString(address, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result.size(), PAGE_SIZE - 1u); EXPECT_EQ(result, region); // 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. std::swap(region[PAGE_SIZE - 1], terminator_or_not); kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE); ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate"); vm_owner.reset(address, PAGE_SIZE); EXPECT_FALSE(memory.ReadCString(address, &result)); // Clear the result before testing that the string can be read. This makes // sure that the result is actually filled in, because it already contains the // expected value from the tests above. result.clear(); std::swap(region[PAGE_SIZE - 1], terminator_or_not); ASSERT_TRUE(memory.ReadCString(address, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result.size(), PAGE_SIZE - 1u); EXPECT_EQ(result, region); } // This function consolidates the cast from a char* to mach_vm_address_t in one // location when reading from the current task. bool ReadCStringSizeLimitedSelf(TaskMemory* memory, const char* pointer, size_t size, std::string* result) { return memory->ReadCStringSizeLimited( FromPointerCast(pointer), size, result); } TEST(TaskMemory, ReadCStringSizeLimited_ConstCharEmpty) { TaskMemory memory(mach_task_self()); std::string result; static constexpr char kConstCharEmpty[] = ""; ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kConstCharEmpty); result.clear(); ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, kConstCharEmpty, arraysize(kConstCharEmpty) + 1, &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kConstCharEmpty); result.clear(); ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kConstCharEmpty, 0, &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kConstCharEmpty); } TEST(TaskMemory, ReadCStringSizeLimited_ConstCharShort) { TaskMemory memory(mach_task_self()); std::string result; static constexpr char kConstCharShort[] = "A short const char[]"; ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, kConstCharShort, arraysize(kConstCharShort), &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kConstCharShort); result.clear(); ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, kConstCharShort, arraysize(kConstCharShort) + 1, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kConstCharShort); ASSERT_FALSE(ReadCStringSizeLimitedSelf( &memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result)); } TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharEmpty) { TaskMemory memory(mach_task_self()); std::string result; static constexpr char kStaticConstCharEmpty[] = ""; ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, arraysize(kStaticConstCharEmpty), &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kStaticConstCharEmpty); result.clear(); ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, arraysize(kStaticConstCharEmpty) + 1, &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kStaticConstCharEmpty); result.clear(); ASSERT_TRUE( ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, 0, &result)); EXPECT_TRUE(result.empty()); EXPECT_EQ(result, kStaticConstCharEmpty); } TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharShort) { TaskMemory memory(mach_task_self()); std::string result; static constexpr char kStaticConstCharShort[] = "A short static constexpr char[]"; ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kStaticConstCharShort, arraysize(kStaticConstCharShort), &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kStaticConstCharShort); result.clear(); ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kStaticConstCharShort, arraysize(kStaticConstCharShort) + 1, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, kStaticConstCharShort); ASSERT_FALSE(ReadCStringSizeLimitedSelf(&memory, kStaticConstCharShort, arraysize(kStaticConstCharShort) - 1, &result)); } TEST(TaskMemory, ReadCStringSizeLimited_StringShort) { TaskMemory memory(mach_task_self()); std::string result; std::string string_short("A short std::string in a function"); ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, &string_short[0], string_short.size() + 1, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, string_short); result.clear(); ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, &string_short[0], string_short.size() + 2, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result, string_short); ASSERT_FALSE(ReadCStringSizeLimitedSelf( &memory, &string_short[0], string_short.size(), &result)); } TEST(TaskMemory, ReadCStringSizeLimited_StringLong) { TaskMemory memory(mach_task_self()); std::string result; std::string string_long; constexpr size_t kStringLongSize = 4 * PAGE_SIZE; for (size_t index = 0; index < kStringLongSize; ++index) { // Don’t include any NUL bytes, because ReadCString stops when it encounters // a NUL. string_long.append(1, (index % 255) + 1); } ASSERT_EQ(string_long.size(), kStringLongSize); ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, &string_long[0], string_long.size() + 1, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result.size(), kStringLongSize); EXPECT_EQ(result, string_long); result.clear(); ASSERT_TRUE(ReadCStringSizeLimitedSelf( &memory, &string_long[0], string_long.size() + 2, &result)); EXPECT_FALSE(result.empty()); EXPECT_EQ(result.size(), kStringLongSize); EXPECT_EQ(result, string_long); ASSERT_FALSE(ReadCStringSizeLimitedSelf( &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(&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()); std::unique_ptr mapped; static constexpr char kTestBuffer[] = "hello!"; mach_vm_address_t test_address = FromPointerCast(&kTestBuffer); ASSERT_TRUE((mapped = memory.ReadMapped(test_address, sizeof(kTestBuffer)))); EXPECT_EQ(memcmp(kTestBuffer, mapped->data(), sizeof(kTestBuffer)), 0); vm_address_t mapped_address = reinterpret_cast(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. constexpr size_t kBigSize = 4 * PAGE_SIZE; std::unique_ptr big_buffer(new char[kBigSize]); test_address = FromPointerCast(&big_buffer[0]); ASSERT_TRUE((mapped = memory.ReadMapped(test_address, kBigSize))); mapped_address = reinterpret_cast(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()); std::unique_ptr mapped; static constexpr char kTestBuffer[] = "0\0" "2\0" "45\0" "789"; const mach_vm_address_t kTestAddress = FromPointerCast(&kTestBuffer); ASSERT_TRUE((mapped = memory.ReadMapped(kTestAddress, 10))); std::string string; ASSERT_TRUE(mapped->ReadCString(0, &string)); EXPECT_EQ(string, "0"); ASSERT_TRUE(mapped->ReadCString(1, &string)); EXPECT_EQ(string, ""); ASSERT_TRUE(mapped->ReadCString(2, &string)); EXPECT_EQ(string, "2"); ASSERT_TRUE(mapped->ReadCString(3, &string)); EXPECT_EQ(string, ""); ASSERT_TRUE(mapped->ReadCString(4, &string)); EXPECT_EQ(string, "45"); ASSERT_TRUE(mapped->ReadCString(5, &string)); EXPECT_EQ(string, "5"); 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(string, "789"); ASSERT_TRUE(mapped->ReadCString(8, &string)); EXPECT_EQ(string, "89"); ASSERT_TRUE(mapped->ReadCString(9, &string)); EXPECT_EQ(string, "9"); 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 } // namespace test } // namespace crashpad