// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "mojo/edk/embedder/platform_shared_buffer.h" #include <stddef.h> #include <utility> #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/shared_memory.h" #include "base/process/process_handle.h" #include "base/sys_info.h" #include "mojo/edk/embedder/platform_handle_utils.h" #if defined(OS_NACL) // For getpagesize() on NaCl. #include <unistd.h> #endif namespace mojo { namespace edk { namespace { // Takes ownership of |memory_handle|. ScopedPlatformHandle SharedMemoryToPlatformHandle( base::SharedMemoryHandle memory_handle) { #if defined(OS_POSIX) && !(defined(OS_MACOSX) && !defined(OS_IOS)) return ScopedPlatformHandle(PlatformHandle(memory_handle.fd)); #elif defined(OS_WIN) return ScopedPlatformHandle(PlatformHandle(memory_handle.GetHandle())); #else return ScopedPlatformHandle(PlatformHandle(memory_handle.GetMemoryObject())); #endif } } // namespace // static PlatformSharedBuffer* PlatformSharedBuffer::Create(size_t num_bytes) { DCHECK_GT(num_bytes, 0u); PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, false); if (!rv->Init()) { // We can't just delete it directly, due to the "in destructor" (debug) // check. scoped_refptr<PlatformSharedBuffer> deleter(rv); return nullptr; } return rv; } // static PlatformSharedBuffer* PlatformSharedBuffer::CreateFromPlatformHandle( size_t num_bytes, bool read_only, ScopedPlatformHandle platform_handle) { DCHECK_GT(num_bytes, 0u); PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, read_only); if (!rv->InitFromPlatformHandle(std::move(platform_handle))) { // We can't just delete it directly, due to the "in destructor" (debug) // check. scoped_refptr<PlatformSharedBuffer> deleter(rv); return nullptr; } return rv; } // static PlatformSharedBuffer* PlatformSharedBuffer::CreateFromPlatformHandlePair( size_t num_bytes, ScopedPlatformHandle rw_platform_handle, ScopedPlatformHandle ro_platform_handle) { DCHECK_GT(num_bytes, 0u); DCHECK(rw_platform_handle.is_valid()); DCHECK(ro_platform_handle.is_valid()); PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, false); if (!rv->InitFromPlatformHandlePair(std::move(rw_platform_handle), std::move(ro_platform_handle))) { // We can't just delete it directly, due to the "in destructor" (debug) // check. scoped_refptr<PlatformSharedBuffer> deleter(rv); return nullptr; } return rv; } // static PlatformSharedBuffer* PlatformSharedBuffer::CreateFromSharedMemoryHandle( size_t num_bytes, bool read_only, base::SharedMemoryHandle handle) { DCHECK_GT(num_bytes, 0u); PlatformSharedBuffer* rv = new PlatformSharedBuffer(num_bytes, read_only); rv->InitFromSharedMemoryHandle(handle); return rv; } size_t PlatformSharedBuffer::GetNumBytes() const { return num_bytes_; } bool PlatformSharedBuffer::IsReadOnly() const { return read_only_; } std::unique_ptr<PlatformSharedBufferMapping> PlatformSharedBuffer::Map( size_t offset, size_t length) { if (!IsValidMap(offset, length)) return nullptr; return MapNoCheck(offset, length); } bool PlatformSharedBuffer::IsValidMap(size_t offset, size_t length) { if (offset > num_bytes_ || length == 0) return false; // Note: This is an overflow-safe check of |offset + length > num_bytes_| // (that |num_bytes >= offset| is verified above). if (length > num_bytes_ - offset) return false; return true; } std::unique_ptr<PlatformSharedBufferMapping> PlatformSharedBuffer::MapNoCheck( size_t offset, size_t length) { DCHECK(IsValidMap(offset, length)); DCHECK(shared_memory_); base::SharedMemoryHandle handle; { base::AutoLock locker(lock_); handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle()); } if (handle == base::SharedMemory::NULLHandle()) return nullptr; std::unique_ptr<PlatformSharedBufferMapping> mapping( new PlatformSharedBufferMapping(handle, read_only_, offset, length)); if (mapping->Map()) return base::WrapUnique(mapping.release()); return nullptr; } ScopedPlatformHandle PlatformSharedBuffer::DuplicatePlatformHandle() { DCHECK(shared_memory_); base::SharedMemoryHandle handle; { base::AutoLock locker(lock_); handle = base::SharedMemory::DuplicateHandle(shared_memory_->handle()); } if (handle == base::SharedMemory::NULLHandle()) return ScopedPlatformHandle(); return SharedMemoryToPlatformHandle(handle); } ScopedPlatformHandle PlatformSharedBuffer::PassPlatformHandle() { DCHECK(HasOneRef()); // The only way to pass a handle from base::SharedMemory is to duplicate it // and close the original. ScopedPlatformHandle handle = DuplicatePlatformHandle(); base::AutoLock locker(lock_); shared_memory_->Close(); return handle; } base::SharedMemoryHandle PlatformSharedBuffer::DuplicateSharedMemoryHandle() { DCHECK(shared_memory_); base::AutoLock locker(lock_); return base::SharedMemory::DuplicateHandle(shared_memory_->handle()); } PlatformSharedBuffer* PlatformSharedBuffer::CreateReadOnlyDuplicate() { DCHECK(shared_memory_); if (ro_shared_memory_) { base::AutoLock locker(lock_); base::SharedMemoryHandle handle; handle = base::SharedMemory::DuplicateHandle(ro_shared_memory_->handle()); if (handle == base::SharedMemory::NULLHandle()) return nullptr; return CreateFromSharedMemoryHandle(num_bytes_, true, handle); } base::SharedMemoryHandle handle; bool success; { base::AutoLock locker(lock_); success = shared_memory_->ShareReadOnlyToProcess( base::GetCurrentProcessHandle(), &handle); } if (!success || handle == base::SharedMemory::NULLHandle()) return nullptr; return CreateFromSharedMemoryHandle(num_bytes_, true, handle); } PlatformSharedBuffer::PlatformSharedBuffer(size_t num_bytes, bool read_only) : num_bytes_(num_bytes), read_only_(read_only) {} PlatformSharedBuffer::~PlatformSharedBuffer() {} bool PlatformSharedBuffer::Init() { DCHECK(!shared_memory_); DCHECK(!read_only_); base::SharedMemoryCreateOptions options; options.size = num_bytes_; // By default, we can share as read-only. options.share_read_only = true; shared_memory_.reset(new base::SharedMemory); return shared_memory_->Create(options); } bool PlatformSharedBuffer::InitFromPlatformHandle( ScopedPlatformHandle platform_handle) { DCHECK(!shared_memory_); #if defined(OS_WIN) base::SharedMemoryHandle handle(platform_handle.release().handle, base::GetCurrentProcId()); #elif defined(OS_MACOSX) && !defined(OS_IOS) base::SharedMemoryHandle handle; handle = base::SharedMemoryHandle(platform_handle.release().port, num_bytes_, base::GetCurrentProcId()); #else base::SharedMemoryHandle handle(platform_handle.release().handle, false); #endif shared_memory_.reset(new base::SharedMemory(handle, read_only_)); return true; } bool PlatformSharedBuffer::InitFromPlatformHandlePair( ScopedPlatformHandle rw_platform_handle, ScopedPlatformHandle ro_platform_handle) { #if defined(OS_MACOSX) NOTREACHED(); return false; #else // defined(OS_MACOSX) #if defined(OS_WIN) base::SharedMemoryHandle handle(rw_platform_handle.release().handle, base::GetCurrentProcId()); base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle, base::GetCurrentProcId()); #else // defined(OS_WIN) base::SharedMemoryHandle handle(rw_platform_handle.release().handle, false); base::SharedMemoryHandle ro_handle(ro_platform_handle.release().handle, false); #endif // defined(OS_WIN) DCHECK(!shared_memory_); shared_memory_.reset(new base::SharedMemory(handle, false)); ro_shared_memory_.reset(new base::SharedMemory(ro_handle, true)); return true; #endif // defined(OS_MACOSX) } void PlatformSharedBuffer::InitFromSharedMemoryHandle( base::SharedMemoryHandle handle) { DCHECK(!shared_memory_); shared_memory_.reset(new base::SharedMemory(handle, read_only_)); } PlatformSharedBufferMapping::~PlatformSharedBufferMapping() { Unmap(); } void* PlatformSharedBufferMapping::GetBase() const { return base_; } size_t PlatformSharedBufferMapping::GetLength() const { return length_; } bool PlatformSharedBufferMapping::Map() { // Mojo shared buffers can be mapped at any offset. However, // base::SharedMemory must be mapped at a page boundary. So calculate what the // nearest whole page offset is, and build a mapping that's offset from that. #if defined(OS_NACL) // base::SysInfo isn't available under NaCl. size_t page_size = getpagesize(); #else size_t page_size = base::SysInfo::VMAllocationGranularity(); #endif size_t offset_rounding = offset_ % page_size; size_t real_offset = offset_ - offset_rounding; size_t real_length = length_ + offset_rounding; if (!shared_memory_.MapAt(static_cast<off_t>(real_offset), real_length)) return false; base_ = static_cast<char*>(shared_memory_.memory()) + offset_rounding; return true; } void PlatformSharedBufferMapping::Unmap() { shared_memory_.Unmap(); } } // namespace edk } // namespace mojo