summaryrefslogtreecommitdiff
path: root/chromium/base/memory/shared_memory_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/base/memory/shared_memory_unittest.cc')
-rw-r--r--chromium/base/memory/shared_memory_unittest.cc114
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