Index: base/memory/shared_memory_unittest.cc |
diff --git a/base/memory/shared_memory_unittest.cc b/base/memory/shared_memory_unittest.cc |
index 892fd7f1a590b0d20b8f9e6289b07a225c8676fa..d923b26a0ba445e90f7dd52e8c344582e1ea054d 100644 |
--- a/base/memory/shared_memory_unittest.cc |
+++ b/base/memory/shared_memory_unittest.cc |
@@ -7,7 +7,9 @@ |
#include "base/memory/shared_memory.h" |
#include "base/process/kill.h" |
#include "base/rand_util.h" |
+#include "base/safe_numerics.h" |
#include "base/strings/string_number_conversions.h" |
+#include "base/strings/stringprintf.h" |
#include "base/sys_info.h" |
#include "base/test/multiprocess_test.h" |
#include "base/threading/platform_thread.h" |
@@ -20,6 +22,8 @@ |
#endif |
#if defined(OS_POSIX) |
+#include <errno.h> |
+#include <fcntl.h> |
#include <sys/mman.h> |
#include <sys/stat.h> |
#include <sys/types.h> |
@@ -361,6 +365,107 @@ TEST(SharedMemoryTest, AnonymousPrivate) { |
} |
} |
+TEST(SharedMemoryTest, AnonymousReadOnly) { |
+ StringPiece contents = "Hello World"; |
+ scoped_ptr<SharedMemory> shmem( |
+ SharedMemory::NewAnonymousReadOnly("Hello World")); |
+ |
+ ASSERT_TRUE(shmem->Map(contents.size())); |
+ EXPECT_EQ( |
+ contents, |
+ StringPiece(static_cast<const char*>(shmem->memory()), contents.size())); |
+ |
+ // 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 |
+ // would be a security hole.) Setting up multiple processes is hard in a |
+ // unittest, so this test checks that the *current* process can't reopen the |
+ // segment read/write. I think the test here is stronger than we actually |
+ // care about, but there's a remote possibility that sending a file over a |
+ // pipe would transform it into read/write. |
+ SharedMemoryHandle handle = shmem->handle(); |
+#if defined(OS_POSIX) |
+ |
+ EXPECT_EQ(O_RDONLY, fcntl(handle.fd, F_GETFL) & O_ACCMODE) |
+ << "The descriptor itself should be read-only."; |
+ |
+ errno = 0; |
+ void* writable = mmap(NULL, |
+ shmem->mapped_size(), |
+ PROT_READ | PROT_WRITE, |
+ MAP_SHARED, |
+ handle.fd, |
+ 0); |
+ int mmap_errno = errno; |
+ EXPECT_EQ(MAP_FAILED, writable) |
+ << "It shouldn't be possible to re-mmap the descriptor writable."; |
+ EXPECT_EQ(EACCES, mmap_errno); |
+ |
+ struct stat fd_stat; |
+ errno = 0; |
+ EXPECT_EQ(0, fstat(handle.fd, &fd_stat)) << strerror(errno); |
+ EXPECT_EQ(0400, checked_numeric_cast<int>(fd_stat.st_mode & 0777)) |
+ << "inode should be read-only"; |
+ EXPECT_EQ(0U, fd_stat.st_nlink) << "inode should be unlinked"; |
+ EXPECT_EQ(geteuid(), fd_stat.st_uid) |
+ << "inode should be owned by current user"; |
+ |
+ if (0 == access("/dev/fd", X_OK)) { |
+ // Try to re-open through /dev/fd. This is an end-run around the notion of |
+ // an FD as a capability. |
+ const std::string shmem_path = StringPrintf("/dev/fd/%d", handle.fd); |
+ errno = 0; |
+ int readable_fd = open(shmem_path.c_str(), O_RDONLY); |
+ EXPECT_NE(-1, readable_fd) << strerror(errno); |
+ close(readable_fd); |
+ |
+ errno = 0; |
+ int writable_fd = open(shmem_path.c_str(), O_WRONLY); |
+ int open_writable_errno = errno; |
+ EXPECT_EQ(-1, writable_fd); |
+ EXPECT_EQ(EACCES, open_writable_errno) << strerror(open_writable_errno); |
+ close(writable_fd); |
+ |
+ // However, if we explicitly make the entry in /dev/fd writable first, the |
+ // open() call successfully creates a writable file on Linux. The sandbox |
+ // has to prevent opening this path. TODO(jln): Write a test that attacks |
+ // this from inside the sandbox. |
+ errno = 0; |
+ EXPECT_EQ(0, fchmod(handle.fd, S_IRUSR | S_IWUSR)) << strerror(errno); |
+ |
+ errno = 0; |
+ writable_fd = open(shmem_path.c_str(), O_WRONLY); |
+ open_writable_errno = errno; |
+ // On Linux, opening the file /dev/fd/N where 'N' is a read-only file |
+ // descriptor, can produce a writable file descriptor if the inode is |
+ // writable (see the fchmod above). Mac appears to restrict the open() call |
+ // appropriately. Other systems might let the open() succeed but still |
+ // produce a read-only descriptor. |
+#if !defined(OS_LINUX) |
+ EXPECT_EQ(-1, writable_fd); |
+ EXPECT_EQ(EACCES, open_writable_errno) << strerror(open_writable_errno); |
+#endif |
+ close(writable_fd); |
+ } |
+ |
+#elif defined(OS_WIN) |
+ EXPECT_EQ(NULL, MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, 0)) |
+ << "Shouldn't be able to map memory writable."; |
+ |
+ SharedMemoryHandle writable_handle = INVALID_HANDLE_VALUE; |
+ EXPECT_EQ(0, |
+ ::DuplicateHandle(GetCurrentProcess(), |
Will Harris
2013/10/16 17:01:03
should probably CloseHandle after this succeeds, o
Jeffrey Yasskin
2013/10/16 22:27:53
Thanks, done with ScopedHandle.
|
+ handle, |
+ GetCurrentProcess, |
+ &writable_handle, |
+ FILE_MAP_ALL_ACCESS, |
+ false, |
+ 0)) |
+ << "Shouldn't be able to duplicate the handle into a writable one."; |
+#else |
+#error Unexpected platform; write a test that tries to make 'handle' writable. |
+#endif |
+} |
+ |
TEST(SharedMemoryTest, MapAt) { |
ASSERT_TRUE(SysInfo::VMAllocationGranularity() >= sizeof(uint32)); |
const size_t kCount = SysInfo::VMAllocationGranularity(); |