// 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/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(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_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(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_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); shm_ = SharedMemoryHandle(); memory_ = nullptr; mapped_size_ = 0; return handle; } SharedMemoryHandle SharedMemory::DuplicateHandle( const SharedMemoryHandle& handle) { return handle.Duplicate(); } SharedMemoryHandle SharedMemory::GetReadOnlyHandle() { 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