Chromium Code Reviews| 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..c5b4310e8269cbcadd14716670f1f124c3860b81 100644 |
| --- a/base/memory/shared_memory_unittest.cc |
| +++ b/base/memory/shared_memory_unittest.cc |
| @@ -8,6 +8,7 @@ |
| #include "base/process/kill.h" |
| #include "base/rand_util.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 +21,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 +364,91 @@ 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 OS_POSIX |
|
jln (very slow on Chromium)
2013/10/16 00:16:39
Style:
#if defined()
Jeffrey Yasskin
2013/10/16 01:16:39
Done.
|
| + |
| + 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); |
| + |
| + // 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. |
| + fchmod(handle.fd, S_IRUSR | S_IWUSR); |
|
jln (very slow on Chromium)
2013/10/16 00:16:39
Please, check the return value, it's better to kee
Jeffrey Yasskin
2013/10/16 01:16:39
Done.
|
| + |
| + errno = 0; |
| + writable_fd = open(shmem_path.c_str(), O_WRONLY); |
| + open_writable_errno = errno; |
| + // Mac appears to get this right by treating open(/dev/fd/N) as dup(N). |
|
jln (very slow on Chromium)
2013/10/16 00:16:39
This is not correct, open(/dev/fd/N) is very diffe
Jeffrey Yasskin
2013/10/16 01:16:39
https://developer.apple.com/library/mac/documentat
jln (very slow on Chromium)
2013/10/16 17:51:15
Ohh yeah, F_DUPFD is confusing. It really has noth
|
| +#if !OS_LINUX |
|
jln (very slow on Chromium)
2013/10/16 00:16:39
Style: #if !defined()
Jeffrey Yasskin
2013/10/16 01:16:39
Done.
|
| + EXPECT_EQ(-1, writable_fd); |
| + EXPECT_EQ(EACCES, open_writable_errno) << strerror(open_writable_errno); |
| +#endif |
| + close(writable_fd); |
| + |
| +#elif OS_WIN |
|
Jeffrey Yasskin
2013/10/15 23:03:52
Please try to think of other attacks I should be t
|
| + 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(), |
| + 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(); |