OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
| 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at |
| 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. |
| 14 |
| 15 #include "util/mach/task_memory.h" |
| 16 |
| 17 #include <mach/mach.h> |
| 18 |
| 19 #include <algorithm> |
| 20 #include <string> |
| 21 |
| 22 #include "base/mac/scoped_mach_vm.h" |
| 23 #include "gtest/gtest.h" |
| 24 #include "util/test/mac/mach_errors.h" |
| 25 |
| 26 namespace { |
| 27 |
| 28 using namespace crashpad; |
| 29 using namespace crashpad::test; |
| 30 |
| 31 TEST(TaskMemory, ReadSelf) { |
| 32 vm_address_t address = 0; |
| 33 const vm_size_t kSize = 4 * PAGE_SIZE; |
| 34 kern_return_t kr = |
| 35 vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); |
| 36 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); |
| 37 base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); |
| 38 |
| 39 char* region = reinterpret_cast<char*>(address); |
| 40 for (size_t index = 0; index < kSize; ++index) { |
| 41 region[index] = (index % 256) ^ ((index >> 8) % 256); |
| 42 } |
| 43 |
| 44 TaskMemory memory(mach_task_self()); |
| 45 std::string result(kSize, '\0'); |
| 46 |
| 47 // Ensure that the entire region can be read. |
| 48 ASSERT_TRUE(memory.Read(address, kSize, &result[0])); |
| 49 EXPECT_EQ(0, memcmp(region, &result[0], kSize)); |
| 50 |
| 51 // Ensure that a read of length 0 succeeds and doesn’t touch the result. |
| 52 result.assign(kSize, '\0'); |
| 53 std::string zeroes = result; |
| 54 ASSERT_TRUE(memory.Read(address, 0, &result[0])); |
| 55 EXPECT_EQ(zeroes, result); |
| 56 |
| 57 // Ensure that a read starting at an unaligned address works. |
| 58 ASSERT_TRUE(memory.Read(address + 1, kSize - 1, &result[0])); |
| 59 EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 1)); |
| 60 |
| 61 // Ensure that a read ending at an unaligned address works. |
| 62 ASSERT_TRUE(memory.Read(address, kSize - 1, &result[0])); |
| 63 EXPECT_EQ(0, memcmp(region, &result[0], kSize - 1)); |
| 64 |
| 65 // Ensure that a read starting and ending at unaligned addresses works. |
| 66 ASSERT_TRUE(memory.Read(address + 1, kSize - 2, &result[0])); |
| 67 EXPECT_EQ(0, memcmp(region + 1, &result[0], kSize - 2)); |
| 68 |
| 69 // Ensure that a read of exactly one page works. |
| 70 ASSERT_TRUE(memory.Read(address + PAGE_SIZE, PAGE_SIZE, &result[0])); |
| 71 EXPECT_EQ(0, memcmp(region + PAGE_SIZE, &result[0], PAGE_SIZE)); |
| 72 |
| 73 // Ensure that a read of a single byte works. |
| 74 ASSERT_TRUE(memory.Read(address + 2, 1, &result[0])); |
| 75 EXPECT_EQ(region[2], result[0]); |
| 76 |
| 77 // Ensure that a read of length zero works and doesn’t touch the data. |
| 78 result[0] = 'M'; |
| 79 ASSERT_TRUE(memory.Read(address + 3, 0, &result[0])); |
| 80 EXPECT_EQ('M', result[0]); |
| 81 } |
| 82 |
| 83 TEST(TaskMemory, ReadSelfUnmapped) { |
| 84 vm_address_t address = 0; |
| 85 const vm_size_t kSize = 2 * PAGE_SIZE; |
| 86 kern_return_t kr = |
| 87 vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); |
| 88 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); |
| 89 base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); |
| 90 |
| 91 char* region = reinterpret_cast<char*>(address); |
| 92 for (size_t index = 0; index < kSize; ++index) { |
| 93 // Don’t include any NUL bytes, because ReadCString stops when it encounters |
| 94 // a NUL. |
| 95 region[index] = (index % 255) + 1; |
| 96 } |
| 97 |
| 98 kr = vm_protect( |
| 99 mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE); |
| 100 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_protect"); |
| 101 |
| 102 TaskMemory memory(mach_task_self()); |
| 103 std::string result(kSize, '\0'); |
| 104 |
| 105 EXPECT_FALSE(memory.Read(address, kSize, &result[0])); |
| 106 EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0])); |
| 107 EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0])); |
| 108 EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0])); |
| 109 EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0])); |
| 110 EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0])); |
| 111 |
| 112 // Repeat the test with an unmapped page instead of an unreadable one. This |
| 113 // portion of the test may be flaky in the presence of other threads, if |
| 114 // another thread maps something in the region that is deallocated here. |
| 115 kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE); |
| 116 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate"); |
| 117 vm_owner.reset(address, PAGE_SIZE); |
| 118 |
| 119 EXPECT_FALSE(memory.Read(address, kSize, &result[0])); |
| 120 EXPECT_FALSE(memory.Read(address + 1, kSize - 1, &result[0])); |
| 121 EXPECT_FALSE(memory.Read(address + PAGE_SIZE, 1, &result[0])); |
| 122 EXPECT_FALSE(memory.Read(address + PAGE_SIZE - 1, 2, &result[0])); |
| 123 EXPECT_TRUE(memory.Read(address, PAGE_SIZE, &result[0])); |
| 124 EXPECT_TRUE(memory.Read(address + PAGE_SIZE - 1, 1, &result[0])); |
| 125 } |
| 126 |
| 127 // This function consolidates the cast from a char* to mach_vm_address_t in one |
| 128 // location when reading from the current task. |
| 129 bool ReadCStringSelf(TaskMemory* memory, |
| 130 const char* pointer, |
| 131 std::string* result) { |
| 132 return memory->ReadCString(reinterpret_cast<mach_vm_address_t>(pointer), |
| 133 result); |
| 134 } |
| 135 |
| 136 TEST(TaskMemory, ReadCStringSelf) { |
| 137 TaskMemory memory(mach_task_self()); |
| 138 std::string result; |
| 139 |
| 140 const char kConstCharEmpty[] = ""; |
| 141 ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharEmpty, &result)); |
| 142 EXPECT_TRUE(result.empty()); |
| 143 EXPECT_EQ(kConstCharEmpty, result); |
| 144 |
| 145 const char kConstCharShort[] = "A short const char[]"; |
| 146 ASSERT_TRUE(ReadCStringSelf(&memory, kConstCharShort, &result)); |
| 147 EXPECT_FALSE(result.empty()); |
| 148 EXPECT_EQ(kConstCharShort, result); |
| 149 |
| 150 static const char kStaticConstCharEmpty[] = ""; |
| 151 ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharEmpty, &result)); |
| 152 EXPECT_TRUE(result.empty()); |
| 153 EXPECT_EQ(kStaticConstCharEmpty, result); |
| 154 |
| 155 static const char kStaticConstCharShort[] = "A short static const char[]"; |
| 156 ASSERT_TRUE(ReadCStringSelf(&memory, kStaticConstCharShort, &result)); |
| 157 EXPECT_FALSE(result.empty()); |
| 158 EXPECT_EQ(kStaticConstCharShort, result); |
| 159 |
| 160 std::string string_short("A short std::string in a function"); |
| 161 ASSERT_TRUE(ReadCStringSelf(&memory, &string_short[0], &result)); |
| 162 EXPECT_FALSE(result.empty()); |
| 163 EXPECT_EQ(string_short, result); |
| 164 |
| 165 std::string string_long; |
| 166 const size_t kStringLongSize = 4 * PAGE_SIZE; |
| 167 for (size_t index = 0; index < kStringLongSize; ++index) { |
| 168 // Don’t include any NUL bytes, because ReadCString stops when it encounters |
| 169 // a NUL. |
| 170 string_long.append(1, (index % 255) + 1); |
| 171 } |
| 172 ASSERT_EQ(kStringLongSize, string_long.size()); |
| 173 ASSERT_TRUE(ReadCStringSelf(&memory, &string_long[0], &result)); |
| 174 EXPECT_FALSE(result.empty()); |
| 175 EXPECT_EQ(kStringLongSize, result.size()); |
| 176 EXPECT_EQ(string_long, result); |
| 177 } |
| 178 |
| 179 TEST(TaskMemory, ReadCStringSelfUnmapped) { |
| 180 vm_address_t address = 0; |
| 181 const vm_size_t kSize = 2 * PAGE_SIZE; |
| 182 kern_return_t kr = |
| 183 vm_allocate(mach_task_self(), &address, kSize, VM_FLAGS_ANYWHERE); |
| 184 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_allocate"); |
| 185 base::mac::ScopedMachVM vm_owner(address, mach_vm_round_page(kSize)); |
| 186 |
| 187 char* region = reinterpret_cast<char*>(address); |
| 188 for (size_t index = 0; index < kSize; ++index) { |
| 189 // Don’t include any NUL bytes, because ReadCString stops when it encounters |
| 190 // a NUL. |
| 191 region[index] = (index % 255) + 1; |
| 192 } |
| 193 |
| 194 kr = vm_protect( |
| 195 mach_task_self(), address + PAGE_SIZE, PAGE_SIZE, FALSE, VM_PROT_NONE); |
| 196 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_protect"); |
| 197 |
| 198 TaskMemory memory(mach_task_self()); |
| 199 std::string result; |
| 200 EXPECT_FALSE(memory.ReadCString(address, &result)); |
| 201 |
| 202 // Make sure that if the string is NUL-terminated within the mapped memory |
| 203 // region, it can be read properly. |
| 204 char terminator_or_not = '\0'; |
| 205 std::swap(region[PAGE_SIZE - 1], terminator_or_not); |
| 206 ASSERT_TRUE(memory.ReadCString(address, &result)); |
| 207 EXPECT_FALSE(result.empty()); |
| 208 EXPECT_EQ(PAGE_SIZE - 1u, result.size()); |
| 209 EXPECT_EQ(region, result); |
| 210 |
| 211 // Repeat the test with an unmapped page instead of an unreadable one. This |
| 212 // portion of the test may be flaky in the presence of other threads, if |
| 213 // another thread maps something in the region that is deallocated here. |
| 214 std::swap(region[PAGE_SIZE - 1], terminator_or_not); |
| 215 kr = vm_deallocate(mach_task_self(), address + PAGE_SIZE, PAGE_SIZE); |
| 216 ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "vm_deallocate"); |
| 217 vm_owner.reset(address, PAGE_SIZE); |
| 218 |
| 219 EXPECT_FALSE(memory.ReadCString(address, &result)); |
| 220 |
| 221 // Clear the result before testing that the string can be read. This makes |
| 222 // sure that the result is actually filled in, because it already contains the |
| 223 // expected value from the tests above. |
| 224 result.clear(); |
| 225 std::swap(region[PAGE_SIZE - 1], terminator_or_not); |
| 226 ASSERT_TRUE(memory.ReadCString(address, &result)); |
| 227 EXPECT_FALSE(result.empty()); |
| 228 EXPECT_EQ(PAGE_SIZE - 1u, result.size()); |
| 229 EXPECT_EQ(region, result); |
| 230 } |
| 231 |
| 232 // This function consolidates the cast from a char* to mach_vm_address_t in one |
| 233 // location when reading from the current task. |
| 234 bool ReadCStringSizeLimitedSelf(TaskMemory* memory, |
| 235 const char* pointer, |
| 236 size_t size, |
| 237 std::string* result) { |
| 238 return memory->ReadCStringSizeLimited( |
| 239 reinterpret_cast<mach_vm_address_t>(pointer), size, result); |
| 240 } |
| 241 |
| 242 TEST(TaskMemory, ReadCStringSizeLimited_ConstCharEmpty) { |
| 243 TaskMemory memory(mach_task_self()); |
| 244 std::string result; |
| 245 |
| 246 const char kConstCharEmpty[] = ""; |
| 247 ASSERT_TRUE(ReadCStringSizeLimitedSelf( |
| 248 &memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result)); |
| 249 EXPECT_TRUE(result.empty()); |
| 250 EXPECT_EQ(kConstCharEmpty, result); |
| 251 |
| 252 result.clear(); |
| 253 ASSERT_TRUE(ReadCStringSizeLimitedSelf( |
| 254 &memory, kConstCharEmpty, arraysize(kConstCharEmpty) + 1, &result)); |
| 255 EXPECT_TRUE(result.empty()); |
| 256 EXPECT_EQ(kConstCharEmpty, result); |
| 257 |
| 258 result.clear(); |
| 259 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, kConstCharEmpty, 0, &result)); |
| 260 EXPECT_TRUE(result.empty()); |
| 261 EXPECT_EQ(kConstCharEmpty, result); |
| 262 } |
| 263 |
| 264 TEST(TaskMemory, ReadCStringSizeLimited_ConstCharShort) { |
| 265 TaskMemory memory(mach_task_self()); |
| 266 std::string result; |
| 267 |
| 268 const char kConstCharShort[] = "A short const char[]"; |
| 269 ASSERT_TRUE(ReadCStringSizeLimitedSelf( |
| 270 &memory, kConstCharShort, arraysize(kConstCharShort), &result)); |
| 271 EXPECT_FALSE(result.empty()); |
| 272 EXPECT_EQ(kConstCharShort, result); |
| 273 |
| 274 result.clear(); |
| 275 ASSERT_TRUE(ReadCStringSizeLimitedSelf( |
| 276 &memory, kConstCharShort, arraysize(kConstCharShort) + 1, &result)); |
| 277 EXPECT_FALSE(result.empty()); |
| 278 EXPECT_EQ(kConstCharShort, result); |
| 279 |
| 280 ASSERT_FALSE(ReadCStringSizeLimitedSelf( |
| 281 &memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result)); |
| 282 } |
| 283 |
| 284 TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharEmpty) { |
| 285 TaskMemory memory(mach_task_self()); |
| 286 std::string result; |
| 287 |
| 288 static const char kStaticConstCharEmpty[] = ""; |
| 289 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, |
| 290 kStaticConstCharEmpty, |
| 291 arraysize(kStaticConstCharEmpty), |
| 292 &result)); |
| 293 EXPECT_TRUE(result.empty()); |
| 294 EXPECT_EQ(kStaticConstCharEmpty, result); |
| 295 |
| 296 result.clear(); |
| 297 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, |
| 298 kStaticConstCharEmpty, |
| 299 arraysize(kStaticConstCharEmpty) + 1, |
| 300 &result)); |
| 301 EXPECT_TRUE(result.empty()); |
| 302 EXPECT_EQ(kStaticConstCharEmpty, result); |
| 303 |
| 304 result.clear(); |
| 305 ASSERT_TRUE( |
| 306 ReadCStringSizeLimitedSelf(&memory, kStaticConstCharEmpty, 0, &result)); |
| 307 EXPECT_TRUE(result.empty()); |
| 308 EXPECT_EQ(kStaticConstCharEmpty, result); |
| 309 } |
| 310 |
| 311 TEST(TaskMemory, ReadCStringSizeLimited_StaticConstCharShort) { |
| 312 TaskMemory memory(mach_task_self()); |
| 313 std::string result; |
| 314 |
| 315 static const char kStaticConstCharShort[] = "A short static const char[]"; |
| 316 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, |
| 317 kStaticConstCharShort, |
| 318 arraysize(kStaticConstCharShort), |
| 319 &result)); |
| 320 EXPECT_FALSE(result.empty()); |
| 321 EXPECT_EQ(kStaticConstCharShort, result); |
| 322 |
| 323 result.clear(); |
| 324 ASSERT_TRUE(ReadCStringSizeLimitedSelf(&memory, |
| 325 kStaticConstCharShort, |
| 326 arraysize(kStaticConstCharShort) + 1, |
| 327 &result)); |
| 328 EXPECT_FALSE(result.empty()); |
| 329 EXPECT_EQ(kStaticConstCharShort, result); |
| 330 |
| 331 ASSERT_FALSE(ReadCStringSizeLimitedSelf(&memory, |
| 332 kStaticConstCharShort, |
| 333 arraysize(kStaticConstCharShort) - 1, |
| 334 &result)); |
| 335 } |
| 336 |
| 337 TEST(TaskMemory, ReadCStringSizeLimited_StringShort) { |
| 338 TaskMemory memory(mach_task_self()); |
| 339 std::string result; |
| 340 |
| 341 std::string string_short("A short std::string in a function"); |
| 342 ASSERT_TRUE(ReadCStringSizeLimitedSelf( |
| 343 &memory, &string_short[0], string_short.size() + 1, &result)); |
| 344 EXPECT_FALSE(result.empty()); |
| 345 EXPECT_EQ(string_short, result); |
| 346 |
| 347 result.clear(); |
| 348 ASSERT_TRUE(ReadCStringSizeLimitedSelf( |
| 349 &memory, &string_short[0], string_short.size() + 2, &result)); |
| 350 EXPECT_FALSE(result.empty()); |
| 351 EXPECT_EQ(string_short, result); |
| 352 |
| 353 ASSERT_FALSE(ReadCStringSizeLimitedSelf( |
| 354 &memory, &string_short[0], string_short.size(), &result)); |
| 355 } |
| 356 |
| 357 TEST(TaskMemory, ReadCStringSizeLimited_StringLong) { |
| 358 TaskMemory memory(mach_task_self()); |
| 359 std::string result; |
| 360 |
| 361 std::string string_long; |
| 362 const size_t kStringLongSize = 4 * PAGE_SIZE; |
| 363 for (size_t index = 0; index < kStringLongSize; ++index) { |
| 364 // Don’t include any NUL bytes, because ReadCString stops when it encounters |
| 365 // a NUL. |
| 366 string_long.append(1, (index % 255) + 1); |
| 367 } |
| 368 ASSERT_EQ(kStringLongSize, string_long.size()); |
| 369 ASSERT_TRUE(ReadCStringSizeLimitedSelf( |
| 370 &memory, &string_long[0], string_long.size() + 1, &result)); |
| 371 EXPECT_FALSE(result.empty()); |
| 372 EXPECT_EQ(kStringLongSize, result.size()); |
| 373 EXPECT_EQ(string_long, result); |
| 374 |
| 375 result.clear(); |
| 376 ASSERT_TRUE(ReadCStringSizeLimitedSelf( |
| 377 &memory, &string_long[0], string_long.size() + 2, &result)); |
| 378 EXPECT_FALSE(result.empty()); |
| 379 EXPECT_EQ(kStringLongSize, result.size()); |
| 380 EXPECT_EQ(string_long, result); |
| 381 |
| 382 ASSERT_FALSE(ReadCStringSizeLimitedSelf( |
| 383 &memory, &string_long[0], string_long.size(), &result)); |
| 384 } |
| 385 |
| 386 } // namespace |
OLD | NEW |