| // Copyright 2017 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 "base/memory/shared_memory.h" | 
 |  | 
 | #include <limits> | 
 |  | 
 | #include <zircon/process.h> | 
 | #include <zircon/rights.h> | 
 | #include <zircon/syscalls.h> | 
 |  | 
 | #include "base/bits.h" | 
 | #include "base/fuchsia/scoped_zx_handle.h" | 
 | #include "base/logging.h" | 
 | #include "base/memory/shared_memory_tracker.h" | 
 | #include "base/process/process_metrics.h" | 
 |  | 
 | namespace base { | 
 |  | 
 | SharedMemory::SharedMemory() {} | 
 |  | 
 | SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) | 
 |     : shm_(handle), read_only_(read_only) {} | 
 |  | 
 | SharedMemory::~SharedMemory() { | 
 |   Unmap(); | 
 |   Close(); | 
 | } | 
 |  | 
 | // static | 
 | bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { | 
 |   return handle.IsValid(); | 
 | } | 
 |  | 
 | // static | 
 | void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) { | 
 |   DCHECK(handle.IsValid()); | 
 |   handle.Close(); | 
 | } | 
 |  | 
 | // static | 
 | size_t SharedMemory::GetHandleLimit() { | 
 |   // Duplicated from the internal Magenta kernel constant kMaxHandleCount | 
 |   // (kernel/lib/zircon/zircon.cpp). | 
 |   return 256 * 1024u; | 
 | } | 
 |  | 
 | bool SharedMemory::CreateAndMapAnonymous(size_t size) { | 
 |   return CreateAnonymous(size) && Map(size); | 
 | } | 
 |  | 
 | bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { | 
 |   requested_size_ = options.size; | 
 |   mapped_size_ = bits::Align(requested_size_, GetPageSize()); | 
 |   ScopedZxHandle vmo; | 
 |   zx_status_t status = zx_vmo_create(mapped_size_, 0, vmo.receive()); | 
 |   if (status != ZX_OK) { | 
 |     DLOG(ERROR) << "zx_vmo_create failed, status=" << status; | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (!options.executable) { | 
 |     // If options.executable isn't set, drop that permission by replacement. | 
 |     const int kNoExecFlags = ZX_DEFAULT_VMO_RIGHTS & ~ZX_RIGHT_EXECUTE; | 
 |     ScopedZxHandle old_vmo(std::move(vmo)); | 
 |     status = zx_handle_replace(old_vmo.get(), kNoExecFlags, vmo.receive()); | 
 |     if (status != ZX_OK) { | 
 |       DLOG(ERROR) << "zx_handle_replace() failed: " | 
 |                   << zx_status_get_string(status); | 
 |       return false; | 
 |     } | 
 |     ignore_result(old_vmo.release()); | 
 |   } | 
 |  | 
 |   shm_ = SharedMemoryHandle(vmo.release(), mapped_size_, | 
 |                             UnguessableToken::Create()); | 
 |   return true; | 
 | } | 
 |  | 
 | bool SharedMemory::MapAt(off_t offset, size_t bytes) { | 
 |   if (!shm_.IsValid()) | 
 |     return false; | 
 |  | 
 |   if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) | 
 |     return false; | 
 |  | 
 |   if (memory_) | 
 |     return false; | 
 |  | 
 |   int flags = ZX_VM_FLAG_PERM_READ; | 
 |   if (!read_only_) | 
 |     flags |= ZX_VM_FLAG_PERM_WRITE; | 
 |   uintptr_t addr; | 
 |   zx_status_t status = zx_vmar_map(zx_vmar_root_self(), 0, shm_.GetHandle(), | 
 |                                    offset, bytes, flags, &addr); | 
 |   if (status != ZX_OK) { | 
 |     DLOG(ERROR) << "zx_vmar_map failed, status=" << status; | 
 |     return false; | 
 |   } | 
 |   memory_ = reinterpret_cast<void*>(addr); | 
 |  | 
 |   mapped_size_ = bytes; | 
 |   mapped_id_ = shm_.GetGUID(); | 
 |   SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this); | 
 |   return true; | 
 | } | 
 |  | 
 | bool SharedMemory::Unmap() { | 
 |   if (!memory_) | 
 |     return false; | 
 |  | 
 |   SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this); | 
 |  | 
 |   uintptr_t addr = reinterpret_cast<uintptr_t>(memory_); | 
 |   zx_status_t status = zx_vmar_unmap(zx_vmar_root_self(), addr, mapped_size_); | 
 |   if (status != ZX_OK) { | 
 |     DLOG(ERROR) << "zx_vmar_unmap failed, status=" << status; | 
 |     return false; | 
 |   } | 
 |  | 
 |   memory_ = nullptr; | 
 |   mapped_id_ = UnguessableToken(); | 
 |   return true; | 
 | } | 
 |  | 
 | void SharedMemory::Close() { | 
 |   if (shm_.IsValid()) { | 
 |     shm_.Close(); | 
 |     shm_ = SharedMemoryHandle(); | 
 |   } | 
 | } | 
 |  | 
 | SharedMemoryHandle SharedMemory::handle() const { | 
 |   return shm_; | 
 | } | 
 |  | 
 | SharedMemoryHandle SharedMemory::TakeHandle() { | 
 |   SharedMemoryHandle handle(shm_); | 
 |   handle.SetOwnershipPassesToIPC(true); | 
 |   Unmap(); | 
 |   shm_ = SharedMemoryHandle(); | 
 |   return handle; | 
 | } | 
 |  | 
 | SharedMemoryHandle SharedMemory::DuplicateHandle( | 
 |     const SharedMemoryHandle& handle) { | 
 |   return handle.Duplicate(); | 
 | } | 
 |  | 
 | SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const { | 
 |   zx_handle_t duped_handle; | 
 |   const int kNoWriteOrExec = | 
 |       ZX_DEFAULT_VMO_RIGHTS & | 
 |       ~(ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE | ZX_RIGHT_SET_PROPERTY); | 
 |   zx_status_t status = | 
 |       zx_handle_duplicate(shm_.GetHandle(), kNoWriteOrExec, &duped_handle); | 
 |   if (status != ZX_OK) | 
 |     return SharedMemoryHandle(); | 
 |  | 
 |   SharedMemoryHandle handle(duped_handle, shm_.GetSize(), shm_.GetGUID()); | 
 |   handle.SetOwnershipPassesToIPC(true); | 
 |   return handle; | 
 | } | 
 |  | 
 | }  // namespace base |