brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.2 KiB · 9d6df2b Raw
262 lines · cpp
1//===-- mem_map_fuchsia.cpp -------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "mem_map_fuchsia.h"10 11#include "atomic_helpers.h"12#include "common.h"13#include "string_utils.h"14 15#if SCUDO_FUCHSIA16 17#include <zircon/process.h>18#include <zircon/status.h>19#include <zircon/syscalls.h>20 21namespace scudo {22 23static void NORETURN dieOnError(zx_status_t Status, const char *FnName,24                                uptr Size) {25  ScopedString Error;26  Error.append("SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,27               Size >> 10, _zx_status_get_string(Status));28  outputRaw(Error.data());29  die();30}31 32static void setVmoName(zx_handle_t Vmo, const char *Name) {33  size_t Len = strlen(Name);34  DCHECK_LT(Len, ZX_MAX_NAME_LEN);35  zx_status_t Status = _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, Len);36  CHECK_EQ(Status, ZX_OK);37}38 39// Returns the (cached) base address of the root VMAR.40static uptr getRootVmarBase() {41  static atomic_uptr CachedResult = {0};42 43  uptr Result = atomic_load(&CachedResult, memory_order_acquire);44  if (UNLIKELY(!Result)) {45    zx_info_vmar_t VmarInfo;46    zx_status_t Status =47        _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &VmarInfo,48                            sizeof(VmarInfo), nullptr, nullptr);49    CHECK_EQ(Status, ZX_OK);50    CHECK_NE(VmarInfo.base, 0);51 52    atomic_store(&CachedResult, VmarInfo.base, memory_order_release);53    Result = VmarInfo.base;54  }55 56  return Result;57}58 59// Lazily creates and then always returns the same zero-sized VMO.60static zx_handle_t getPlaceholderVmo() {61  static atomic_u32 StoredVmo = {ZX_HANDLE_INVALID};62 63  zx_handle_t Vmo = atomic_load(&StoredVmo, memory_order_acquire);64  if (UNLIKELY(Vmo == ZX_HANDLE_INVALID)) {65    // Create a zero-sized placeholder VMO.66    zx_status_t Status = _zx_vmo_create(0, 0, &Vmo);67    if (UNLIKELY(Status != ZX_OK))68      dieOnError(Status, "zx_vmo_create", 0);69 70    setVmoName(Vmo, "scudo:reserved");71 72    // Atomically store its handle. If some other thread wins the race, use its73    // handle and discard ours.74    zx_handle_t OldValue = atomic_compare_exchange_strong(75        &StoredVmo, ZX_HANDLE_INVALID, Vmo, memory_order_acq_rel);76    if (UNLIKELY(OldValue != ZX_HANDLE_INVALID)) {77      Status = _zx_handle_close(Vmo);78      CHECK_EQ(Status, ZX_OK);79 80      Vmo = OldValue;81    }82  }83 84  return Vmo;85}86 87// Checks if MAP_ALLOWNOMEM allows the given error code.88static bool IsNoMemError(zx_status_t Status) {89  // Note: _zx_vmar_map returns ZX_ERR_NO_RESOURCES if the VMAR does not contain90  // a suitable free spot.91  return Status == ZX_ERR_NO_MEMORY || Status == ZX_ERR_NO_RESOURCES;92}93 94// Note: this constructor is only called by ReservedMemoryFuchsia::dispatch.95MemMapFuchsia::MemMapFuchsia(uptr Base, uptr Capacity)96    : MapAddr(Base), WindowBase(Base), WindowSize(Capacity) {97  // Create the VMO.98  zx_status_t Status = _zx_vmo_create(Capacity, 0, &Vmo);99  if (UNLIKELY(Status != ZX_OK))100    dieOnError(Status, "zx_vmo_create", Capacity);101 102  setVmoName(Vmo, "scudo:dispatched");103}104 105bool MemMapFuchsia::mapImpl(UNUSED uptr Addr, uptr Size, const char *Name,106                            uptr Flags) {107  const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);108  const bool PreCommit = !!(Flags & MAP_PRECOMMIT);109  const bool NoAccess = !!(Flags & MAP_NOACCESS);110 111  // Create the VMO.112  zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);113  if (UNLIKELY(Status != ZX_OK)) {114    if (AllowNoMem && IsNoMemError(Status))115      return false;116    dieOnError(Status, "zx_vmo_create", Size);117  }118 119  if (Name != nullptr)120    setVmoName(Vmo, Name);121 122  // Map it.123  zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS;124  if (!NoAccess)125    MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;126  Status =127      _zx_vmar_map(_zx_vmar_root_self(), MapFlags, 0, Vmo, 0, Size, &MapAddr);128  if (UNLIKELY(Status != ZX_OK)) {129    if (AllowNoMem && IsNoMemError(Status)) {130      Status = _zx_handle_close(Vmo);131      CHECK_EQ(Status, ZX_OK);132 133      MapAddr = 0;134      Vmo = ZX_HANDLE_INVALID;135      return false;136    }137    dieOnError(Status, "zx_vmar_map", Size);138  }139 140  if (PreCommit) {141    Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,142                               Size, nullptr, 0);143    CHECK_EQ(Status, ZX_OK);144  }145 146  WindowBase = MapAddr;147  WindowSize = Size;148  return true;149}150 151void MemMapFuchsia::unmapImpl(uptr Addr, uptr Size) {152  zx_status_t Status;153 154  if (Size == WindowSize) {155    // NOTE: Closing first and then unmapping seems slightly faster than doing156    // the same operations in the opposite order.157    Status = _zx_handle_close(Vmo);158    CHECK_EQ(Status, ZX_OK);159    Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);160    CHECK_EQ(Status, ZX_OK);161 162    MapAddr = WindowBase = WindowSize = 0;163    Vmo = ZX_HANDLE_INVALID;164  } else {165    // Unmap the subrange.166    Status = _zx_vmar_unmap(_zx_vmar_root_self(), Addr, Size);167    CHECK_EQ(Status, ZX_OK);168 169    // Decommit the pages that we just unmapped.170    Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, Addr - MapAddr, Size,171                              nullptr, 0);172    CHECK_EQ(Status, ZX_OK);173 174    if (Addr == WindowBase)175      WindowBase += Size;176    WindowSize -= Size;177  }178}179 180bool MemMapFuchsia::remapImpl(uptr Addr, uptr Size, const char *Name,181                              uptr Flags) {182  const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);183  const bool PreCommit = !!(Flags & MAP_PRECOMMIT);184  const bool NoAccess = !!(Flags & MAP_NOACCESS);185 186  // NOTE: This will rename the *whole* VMO, not only the requested portion of187  // it. But we cannot do better than this given the MemMap API. In practice,188  // the upper layers of Scudo always pass the same Name for a given MemMap.189  if (Name != nullptr)190    setVmoName(Vmo, Name);191 192  uptr MappedAddr;193  zx_vm_option_t MapFlags = ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC_OVERWRITE;194  if (!NoAccess)195    MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;196  zx_status_t Status =197      _zx_vmar_map(_zx_vmar_root_self(), MapFlags, Addr - getRootVmarBase(),198                   Vmo, Addr - MapAddr, Size, &MappedAddr);199  if (UNLIKELY(Status != ZX_OK)) {200    if (AllowNoMem && IsNoMemError(Status))201      return false;202    dieOnError(Status, "zx_vmar_map", Size);203  }204  DCHECK_EQ(Addr, MappedAddr);205 206  if (PreCommit) {207    Status = _zx_vmar_op_range(_zx_vmar_root_self(), ZX_VMAR_OP_COMMIT, MapAddr,208                               Size, nullptr, 0);209    CHECK_EQ(Status, ZX_OK);210  }211 212  return true;213}214 215void MemMapFuchsia::releaseAndZeroPagesToOSImpl(uptr From, uptr Size) {216  zx_status_t Status = _zx_vmo_op_range(Vmo, ZX_VMO_OP_DECOMMIT, From - MapAddr,217                                        Size, nullptr, 0);218  CHECK_EQ(Status, ZX_OK);219}220 221void MemMapFuchsia::setMemoryPermissionImpl(uptr Addr, uptr Size, uptr Flags) {222  const bool NoAccess = !!(Flags & MAP_NOACCESS);223 224  zx_vm_option_t MapFlags = 0;225  if (!NoAccess)226    MapFlags |= ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;227  zx_status_t Status =228      _zx_vmar_protect(_zx_vmar_root_self(), MapFlags, Addr, Size);229  CHECK_EQ(Status, ZX_OK);230}231 232bool ReservedMemoryFuchsia::createImpl(UNUSED uptr Addr, uptr Size,233                                       UNUSED const char *Name, uptr Flags) {234  const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);235 236  // Reserve memory by mapping the placeholder VMO without any permission.237  zx_status_t Status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_ALLOW_FAULTS, 0,238                                    getPlaceholderVmo(), 0, Size, &Base);239  if (UNLIKELY(Status != ZX_OK)) {240    if (AllowNoMem && IsNoMemError(Status))241      return false;242    dieOnError(Status, "zx_vmar_map", Size);243  }244 245  Capacity = Size;246  return true;247}248 249void ReservedMemoryFuchsia::releaseImpl() {250  zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(), Base, Capacity);251  CHECK_EQ(Status, ZX_OK);252}253 254ReservedMemoryFuchsia::MemMapT ReservedMemoryFuchsia::dispatchImpl(uptr Addr,255                                                                   uptr Size) {256  return ReservedMemoryFuchsia::MemMapT(Addr, Size);257}258 259} // namespace scudo260 261#endif // SCUDO_FUCHSIA262