| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "base/atomicops.h" | |
| 6 #include "base/basictypes.h" | |
| 7 #include "base/memory/scoped_ptr.h" | |
| 8 #include "base/memory/shared_memory.h" | |
| 9 #include "base/process/kill.h" | |
| 10 #include "base/rand_util.h" | |
| 11 #include "base/strings/string_number_conversions.h" | |
| 12 #include "base/sys_info.h" | |
| 13 #include "base/test/multiprocess_test.h" | |
| 14 #include "base/threading/platform_thread.h" | |
| 15 #include "base/time/time.h" | |
| 16 #include "testing/gtest/include/gtest/gtest.h" | |
| 17 #include "testing/multiprocess_func_list.h" | |
| 18 | |
| 19 #if defined(OS_MACOSX) | |
| 20 #include "base/mac/scoped_nsautorelease_pool.h" | |
| 21 #endif | |
| 22 | |
| 23 #if defined(OS_POSIX) | |
| 24 #include <errno.h> | |
| 25 #include <fcntl.h> | |
| 26 #include <sys/mman.h> | |
| 27 #include <sys/stat.h> | |
| 28 #include <sys/types.h> | |
| 29 #include <unistd.h> | |
| 30 #endif | |
| 31 | |
| 32 #if defined(OS_WIN) | |
| 33 #include "base/win/scoped_handle.h" | |
| 34 #endif | |
| 35 | |
| 36 static const int kNumThreads = 5; | |
| 37 #if !defined(OS_IOS) && !defined(OS_ANDROID) | |
| 38 static const int kNumTasks = 5; | |
| 39 #endif | |
| 40 | |
| 41 namespace base { | |
| 42 | |
| 43 namespace { | |
| 44 | |
| 45 // Each thread will open the shared memory. Each thread will take a different 4 | |
| 46 // byte int pointer, and keep changing it, with some small pauses in between. | |
| 47 // Verify that each thread's value in the shared memory is always correct. | |
| 48 class MultipleThreadMain : public PlatformThread::Delegate { | |
| 49 public: | |
| 50 explicit MultipleThreadMain(int16 id) : id_(id) {} | |
| 51 ~MultipleThreadMain() override {} | |
| 52 | |
| 53 static void CleanUp() { | |
| 54 SharedMemory memory; | |
| 55 memory.Delete(s_test_name_); | |
| 56 } | |
| 57 | |
| 58 // PlatformThread::Delegate interface. | |
| 59 void ThreadMain() override { | |
| 60 #if defined(OS_MACOSX) | |
| 61 mac::ScopedNSAutoreleasePool pool; | |
| 62 #endif | |
| 63 const uint32 kDataSize = 1024; | |
| 64 SharedMemory memory; | |
| 65 bool rv = memory.CreateNamedDeprecated(s_test_name_, true, kDataSize); | |
| 66 EXPECT_TRUE(rv); | |
| 67 rv = memory.Map(kDataSize); | |
| 68 EXPECT_TRUE(rv); | |
| 69 int *ptr = static_cast<int*>(memory.memory()) + id_; | |
| 70 EXPECT_EQ(0, *ptr); | |
| 71 | |
| 72 for (int idx = 0; idx < 100; idx++) { | |
| 73 *ptr = idx; | |
| 74 PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1)); | |
| 75 EXPECT_EQ(*ptr, idx); | |
| 76 } | |
| 77 // Reset back to 0 for the next test that uses the same name. | |
| 78 *ptr = 0; | |
| 79 | |
| 80 memory.Close(); | |
| 81 } | |
| 82 | |
| 83 private: | |
| 84 int16 id_; | |
| 85 | |
| 86 static const char* const s_test_name_; | |
| 87 | |
| 88 DISALLOW_COPY_AND_ASSIGN(MultipleThreadMain); | |
| 89 }; | |
| 90 | |
| 91 const char* const MultipleThreadMain::s_test_name_ = | |
| 92 "SharedMemoryOpenThreadTest"; | |
| 93 | |
| 94 } // namespace | |
| 95 | |
| 96 // Android doesn't support SharedMemory::Open/Delete/ | |
| 97 // CreateNamedDeprecated(openExisting=true) | |
| 98 #if !defined(OS_ANDROID) | |
| 99 TEST(SharedMemoryTest, OpenClose) { | |
| 100 const uint32 kDataSize = 1024; | |
| 101 std::string test_name = "SharedMemoryOpenCloseTest"; | |
| 102 | |
| 103 // Open two handles to a memory segment, confirm that they are mapped | |
| 104 // separately yet point to the same space. | |
| 105 SharedMemory memory1; | |
| 106 bool rv = memory1.Delete(test_name); | |
| 107 EXPECT_TRUE(rv); | |
| 108 rv = memory1.Delete(test_name); | |
| 109 EXPECT_TRUE(rv); | |
| 110 rv = memory1.Open(test_name, false); | |
| 111 EXPECT_FALSE(rv); | |
| 112 rv = memory1.CreateNamedDeprecated(test_name, false, kDataSize); | |
| 113 EXPECT_TRUE(rv); | |
| 114 rv = memory1.Map(kDataSize); | |
| 115 EXPECT_TRUE(rv); | |
| 116 SharedMemory memory2; | |
| 117 rv = memory2.Open(test_name, false); | |
| 118 EXPECT_TRUE(rv); | |
| 119 rv = memory2.Map(kDataSize); | |
| 120 EXPECT_TRUE(rv); | |
| 121 EXPECT_NE(memory1.memory(), memory2.memory()); // Compare the pointers. | |
| 122 | |
| 123 // Make sure we don't segfault. (it actually happened!) | |
| 124 ASSERT_NE(memory1.memory(), static_cast<void*>(NULL)); | |
| 125 ASSERT_NE(memory2.memory(), static_cast<void*>(NULL)); | |
| 126 | |
| 127 // Write data to the first memory segment, verify contents of second. | |
| 128 memset(memory1.memory(), '1', kDataSize); | |
| 129 EXPECT_EQ(memcmp(memory1.memory(), memory2.memory(), kDataSize), 0); | |
| 130 | |
| 131 // Close the first memory segment, and verify the second has the right data. | |
| 132 memory1.Close(); | |
| 133 char *start_ptr = static_cast<char *>(memory2.memory()); | |
| 134 char *end_ptr = start_ptr + kDataSize; | |
| 135 for (char* ptr = start_ptr; ptr < end_ptr; ptr++) | |
| 136 EXPECT_EQ(*ptr, '1'); | |
| 137 | |
| 138 // Close the second memory segment. | |
| 139 memory2.Close(); | |
| 140 | |
| 141 rv = memory1.Delete(test_name); | |
| 142 EXPECT_TRUE(rv); | |
| 143 rv = memory2.Delete(test_name); | |
| 144 EXPECT_TRUE(rv); | |
| 145 } | |
| 146 | |
| 147 TEST(SharedMemoryTest, OpenExclusive) { | |
| 148 const uint32 kDataSize = 1024; | |
| 149 const uint32 kDataSize2 = 2048; | |
| 150 std::ostringstream test_name_stream; | |
| 151 test_name_stream << "SharedMemoryOpenExclusiveTest." | |
| 152 << Time::Now().ToDoubleT(); | |
| 153 std::string test_name = test_name_stream.str(); | |
| 154 | |
| 155 // Open two handles to a memory segment and check that | |
| 156 // open_existing_deprecated works as expected. | |
| 157 SharedMemory memory1; | |
| 158 bool rv = memory1.CreateNamedDeprecated(test_name, false, kDataSize); | |
| 159 EXPECT_TRUE(rv); | |
| 160 | |
| 161 // Memory1 knows it's size because it created it. | |
| 162 EXPECT_EQ(memory1.requested_size(), kDataSize); | |
| 163 | |
| 164 rv = memory1.Map(kDataSize); | |
| 165 EXPECT_TRUE(rv); | |
| 166 | |
| 167 // The mapped memory1 must be at least the size we asked for. | |
| 168 EXPECT_GE(memory1.mapped_size(), kDataSize); | |
| 169 | |
| 170 // The mapped memory1 shouldn't exceed rounding for allocation granularity. | |
| 171 EXPECT_LT(memory1.mapped_size(), | |
| 172 kDataSize + base::SysInfo::VMAllocationGranularity()); | |
| 173 | |
| 174 memset(memory1.memory(), 'G', kDataSize); | |
| 175 | |
| 176 SharedMemory memory2; | |
| 177 // Should not be able to create if openExisting is false. | |
| 178 rv = memory2.CreateNamedDeprecated(test_name, false, kDataSize2); | |
| 179 EXPECT_FALSE(rv); | |
| 180 | |
| 181 // Should be able to create with openExisting true. | |
| 182 rv = memory2.CreateNamedDeprecated(test_name, true, kDataSize2); | |
| 183 EXPECT_TRUE(rv); | |
| 184 | |
| 185 // Memory2 shouldn't know the size because we didn't create it. | |
| 186 EXPECT_EQ(memory2.requested_size(), 0U); | |
| 187 | |
| 188 // We should be able to map the original size. | |
| 189 rv = memory2.Map(kDataSize); | |
| 190 EXPECT_TRUE(rv); | |
| 191 | |
| 192 // The mapped memory2 must be at least the size of the original. | |
| 193 EXPECT_GE(memory2.mapped_size(), kDataSize); | |
| 194 | |
| 195 // The mapped memory2 shouldn't exceed rounding for allocation granularity. | |
| 196 EXPECT_LT(memory2.mapped_size(), | |
| 197 kDataSize2 + base::SysInfo::VMAllocationGranularity()); | |
| 198 | |
| 199 // Verify that opening memory2 didn't truncate or delete memory 1. | |
| 200 char *start_ptr = static_cast<char *>(memory2.memory()); | |
| 201 char *end_ptr = start_ptr + kDataSize; | |
| 202 for (char* ptr = start_ptr; ptr < end_ptr; ptr++) { | |
| 203 EXPECT_EQ(*ptr, 'G'); | |
| 204 } | |
| 205 | |
| 206 memory1.Close(); | |
| 207 memory2.Close(); | |
| 208 | |
| 209 rv = memory1.Delete(test_name); | |
| 210 EXPECT_TRUE(rv); | |
| 211 } | |
| 212 #endif | |
| 213 | |
| 214 // Check that memory is still mapped after its closed. | |
| 215 TEST(SharedMemoryTest, CloseNoUnmap) { | |
| 216 const size_t kDataSize = 4096; | |
| 217 | |
| 218 SharedMemory memory; | |
| 219 ASSERT_TRUE(memory.CreateAndMapAnonymous(kDataSize)); | |
| 220 char* ptr = static_cast<char*>(memory.memory()); | |
| 221 ASSERT_NE(ptr, static_cast<void*>(NULL)); | |
| 222 memset(ptr, 'G', kDataSize); | |
| 223 | |
| 224 memory.Close(); | |
| 225 | |
| 226 EXPECT_EQ(ptr, memory.memory()); | |
| 227 EXPECT_EQ(SharedMemory::NULLHandle(), memory.handle()); | |
| 228 | |
| 229 for (size_t i = 0; i < kDataSize; i++) { | |
| 230 EXPECT_EQ('G', ptr[i]); | |
| 231 } | |
| 232 | |
| 233 memory.Unmap(); | |
| 234 EXPECT_EQ(nullptr, memory.memory()); | |
| 235 } | |
| 236 | |
| 237 // Create a set of N threads to each open a shared memory segment and write to | |
| 238 // it. Verify that they are always reading/writing consistent data. | |
| 239 TEST(SharedMemoryTest, MultipleThreads) { | |
| 240 MultipleThreadMain::CleanUp(); | |
| 241 // On POSIX we have a problem when 2 threads try to create the shmem | |
| 242 // (a file) at exactly the same time, since create both creates the | |
| 243 // file and zerofills it. We solve the problem for this unit test | |
| 244 // (make it not flaky) by starting with 1 thread, then | |
| 245 // intentionally don't clean up its shmem before running with | |
| 246 // kNumThreads. | |
| 247 | |
| 248 int threadcounts[] = { 1, kNumThreads }; | |
| 249 for (size_t i = 0; i < arraysize(threadcounts); i++) { | |
| 250 int numthreads = threadcounts[i]; | |
| 251 scoped_ptr<PlatformThreadHandle[]> thread_handles; | |
| 252 scoped_ptr<MultipleThreadMain*[]> thread_delegates; | |
| 253 | |
| 254 thread_handles.reset(new PlatformThreadHandle[numthreads]); | |
| 255 thread_delegates.reset(new MultipleThreadMain*[numthreads]); | |
| 256 | |
| 257 // Spawn the threads. | |
| 258 for (int16 index = 0; index < numthreads; index++) { | |
| 259 PlatformThreadHandle pth; | |
| 260 thread_delegates[index] = new MultipleThreadMain(index); | |
| 261 EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth)); | |
| 262 thread_handles[index] = pth; | |
| 263 } | |
| 264 | |
| 265 // Wait for the threads to finish. | |
| 266 for (int index = 0; index < numthreads; index++) { | |
| 267 PlatformThread::Join(thread_handles[index]); | |
| 268 delete thread_delegates[index]; | |
| 269 } | |
| 270 } | |
| 271 MultipleThreadMain::CleanUp(); | |
| 272 } | |
| 273 | |
| 274 // Allocate private (unique) shared memory with an empty string for a | |
| 275 // name. Make sure several of them don't point to the same thing as | |
| 276 // we might expect if the names are equal. | |
| 277 TEST(SharedMemoryTest, AnonymousPrivate) { | |
| 278 int i, j; | |
| 279 int count = 4; | |
| 280 bool rv; | |
| 281 const uint32 kDataSize = 8192; | |
| 282 | |
| 283 scoped_ptr<SharedMemory[]> memories(new SharedMemory[count]); | |
| 284 scoped_ptr<int*[]> pointers(new int*[count]); | |
| 285 ASSERT_TRUE(memories.get()); | |
| 286 ASSERT_TRUE(pointers.get()); | |
| 287 | |
| 288 for (i = 0; i < count; i++) { | |
| 289 rv = memories[i].CreateAndMapAnonymous(kDataSize); | |
| 290 EXPECT_TRUE(rv); | |
| 291 int *ptr = static_cast<int*>(memories[i].memory()); | |
| 292 EXPECT_TRUE(ptr); | |
| 293 pointers[i] = ptr; | |
| 294 } | |
| 295 | |
| 296 for (i = 0; i < count; i++) { | |
| 297 // zero out the first int in each except for i; for that one, make it 100. | |
| 298 for (j = 0; j < count; j++) { | |
| 299 if (i == j) | |
| 300 pointers[j][0] = 100; | |
| 301 else | |
| 302 pointers[j][0] = 0; | |
| 303 } | |
| 304 // make sure there is no bleeding of the 100 into the other pointers | |
| 305 for (j = 0; j < count; j++) { | |
| 306 if (i == j) | |
| 307 EXPECT_EQ(100, pointers[j][0]); | |
| 308 else | |
| 309 EXPECT_EQ(0, pointers[j][0]); | |
| 310 } | |
| 311 } | |
| 312 | |
| 313 for (int i = 0; i < count; i++) { | |
| 314 memories[i].Close(); | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 TEST(SharedMemoryTest, ShareReadOnly) { | |
| 319 StringPiece contents = "Hello World"; | |
| 320 | |
| 321 SharedMemory writable_shmem; | |
| 322 SharedMemoryCreateOptions options; | |
| 323 options.size = contents.size(); | |
| 324 options.share_read_only = true; | |
| 325 ASSERT_TRUE(writable_shmem.Create(options)); | |
| 326 ASSERT_TRUE(writable_shmem.Map(options.size)); | |
| 327 memcpy(writable_shmem.memory(), contents.data(), contents.size()); | |
| 328 EXPECT_TRUE(writable_shmem.Unmap()); | |
| 329 | |
| 330 SharedMemoryHandle readonly_handle; | |
| 331 ASSERT_TRUE(writable_shmem.ShareReadOnlyToProcess(GetCurrentProcessHandle(), | |
| 332 &readonly_handle)); | |
| 333 SharedMemory readonly_shmem(readonly_handle, /*readonly=*/true); | |
| 334 | |
| 335 ASSERT_TRUE(readonly_shmem.Map(contents.size())); | |
| 336 EXPECT_EQ(contents, | |
| 337 StringPiece(static_cast<const char*>(readonly_shmem.memory()), | |
| 338 contents.size())); | |
| 339 EXPECT_TRUE(readonly_shmem.Unmap()); | |
| 340 | |
| 341 // Make sure the writable instance is still writable. | |
| 342 ASSERT_TRUE(writable_shmem.Map(contents.size())); | |
| 343 StringPiece new_contents = "Goodbye"; | |
| 344 memcpy(writable_shmem.memory(), new_contents.data(), new_contents.size()); | |
| 345 EXPECT_EQ(new_contents, | |
| 346 StringPiece(static_cast<const char*>(writable_shmem.memory()), | |
| 347 new_contents.size())); | |
| 348 | |
| 349 // We'd like to check that if we send the read-only segment to another | |
| 350 // process, then that other process can't reopen it read/write. (Since that | |
| 351 // would be a security hole.) Setting up multiple processes is hard in a | |
| 352 // unittest, so this test checks that the *current* process can't reopen the | |
| 353 // segment read/write. I think the test here is stronger than we actually | |
| 354 // care about, but there's a remote possibility that sending a file over a | |
| 355 // pipe would transform it into read/write. | |
| 356 SharedMemoryHandle handle = readonly_shmem.handle(); | |
| 357 | |
| 358 #if defined(OS_ANDROID) | |
| 359 // The "read-only" handle is still writable on Android: | |
| 360 // http://crbug.com/320865 | |
| 361 (void)handle; | |
| 362 #elif defined(OS_POSIX) | |
| 363 int handle_fd = SharedMemory::GetFdFromSharedMemoryHandle(handle); | |
| 364 EXPECT_EQ(O_RDONLY, fcntl(handle_fd, F_GETFL) & O_ACCMODE) | |
| 365 << "The descriptor itself should be read-only."; | |
| 366 | |
| 367 errno = 0; | |
| 368 void* writable = mmap(NULL, contents.size(), PROT_READ | PROT_WRITE, | |
| 369 MAP_SHARED, handle_fd, 0); | |
| 370 int mmap_errno = errno; | |
| 371 EXPECT_EQ(MAP_FAILED, writable) | |
| 372 << "It shouldn't be possible to re-mmap the descriptor writable."; | |
| 373 EXPECT_EQ(EACCES, mmap_errno) << strerror(mmap_errno); | |
| 374 if (writable != MAP_FAILED) | |
| 375 EXPECT_EQ(0, munmap(writable, readonly_shmem.mapped_size())); | |
| 376 | |
| 377 #elif defined(OS_WIN) | |
| 378 EXPECT_EQ(NULL, MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, 0)) | |
| 379 << "Shouldn't be able to map memory writable."; | |
| 380 | |
| 381 HANDLE temp_handle; | |
| 382 BOOL rv = ::DuplicateHandle(GetCurrentProcess(), | |
| 383 handle, | |
| 384 GetCurrentProcess(), | |
| 385 &temp_handle, | |
| 386 FILE_MAP_ALL_ACCESS, | |
| 387 false, | |
| 388 0); | |
| 389 EXPECT_EQ(FALSE, rv) | |
| 390 << "Shouldn't be able to duplicate the handle into a writable one."; | |
| 391 if (rv) | |
| 392 base::win::ScopedHandle writable_handle(temp_handle); | |
| 393 rv = ::DuplicateHandle(GetCurrentProcess(), | |
| 394 handle, | |
| 395 GetCurrentProcess(), | |
| 396 &temp_handle, | |
| 397 FILE_MAP_READ, | |
| 398 false, | |
| 399 0); | |
| 400 EXPECT_EQ(TRUE, rv) | |
| 401 << "Should be able to duplicate the handle into a readable one."; | |
| 402 if (rv) | |
| 403 base::win::ScopedHandle writable_handle(temp_handle); | |
| 404 #else | |
| 405 #error Unexpected platform; write a test that tries to make 'handle' writable. | |
| 406 #endif // defined(OS_POSIX) || defined(OS_WIN) | |
| 407 } | |
| 408 | |
| 409 TEST(SharedMemoryTest, ShareToSelf) { | |
| 410 StringPiece contents = "Hello World"; | |
| 411 | |
| 412 SharedMemory shmem; | |
| 413 ASSERT_TRUE(shmem.CreateAndMapAnonymous(contents.size())); | |
| 414 memcpy(shmem.memory(), contents.data(), contents.size()); | |
| 415 EXPECT_TRUE(shmem.Unmap()); | |
| 416 | |
| 417 SharedMemoryHandle shared_handle; | |
| 418 ASSERT_TRUE(shmem.ShareToProcess(GetCurrentProcessHandle(), &shared_handle)); | |
| 419 SharedMemory shared(shared_handle, /*readonly=*/false); | |
| 420 | |
| 421 ASSERT_TRUE(shared.Map(contents.size())); | |
| 422 EXPECT_EQ( | |
| 423 contents, | |
| 424 StringPiece(static_cast<const char*>(shared.memory()), contents.size())); | |
| 425 | |
| 426 ASSERT_TRUE(shmem.ShareToProcess(GetCurrentProcessHandle(), &shared_handle)); | |
| 427 SharedMemory readonly(shared_handle, /*readonly=*/true); | |
| 428 | |
| 429 ASSERT_TRUE(readonly.Map(contents.size())); | |
| 430 EXPECT_EQ(contents, | |
| 431 StringPiece(static_cast<const char*>(readonly.memory()), | |
| 432 contents.size())); | |
| 433 } | |
| 434 | |
| 435 TEST(SharedMemoryTest, MapAt) { | |
| 436 ASSERT_TRUE(SysInfo::VMAllocationGranularity() >= sizeof(uint32)); | |
| 437 const size_t kCount = SysInfo::VMAllocationGranularity(); | |
| 438 const size_t kDataSize = kCount * sizeof(uint32); | |
| 439 | |
| 440 SharedMemory memory; | |
| 441 ASSERT_TRUE(memory.CreateAndMapAnonymous(kDataSize)); | |
| 442 uint32* ptr = static_cast<uint32*>(memory.memory()); | |
| 443 ASSERT_NE(ptr, static_cast<void*>(NULL)); | |
| 444 | |
| 445 for (size_t i = 0; i < kCount; ++i) { | |
| 446 ptr[i] = i; | |
| 447 } | |
| 448 | |
| 449 memory.Unmap(); | |
| 450 | |
| 451 off_t offset = SysInfo::VMAllocationGranularity(); | |
| 452 ASSERT_TRUE(memory.MapAt(offset, kDataSize - offset)); | |
| 453 offset /= sizeof(uint32); | |
| 454 ptr = static_cast<uint32*>(memory.memory()); | |
| 455 ASSERT_NE(ptr, static_cast<void*>(NULL)); | |
| 456 for (size_t i = offset; i < kCount; ++i) { | |
| 457 EXPECT_EQ(ptr[i - offset], i); | |
| 458 } | |
| 459 } | |
| 460 | |
| 461 TEST(SharedMemoryTest, MapTwice) { | |
| 462 const uint32 kDataSize = 1024; | |
| 463 SharedMemory memory; | |
| 464 bool rv = memory.CreateAndMapAnonymous(kDataSize); | |
| 465 EXPECT_TRUE(rv); | |
| 466 | |
| 467 void* old_address = memory.memory(); | |
| 468 | |
| 469 rv = memory.Map(kDataSize); | |
| 470 EXPECT_FALSE(rv); | |
| 471 EXPECT_EQ(old_address, memory.memory()); | |
| 472 } | |
| 473 | |
| 474 #if defined(OS_POSIX) | |
| 475 // This test is not applicable for iOS (crbug.com/399384). | |
| 476 #if !defined(OS_IOS) | |
| 477 // Create a shared memory object, mmap it, and mprotect it to PROT_EXEC. | |
| 478 TEST(SharedMemoryTest, AnonymousExecutable) { | |
| 479 const uint32 kTestSize = 1 << 16; | |
| 480 | |
| 481 SharedMemory shared_memory; | |
| 482 SharedMemoryCreateOptions options; | |
| 483 options.size = kTestSize; | |
| 484 options.executable = true; | |
| 485 | |
| 486 EXPECT_TRUE(shared_memory.Create(options)); | |
| 487 EXPECT_TRUE(shared_memory.Map(shared_memory.requested_size())); | |
| 488 | |
| 489 EXPECT_EQ(0, mprotect(shared_memory.memory(), shared_memory.requested_size(), | |
| 490 PROT_READ | PROT_EXEC)); | |
| 491 } | |
| 492 #endif // !defined(OS_IOS) | |
| 493 | |
| 494 // Android supports a different permission model than POSIX for its "ashmem" | |
| 495 // shared memory implementation. So the tests about file permissions are not | |
| 496 // included on Android. | |
| 497 #if !defined(OS_ANDROID) | |
| 498 | |
| 499 // Set a umask and restore the old mask on destruction. | |
| 500 class ScopedUmaskSetter { | |
| 501 public: | |
| 502 explicit ScopedUmaskSetter(mode_t target_mask) { | |
| 503 old_umask_ = umask(target_mask); | |
| 504 } | |
| 505 ~ScopedUmaskSetter() { umask(old_umask_); } | |
| 506 private: | |
| 507 mode_t old_umask_; | |
| 508 DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedUmaskSetter); | |
| 509 }; | |
| 510 | |
| 511 // Create a shared memory object, check its permissions. | |
| 512 TEST(SharedMemoryTest, FilePermissionsAnonymous) { | |
| 513 const uint32 kTestSize = 1 << 8; | |
| 514 | |
| 515 SharedMemory shared_memory; | |
| 516 SharedMemoryCreateOptions options; | |
| 517 options.size = kTestSize; | |
| 518 // Set a file mode creation mask that gives all permissions. | |
| 519 ScopedUmaskSetter permissive_mask(S_IWGRP | S_IWOTH); | |
| 520 | |
| 521 EXPECT_TRUE(shared_memory.Create(options)); | |
| 522 | |
| 523 int shm_fd = | |
| 524 SharedMemory::GetFdFromSharedMemoryHandle(shared_memory.handle()); | |
| 525 struct stat shm_stat; | |
| 526 EXPECT_EQ(0, fstat(shm_fd, &shm_stat)); | |
| 527 // Neither the group, nor others should be able to read the shared memory | |
| 528 // file. | |
| 529 EXPECT_FALSE(shm_stat.st_mode & S_IRWXO); | |
| 530 EXPECT_FALSE(shm_stat.st_mode & S_IRWXG); | |
| 531 } | |
| 532 | |
| 533 // Create a shared memory object, check its permissions. | |
| 534 TEST(SharedMemoryTest, FilePermissionsNamed) { | |
| 535 const uint32 kTestSize = 1 << 8; | |
| 536 | |
| 537 SharedMemory shared_memory; | |
| 538 SharedMemoryCreateOptions options; | |
| 539 options.size = kTestSize; | |
| 540 std::string shared_mem_name = "shared_perm_test-" + IntToString(getpid()) + | |
| 541 "-" + Uint64ToString(RandUint64()); | |
| 542 options.name_deprecated = &shared_mem_name; | |
| 543 // Set a file mode creation mask that gives all permissions. | |
| 544 ScopedUmaskSetter permissive_mask(S_IWGRP | S_IWOTH); | |
| 545 | |
| 546 EXPECT_TRUE(shared_memory.Create(options)); | |
| 547 // Clean-up the backing file name immediately, we don't need it. | |
| 548 EXPECT_TRUE(shared_memory.Delete(shared_mem_name)); | |
| 549 | |
| 550 int shm_fd = | |
| 551 SharedMemory::GetFdFromSharedMemoryHandle(shared_memory.handle()); | |
| 552 struct stat shm_stat; | |
| 553 EXPECT_EQ(0, fstat(shm_fd, &shm_stat)); | |
| 554 // Neither the group, nor others should have been able to open the shared | |
| 555 // memory file while its name existed. | |
| 556 EXPECT_FALSE(shm_stat.st_mode & S_IRWXO); | |
| 557 EXPECT_FALSE(shm_stat.st_mode & S_IRWXG); | |
| 558 } | |
| 559 #endif // !defined(OS_ANDROID) | |
| 560 | |
| 561 #endif // defined(OS_POSIX) | |
| 562 | |
| 563 // Map() will return addresses which are aligned to the platform page size, this | |
| 564 // varies from platform to platform though. Since we'd like to advertise a | |
| 565 // minimum alignment that callers can count on, test for it here. | |
| 566 TEST(SharedMemoryTest, MapMinimumAlignment) { | |
| 567 static const int kDataSize = 8192; | |
| 568 | |
| 569 SharedMemory shared_memory; | |
| 570 ASSERT_TRUE(shared_memory.CreateAndMapAnonymous(kDataSize)); | |
| 571 EXPECT_EQ(0U, reinterpret_cast<uintptr_t>( | |
| 572 shared_memory.memory()) & (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1)); | |
| 573 shared_memory.Close(); | |
| 574 } | |
| 575 | |
| 576 // iOS does not allow multiple processes. | |
| 577 // Android ashmem doesn't support named shared memory. | |
| 578 #if !defined(OS_IOS) && !defined(OS_ANDROID) | |
| 579 | |
| 580 // On POSIX it is especially important we test shmem across processes, | |
| 581 // not just across threads. But the test is enabled on all platforms. | |
| 582 class SharedMemoryProcessTest : public MultiProcessTest { | |
| 583 public: | |
| 584 | |
| 585 static void CleanUp() { | |
| 586 SharedMemory memory; | |
| 587 memory.Delete(s_test_name_); | |
| 588 } | |
| 589 | |
| 590 static int TaskTestMain() { | |
| 591 int errors = 0; | |
| 592 #if defined(OS_MACOSX) | |
| 593 mac::ScopedNSAutoreleasePool pool; | |
| 594 #endif | |
| 595 SharedMemory memory; | |
| 596 bool rv = memory.CreateNamedDeprecated(s_test_name_, true, s_data_size_); | |
| 597 EXPECT_TRUE(rv); | |
| 598 if (rv != true) | |
| 599 errors++; | |
| 600 rv = memory.Map(s_data_size_); | |
| 601 EXPECT_TRUE(rv); | |
| 602 if (rv != true) | |
| 603 errors++; | |
| 604 int *ptr = static_cast<int*>(memory.memory()); | |
| 605 | |
| 606 // This runs concurrently in multiple processes. Writes need to be atomic. | |
| 607 base::subtle::Barrier_AtomicIncrement(ptr, 1); | |
| 608 memory.Close(); | |
| 609 return errors; | |
| 610 } | |
| 611 | |
| 612 static const char* const s_test_name_; | |
| 613 static const uint32 s_data_size_; | |
| 614 }; | |
| 615 | |
| 616 const char* const SharedMemoryProcessTest::s_test_name_ = "MPMem"; | |
| 617 const uint32 SharedMemoryProcessTest::s_data_size_ = 1024; | |
| 618 | |
| 619 TEST_F(SharedMemoryProcessTest, SharedMemoryAcrossProcesses) { | |
| 620 SharedMemoryProcessTest::CleanUp(); | |
| 621 | |
| 622 // Create a shared memory region. Set the first word to 0. | |
| 623 SharedMemory memory; | |
| 624 bool rv = memory.CreateNamedDeprecated(s_test_name_, true, s_data_size_); | |
| 625 ASSERT_TRUE(rv); | |
| 626 rv = memory.Map(s_data_size_); | |
| 627 ASSERT_TRUE(rv); | |
| 628 int* ptr = static_cast<int*>(memory.memory()); | |
| 629 *ptr = 0; | |
| 630 | |
| 631 // Start |kNumTasks| processes, each of which atomically increments the first | |
| 632 // word by 1. | |
| 633 Process processes[kNumTasks]; | |
| 634 for (int index = 0; index < kNumTasks; ++index) { | |
| 635 processes[index] = SpawnChild("SharedMemoryTestMain"); | |
| 636 ASSERT_TRUE(processes[index].IsValid()); | |
| 637 } | |
| 638 | |
| 639 // Check that each process exited correctly. | |
| 640 int exit_code = 0; | |
| 641 for (int index = 0; index < kNumTasks; ++index) { | |
| 642 EXPECT_TRUE(processes[index].WaitForExit(&exit_code)); | |
| 643 EXPECT_EQ(0, exit_code); | |
| 644 } | |
| 645 | |
| 646 // Check that the shared memory region reflects |kNumTasks| increments. | |
| 647 ASSERT_EQ(kNumTasks, *ptr); | |
| 648 | |
| 649 memory.Close(); | |
| 650 SharedMemoryProcessTest::CleanUp(); | |
| 651 } | |
| 652 | |
| 653 MULTIPROCESS_TEST_MAIN(SharedMemoryTestMain) { | |
| 654 return SharedMemoryProcessTest::TaskTestMain(); | |
| 655 } | |
| 656 | |
| 657 #endif // !defined(OS_IOS) && !defined(OS_ANDROID) | |
| 658 | |
| 659 } // namespace base | |
| OLD | NEW |