Index: base/memory/shared_memory_posix.cc |
diff --git a/base/memory/shared_memory_posix.cc b/base/memory/shared_memory_posix.cc |
index efb0caf5bc9a29d805700c3abdad18ad6671a199..bb3c2801fdeed6b5de40e137d5d2b9af4ea43fe4 100644 |
--- a/base/memory/shared_memory_posix.cc |
+++ b/base/memory/shared_memory_posix.cc |
@@ -30,6 +30,9 @@ |
#include "third_party/ashmem/ashmem.h" |
#endif |
+using file_util::ScopedFD; |
+using file_util::ScopedFILE; |
+ |
namespace base { |
namespace { |
@@ -40,6 +43,7 @@ LazyInstance<Lock>::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER; |
SharedMemory::SharedMemory() |
: mapped_file_(-1), |
+ readonly_mapped_file_(-1), |
inode_(0), |
mapped_size_(0), |
memory_(NULL), |
@@ -49,6 +53,7 @@ SharedMemory::SharedMemory() |
SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) |
: mapped_file_(handle.fd), |
+ readonly_mapped_file_(-1), |
inode_(0), |
mapped_size_(0), |
memory_(NULL), |
@@ -65,6 +70,7 @@ SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) |
SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, |
ProcessHandle process) |
: mapped_file_(handle.fd), |
+ readonly_mapped_file_(-1), |
inode_(0), |
mapped_size_(0), |
memory_(NULL), |
@@ -92,7 +98,7 @@ SharedMemoryHandle SharedMemory::NULLHandle() { |
// static |
void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { |
DCHECK_GE(handle.fd, 0); |
- if (HANDLE_EINTR(close(handle.fd)) < 0) |
+ if (close(handle.fd) < 0) |
DPLOG(ERROR) << "close"; |
} |
@@ -124,8 +130,10 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { |
// and be deleted before they ever make it out to disk. |
base::ThreadRestrictions::ScopedAllowIO allow_io; |
- FILE *fp; |
+ ScopedFILE fp; |
bool fix_size = true; |
+ int readonly_fd_storage = -1; |
+ ScopedFD readonly_fd(&readonly_fd_storage); |
FilePath path; |
if (options.name == NULL || options.name->empty()) { |
@@ -133,12 +141,19 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { |
DCHECK(!options.open_existing); |
// Q: Why not use the shm_open() etc. APIs? |
// A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU |
- fp = file_util::CreateAndOpenTemporaryShmemFile(&path, options.executable); |
+ fp.reset( |
+ file_util::CreateAndOpenTemporaryShmemFile(&path, options.executable)); |
- // Deleting the file prevents anyone else from mapping it in (making it |
- // private), and prevents the need for cleanup (once the last fd is closed, |
- // it is truly freed). |
if (fp) { |
+ // Also open as readonly so that we can ShareReadOnlyToProcess. |
+ *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); |
+ if (*readonly_fd < 0) { |
+ DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; |
+ fp.reset(); |
+ } |
+ // Deleting the file prevents anyone else from mapping it in (making it |
+ // private), and prevents the need for cleanup (once the last fd is |
+ // closed, it is truly freed). |
if (unlink(path.value().c_str())) |
PLOG(WARNING) << "unlink"; |
} |
@@ -175,32 +190,35 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { |
sb.st_uid != effective_uid)) { |
LOG(ERROR) << |
"Invalid owner when opening existing shared memory file."; |
- HANDLE_EINTR(close(fd)); |
+ close(fd); |
return false; |
} |
// An existing file was opened, so its size should not be fixed. |
fix_size = false; |
} |
- fp = NULL; |
+ |
+ // Also open as readonly so that we can ShareReadOnlyToProcess. |
+ *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); |
+ if (*readonly_fd < 0) { |
+ DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; |
+ close(fd); |
+ fd = -1; |
+ } |
if (fd >= 0) { |
// "a+" is always appropriate: if it's a new file, a+ is similar to w+. |
- fp = fdopen(fd, "a+"); |
+ fp.reset(fdopen(fd, "a+")); |
} |
} |
if (fp && fix_size) { |
// Get current size. |
struct stat stat; |
- if (fstat(fileno(fp), &stat) != 0) { |
- file_util::CloseFile(fp); |
+ if (fstat(fileno(fp.get()), &stat) != 0) |
return false; |
- } |
const size_t current_size = stat.st_size; |
if (current_size != options.size) { |
- if (HANDLE_EINTR(ftruncate(fileno(fp), options.size)) != 0) { |
- file_util::CloseFile(fp); |
+ if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0) |
return false; |
- } |
} |
requested_size_ = options.size; |
} |
@@ -221,7 +239,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { |
return false; |
} |
- return PrepareMapFile(fp); |
+ return PrepareMapFile(fp.Pass(), readonly_fd.Pass()); |
} |
// Our current implementation of shmem is with mmap()ing of files. |
@@ -247,8 +265,14 @@ bool SharedMemory::Open(const std::string& name, bool read_only) { |
read_only_ = read_only; |
const char *mode = read_only ? "r" : "r+"; |
- FILE *fp = file_util::OpenFile(path, mode); |
- return PrepareMapFile(fp); |
+ ScopedFILE fp(file_util::OpenFile(path, mode)); |
+ int readonly_fd_storage = -1; |
+ ScopedFD readonly_fd(&readonly_fd_storage); |
+ *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)); |
+ if (*readonly_fd < 0) { |
+ DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; |
+ } |
+ return PrepareMapFile(fp.Pass(), readonly_fd.Pass()); |
} |
#endif // !defined(OS_ANDROID) |
@@ -305,10 +329,15 @@ void SharedMemory::Close() { |
Unmap(); |
if (mapped_file_ > 0) { |
- if (HANDLE_EINTR(close(mapped_file_)) < 0) |
+ if (close(mapped_file_) < 0) |
PLOG(ERROR) << "close"; |
mapped_file_ = -1; |
} |
+ if (readonly_mapped_file_ > 0) { |
+ if (close(readonly_mapped_file_) < 0) |
+ PLOG(ERROR) << "close"; |
+ readonly_mapped_file_ = -1; |
+ } |
} |
void SharedMemory::Lock() { |
@@ -322,18 +351,28 @@ void SharedMemory::Unlock() { |
} |
#if !defined(OS_ANDROID) |
-bool SharedMemory::PrepareMapFile(FILE *fp) { |
+bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) { |
DCHECK_EQ(-1, mapped_file_); |
- if (fp == NULL) return false; |
+ DCHECK_EQ(-1, readonly_mapped_file_); |
+ if (fp == NULL || *readonly_fd < 0) return false; |
// This function theoretically can block on the disk, but realistically |
// the temporary files we create will just go into the buffer cache |
// and be deleted before they ever make it out to disk. |
base::ThreadRestrictions::ScopedAllowIO allow_io; |
- file_util::ScopedFILE file_closer(fp); |
+ struct stat st = {}; |
+ struct stat readonly_st = {}; |
+ if (fstat(fileno(fp.get()), &st)) |
+ NOTREACHED(); |
+ if (fstat(*readonly_fd, &readonly_st)) |
+ NOTREACHED(); |
+ if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { |
+ LOG(ERROR) << "writable and read-only inodes don't match; bailing"; |
+ return false; |
+ } |
- mapped_file_ = dup(fileno(fp)); |
+ mapped_file_ = dup(fileno(fp.get())); |
if (mapped_file_ == -1) { |
if (errno == EMFILE) { |
LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; |
@@ -342,11 +381,8 @@ bool SharedMemory::PrepareMapFile(FILE *fp) { |
NOTREACHED() << "Call to dup failed, errno=" << errno; |
} |
} |
- |
- struct stat st; |
- if (fstat(mapped_file_, &st)) |
- NOTREACHED(); |
inode_ = st.st_ino; |
+ readonly_mapped_file_ = *readonly_fd.release(); |
return true; |
} |
@@ -399,9 +435,23 @@ void SharedMemory::LockOrUnlockCommon(int function) { |
} |
bool SharedMemory::ShareToProcessCommon(ProcessHandle process, |
- SharedMemoryHandle *new_handle, |
- bool close_self) { |
- const int new_fd = dup(mapped_file_); |
+ SharedMemoryHandle* new_handle, |
+ bool close_self, |
+ ShareMode share_mode) { |
+ int handle_to_dup = -1; |
+ switch(share_mode) { |
+ case SHARE_CURRENT_MODE: |
+ handle_to_dup = mapped_file_; |
+ break; |
+ case SHARE_READONLY: |
+ // We could imagine re-opening the file from /dev/fd, but that can't make |
+ // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10 |
+ CHECK(readonly_mapped_file_ >= 0); |
+ handle_to_dup = readonly_mapped_file_; |
+ break; |
+ } |
+ |
+ const int new_fd = dup(handle_to_dup); |
if (new_fd < 0) { |
DPLOG(ERROR) << "dup() failed."; |
return false; |