// 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 #include #include #include #include "base/bits.h" #include "base/fuchsia/fuchsia_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()); zx::vmo vmo; zx_status_t status = zx::vmo::create(mapped_size_, 0, &vmo); if (status != ZX_OK) { ZX_DLOG(ERROR, status) << "zx_vmo_create"; 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; status = vmo.replace(kNoExecFlags, &vmo); if (status != ZX_OK) { ZX_DLOG(ERROR, status) << "zx_handle_replace"; return false; } } 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(std::numeric_limits::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::root_self()->map( 0, *zx::unowned_vmo(shm_.GetHandle()), offset, bytes, flags, &addr); if (status != ZX_OK) { ZX_DLOG(ERROR, status) << "zx_vmar_map"; return false; } memory_ = reinterpret_cast(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(memory_); zx_status_t status = zx::vmar::root_self()->unmap(addr, mapped_size_); if (status != ZX_OK) { ZX_DLOG(ERROR, status) << "zx_vmar_unmap"; 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::vmo duped_handle; const int kNoWriteOrExec = ZX_DEFAULT_VMO_RIGHTS & ~(ZX_RIGHT_WRITE | ZX_RIGHT_EXECUTE | ZX_RIGHT_SET_PROPERTY); zx_status_t status = zx::unowned_vmo(shm_.GetHandle()) ->duplicate(kNoWriteOrExec, &duped_handle); if (status != ZX_OK) return SharedMemoryHandle(); SharedMemoryHandle handle(duped_handle.release(), shm_.GetSize(), shm_.GetGUID()); handle.SetOwnershipPassesToIPC(true); return handle; } } // namespace base