diff options
Diffstat (limited to 'base/memory/shared_memory_posix.cc')
-rw-r--r-- | base/memory/shared_memory_posix.cc | 110 |
1 files changed, 80 insertions, 30 deletions
diff --git a/base/memory/shared_memory_posix.cc b/base/memory/shared_memory_posix.cc index efb0caf..bb3c280 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; |