diff options
Diffstat (limited to 'chromium/base/memory/shared_memory_unittest.cc')
-rw-r--r-- | chromium/base/memory/shared_memory_unittest.cc | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/chromium/base/memory/shared_memory_unittest.cc b/chromium/base/memory/shared_memory_unittest.cc index d3c31dbd569..b8f197807df 100644 --- a/chromium/base/memory/shared_memory_unittest.cc +++ b/chromium/base/memory/shared_memory_unittest.cc @@ -11,12 +11,15 @@ #include "base/atomicops.h" #include "base/base_switches.h" +#include "base/bind.h" #include "base/command_line.h" +#include "base/logging.h" #include "base/macros.h" #include "base/memory/shared_memory_handle.h" #include "base/process/kill.h" #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/sys_info.h" #include "base/test/multiprocess_test.h" @@ -382,6 +385,11 @@ TEST_P(SharedMemoryTest, GetReadOnlyHandle) { contents.size())); EXPECT_TRUE(readonly_shmem.Unmap()); +#if defined(OS_ANDROID) + // On Android, mapping a region through a read-only descriptor makes the + // region read-only. Any writable mapping attempt should fail. + ASSERT_FALSE(writable_shmem.Map(contents.size())); +#else // Make sure the writable instance is still writable. ASSERT_TRUE(writable_shmem.Map(contents.size())); StringPiece new_contents = "Goodbye"; @@ -389,6 +397,7 @@ TEST_P(SharedMemoryTest, GetReadOnlyHandle) { EXPECT_EQ(new_contents, StringPiece(static_cast<const char*>(writable_shmem.memory()), new_contents.size())); +#endif // We'd like to check that if we send the read-only segment to another // process, then that other process can't reopen it read/write. (Since that @@ -594,6 +603,29 @@ TEST_P(SharedMemoryTest, AnonymousExecutable) { } #endif // !defined(OS_IOS) +#if defined(OS_ANDROID) +// This test is restricted to Android since there is no way on other platforms +// to guarantee that a region can never be mapped with PROT_EXEC. E.g. on +// Linux, anonymous shared regions come from /dev/shm which can be mounted +// without 'noexec'. In this case, anything can perform an mprotect() to +// change the protection mask of a given page. +TEST(SharedMemoryTest, AnonymousIsNotExecutableByDefault) { + const uint32_t kTestSize = 1 << 16; + + SharedMemory shared_memory; + SharedMemoryCreateOptions options; + options.size = kTestSize; + + EXPECT_TRUE(shared_memory.Create(options)); + EXPECT_TRUE(shared_memory.Map(shared_memory.requested_size())); + + errno = 0; + EXPECT_EQ(-1, mprotect(shared_memory.memory(), shared_memory.requested_size(), + PROT_READ | PROT_EXEC)); + EXPECT_EQ(EACCES, errno); +} +#endif // OS_ANDROID + // Android supports a different permission model than POSIX for its "ashmem" // shared memory implementation. So the tests about file permissions are not // included on Android. Fuchsia does not use a file-backed shared memory @@ -844,4 +876,86 @@ INSTANTIATE_TEST_CASE_P(SkipDevShm, ::testing::Values(Mode::DisableDevShm)); #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) +#if defined(OS_ANDROID) +TEST(SharedMemoryTest, ReadOnlyRegions) { + const uint32_t kDataSize = 1024; + SharedMemory memory; + SharedMemoryCreateOptions options; + options.size = kDataSize; + EXPECT_TRUE(memory.Create(options)); + + EXPECT_FALSE(memory.handle().IsRegionReadOnly()); + + // Check that it is possible to map the region directly from the fd. + int region_fd = memory.handle().GetHandle(); + EXPECT_GE(region_fd, 0); + void* address = mmap(nullptr, kDataSize, PROT_READ | PROT_WRITE, MAP_SHARED, + region_fd, 0); + bool success = address && address != MAP_FAILED; + ASSERT_TRUE(address); + ASSERT_NE(address, MAP_FAILED); + if (success) { + EXPECT_EQ(0, munmap(address, kDataSize)); + } + + ASSERT_TRUE(memory.handle().SetRegionReadOnly()); + EXPECT_TRUE(memory.handle().IsRegionReadOnly()); + + // Check that it is no longer possible to map the region read/write. + errno = 0; + address = mmap(nullptr, kDataSize, PROT_READ | PROT_WRITE, MAP_SHARED, + region_fd, 0); + success = address && address != MAP_FAILED; + ASSERT_FALSE(success); + ASSERT_EQ(EPERM, errno); + if (success) { + EXPECT_EQ(0, munmap(address, kDataSize)); + } +} + +TEST(SharedMemoryTest, ReadOnlyDescriptors) { + const uint32_t kDataSize = 1024; + SharedMemory memory; + SharedMemoryCreateOptions options; + options.size = kDataSize; + EXPECT_TRUE(memory.Create(options)); + + EXPECT_FALSE(memory.handle().IsRegionReadOnly()); + + // Getting a read-only descriptor should not make the region read-only itself. + SharedMemoryHandle ro_handle = memory.GetReadOnlyHandle(); + EXPECT_FALSE(memory.handle().IsRegionReadOnly()); + + // Mapping a writable region from a read-only descriptor should not + // be possible, it will DCHECK() in debug builds (see test below), + // while returning false on release ones. + { + bool dcheck_fired = false; + logging::ScopedLogAssertHandler log_assert( + base::BindRepeating([](bool* flag, const char*, int, base::StringPiece, + base::StringPiece) { *flag = true; }, + base::Unretained(&dcheck_fired))); + + SharedMemory rw_region(ro_handle.Duplicate(), /* read_only */ false); + EXPECT_FALSE(rw_region.Map(kDataSize)); + EXPECT_EQ(DCHECK_IS_ON() ? true : false, dcheck_fired); + } + + // Nor shall it turn the region read-only itself. + EXPECT_FALSE(ro_handle.IsRegionReadOnly()); + + // Mapping a read-only region from a read-only descriptor should work. + SharedMemory ro_region(ro_handle.Duplicate(), /* read_only */ true); + EXPECT_TRUE(ro_region.Map(kDataSize)); + + // And it should turn the region read-only too. + EXPECT_TRUE(ro_handle.IsRegionReadOnly()); + EXPECT_TRUE(memory.handle().IsRegionReadOnly()); + EXPECT_FALSE(memory.Map(kDataSize)); + + ro_handle.Close(); +} + +#endif // OS_ANDROID + } // namespace base |