Path: blob/main/contrib/llvm-project/compiler-rt/lib/scudo/standalone/mem_map_fuchsia.cpp
35292 views
//===-- mem_map_fuchsia.cpp -------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "mem_map_fuchsia.h"910#include "atomic_helpers.h"11#include "common.h"12#include "string_utils.h"1314#if SCUDO_FUCHSIA1516#include <zircon/process.h>17#include <zircon/status.h>18#include <zircon/syscalls.h>1920namespace scudo {2122static void NORETURN dieOnError(zx_status_t Status, const char *FnName,23uptr Size) {24ScopedString Error;25Error.append("SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,26Size >> 10, _zx_status_get_string(Status));27outputRaw(Error.data());28die();29}3031static void setVmoName(zx_handle_t Vmo, const char *Name) {32size_t Len = strlen(Name);33DCHECK_LT(Len, ZX_MAX_NAME_LEN);34zx_status_t Status = _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, Len);35CHECK_EQ(Status, ZX_OK);36}3738// Returns the (cached) base address of the root VMAR.39static uptr getRootVmarBase() {40static atomic_uptr CachedResult = {0};4142uptr Result = atomic_load(&CachedResult, memory_order_acquire);43if (UNLIKELY(!Result)) {44zx_info_vmar_t VmarInfo;45zx_status_t Status =46_zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &VmarInfo,47sizeof(VmarInfo), nullptr, nullptr);48CHECK_EQ(Status, ZX_OK);49CHECK_NE(VmarInfo.base, 0);5051atomic_store(&CachedResult, VmarInfo.base, memory_order_release);52Result = VmarInfo.base;53}5455return Result;56}5758// Lazily creates and then always returns the same zero-sized VMO.59static zx_handle_t getPlaceholderVmo() {60static atomic_u32 StoredVmo = {ZX_HANDLE_INVALID};6162zx_handle_t Vmo = atomic_load(&StoredVmo, memory_order_acquire);63if (UNLIKELY(Vmo == ZX_HANDLE_INVALID)) {64// Create a zero-sized placeholder VMO.65zx_status_t Status = _zx_vmo_create(0, 0, &Vmo);66if (UNLIKELY(Status != ZX_OK))67dieOnError(Status, "zx_vmo_create", 0);6869setVmoName(Vmo, "scudo:reserved");7071// Atomically store its handle. If some other thread wins the race, use its72// handle and discard ours.73zx_handle_t OldValue = atomic_compare_exchange_strong(74&StoredVmo, ZX_HANDLE_INVALID, Vmo, memory_order_acq_rel);75if (UNLIKELY(OldValue != ZX_HANDLE_INVALID)) {76Status = _zx_handle_close(Vmo);77CHECK_EQ(Status, ZX_OK);7879Vmo = OldValue;80}81}8283return Vmo;84}8586// Checks if MAP_ALLOWNOMEM allows the given error code.87static bool IsNoMemError(zx_status_t Status) {88// Note: _zx_vmar_map returns ZX_ERR_NO_RESOURCES if the VMAR does not contain89// a suitable free spot.90return Status == ZX_ERR_NO_MEMORY || Status == ZX_ERR_NO_RESOURCES;91}9293// Note: this constructor is only called by ReservedMemoryFuchsia::dispatch.94MemMapFuchsia::MemMapFuchsia(uptr Base, uptr Capacity)95: MapAddr(Base), WindowBase(Base), WindowSize(Capacity) {96// Create the VMO.97zx_status_t Status = _zx_vmo_create(Capacity, 0, &Vmo);98if (UNLIKELY(Status != ZX_OK))99dieOnError(Status, "zx_vmo_create", Capacity);100101setVmoName(Vmo, "scudo:dispatched");102}103104bool MemMapFuchsia::mapImpl(UNUSED uptr Addr, uptr Size, const char *Name,105uptr Flags) {106const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);107const bool PreCommit = !!(Flags & MAP_PRECOMMIT);108const bool NoAccess = !!(Flags & MAP_NOACCESS);109110// Create the VMO.111zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);112if (UNLIKELY(Status != ZX_OK)) {113if (AllowNoMem && IsNoMemError(Status))114return false;115dieOnError(Status, "zx_vmo_create", Size);116}117118if (Name != nullptr)119setVmoName(Vmo, Name);120121// Map it.122zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS;123if (!NoAccess)124MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;125Status =126_zx_vmar_map(_zx_vmar_root_self(), MapFlags, 0, Vmo, 0, Size, &MapAddr);127if (UNLIKELY(Status != ZX_OK)) {128if (AllowNoMem && IsNoMemError(Status)) {129Status = _zx_handle_close(Vmo);130CHECK_EQ(Status, ZX_OK);131132MapAddr = 0;133Vmo = ZX_HANDLE_INVALID;134return false;135}136dieOnError(Status, "zx_vmar_map", Size);137}138139if (PreCommit) {140Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,141Size, nullptr, 0);142CHECK_EQ(Status, ZX_OK);143}144145WindowBase = MapAddr;146WindowSize = Size;147return true;148}149150void MemMapFuchsia::unmapImpl(uptr Addr, uptr Size) {151zx_status_t Status;152153if (Size == WindowSize) {154// NOTE: Closing first and then unmapping seems slightly faster than doing155// the same operations in the opposite order.156Status = _zx_handle_close(Vmo);157CHECK_EQ(Status, ZX_OK);158Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);159CHECK_EQ(Status, ZX_OK);160161MapAddr = WindowBase = WindowSize = 0;162Vmo = ZX_HANDLE_INVALID;163} else {164// Unmap the subrange.165Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);166CHECK_EQ(Status, ZX_OK);167168// Decommit the pages that we just unmapped.169Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, Addr - MapAddr, Size,170nullptr, 0);171CHECK_EQ(Status, ZX_OK);172173if (Addr == WindowBase)174WindowBase += Size;175WindowSize -= Size;176}177}178179bool MemMapFuchsia::remapImpl(uptr Addr, uptr Size, const char *Name,180uptr Flags) {181const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);182const bool PreCommit = !!(Flags & MAP_PRECOMMIT);183const bool NoAccess = !!(Flags & MAP_NOACCESS);184185// NOTE: This will rename the *whole* VMO, not only the requested portion of186// it. But we cannot do better than this given the MemMap API. In practice,187// the upper layers of Scudo always pass the same Name for a given MemMap.188if (Name != nullptr)189setVmoName(Vmo, Name);190191uptr MappedAddr;192zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC_OVERWRITE;193if (!NoAccess)194MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;195zx_status_t Status =196_zx_vmar_map(_zx_vmar_root_self(), MapFlags, Addr - getRootVmarBase(),197Vmo, Addr - MapAddr, Size, &MappedAddr);198if (UNLIKELY(Status != ZX_OK)) {199if (AllowNoMem && IsNoMemError(Status))200return false;201dieOnError(Status, "zx_vmar_map", Size);202}203DCHECK_EQ(Addr, MappedAddr);204205if (PreCommit) {206Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,207Size, nullptr, 0);208CHECK_EQ(Status, ZX_OK);209}210211return true;212}213214void MemMapFuchsia::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) {215zx_status_t Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, From - MapAddr,216Size, nullptr, 0);217CHECK_EQ(Status, ZX_OK);218}219220void MemMapFuchsia::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) {221const bool NoAccess = !!(Flags & MAP_NOACCESS);222223zx_vm_option_t MapFlags = 0;224if (!NoAccess)225MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;226zx_status_t Status =227_zx_vmar_protect(_zx_vmar_root_self(), MapFlags, Addr, Size);228CHECK_EQ(Status, ZX_OK);229}230231bool ReservedMemoryFuchsia::createImpl(UNUSED uptr Addr, uptr Size,232UNUSED const char *Name, uptr Flags) {233const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);234235// Reserve memory by mapping the placeholder VMO without any permission.236zx_status_t Status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_ALLOW_FAULTS, 0,237getPlaceholderVmo(), 0, Size, &Base);238if (UNLIKELY(Status != ZX_OK)) {239if (AllowNoMem && IsNoMemError(Status))240return false;241dieOnError(Status, "zx_vmar_map", Size);242}243244Capacity = Size;245return true;246}247248void ReservedMemoryFuchsia::releaseImpl() {249zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(), Base, Capacity);250CHECK_EQ(Status, ZX_OK);251}252253ReservedMemoryFuchsia::MemMapT ReservedMemoryFuchsia::dispatchImpl(uptr Addr,254uptr Size) {255return ReservedMemoryFuchsia::MemMapT(Addr, Size);256}257258} // namespace scudo259260#endif // SCUDO_FUCHSIA261262263