Path: blob/main/contrib/llvm-project/compiler-rt/lib/scudo/standalone/fuchsia.cpp
35292 views
//===-- 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 "platform.h"910#if SCUDO_FUCHSIA1112#include "common.h"13#include "mutex.h"14#include "string_utils.h"1516#include <lib/sync/mutex.h> // for sync_mutex_t17#include <stdlib.h> // for getenv()18#include <zircon/compiler.h>19#include <zircon/process.h>20#include <zircon/sanitizer.h>21#include <zircon/status.h>22#include <zircon/syscalls.h>2324namespace scudo {2526uptr getPageSize() { return _zx_system_get_page_size(); }2728void NORETURN die() { __builtin_trap(); }2930// We zero-initialize the Extra parameter of map(), make sure this is consistent31// with ZX_HANDLE_INVALID.32static_assert(ZX_HANDLE_INVALID == 0, "");3334static void NORETURN dieOnError(zx_status_t Status, const char *FnName,35uptr Size) {36ScopedString Error;37Error.append("SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,38Size >> 10, zx_status_get_string(Status));39outputRaw(Error.data());40die();41}4243static void *allocateVmar(uptr Size, MapPlatformData *Data, bool AllowNoMem) {44// Only scenario so far.45DCHECK(Data);46DCHECK_EQ(Data->Vmar, ZX_HANDLE_INVALID);4748const zx_status_t Status = _zx_vmar_allocate(49_zx_vmar_root_self(),50ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0,51Size, &Data->Vmar, &Data->VmarBase);52if (UNLIKELY(Status != ZX_OK)) {53if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)54dieOnError(Status, "zx_vmar_allocate", Size);55return nullptr;56}57return reinterpret_cast<void *>(Data->VmarBase);58}5960void *map(void *Addr, uptr Size, const char *Name, uptr Flags,61MapPlatformData *Data) {62DCHECK_EQ(Size % getPageSizeCached(), 0);63const bool AllowNoMem = !!(Flags & MAP_ALLOWNOMEM);6465// For MAP_NOACCESS, just allocate a Vmar and return.66if (Flags & MAP_NOACCESS)67return allocateVmar(Size, Data, AllowNoMem);6869const zx_handle_t Vmar = (Data && Data->Vmar != ZX_HANDLE_INVALID)70? Data->Vmar71: _zx_vmar_root_self();7273zx_status_t Status;74zx_handle_t Vmo;75uint64_t VmoSize = 0;76if (Data && Data->Vmo != ZX_HANDLE_INVALID) {77// If a Vmo was specified, it's a resize operation.78CHECK(Addr);79DCHECK(Flags & MAP_RESIZABLE);80Vmo = Data->Vmo;81VmoSize = Data->VmoSize;82Status = _zx_vmo_set_size(Vmo, VmoSize + Size);83if (Status != ZX_OK) {84if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)85dieOnError(Status, "zx_vmo_set_size", VmoSize + Size);86return nullptr;87}88} else {89// Otherwise, create a Vmo and set its name.90Status = _zx_vmo_create(Size, ZX_VMO_RESIZABLE, &Vmo);91if (UNLIKELY(Status != ZX_OK)) {92if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)93dieOnError(Status, "zx_vmo_create", Size);94return nullptr;95}96_zx_object_set_property(Vmo, ZX_PROP_NAME, Name, strlen(Name));97}9899uintptr_t P;100zx_vm_option_t MapFlags =101ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_ALLOW_FAULTS;102if (Addr)103DCHECK(Data);104const uint64_t Offset =105Addr ? reinterpret_cast<uintptr_t>(Addr) - Data->VmarBase : 0;106if (Offset)107MapFlags |= ZX_VM_SPECIFIC;108Status = _zx_vmar_map(Vmar, MapFlags, Offset, Vmo, VmoSize, Size, &P);109if (UNLIKELY(Status != ZX_OK)) {110if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)111dieOnError(Status, "zx_vmar_map", Size);112return nullptr;113}114115if (Flags & MAP_PRECOMMIT) {116Status = _zx_vmar_op_range(Vmar, ZX_VMAR_OP_COMMIT, P, Size,117/*buffer=*/nullptr, /*buffer_size=*/0);118}119120// No need to track the Vmo if we don't intend on resizing it. Close it.121if (Flags & MAP_RESIZABLE) {122DCHECK(Data);123if (Data->Vmo == ZX_HANDLE_INVALID)124Data->Vmo = Vmo;125else126DCHECK_EQ(Data->Vmo, Vmo);127} else {128CHECK_EQ(_zx_handle_close(Vmo), ZX_OK);129}130if (UNLIKELY(Status != ZX_OK)) {131if (Status != ZX_ERR_NO_MEMORY || !AllowNoMem)132dieOnError(Status, "zx_vmar_op_range", Size);133return nullptr;134}135136if (Data)137Data->VmoSize += Size;138139return reinterpret_cast<void *>(P);140}141142void unmap(void *Addr, uptr Size, uptr Flags, MapPlatformData *Data) {143if (Flags & UNMAP_ALL) {144DCHECK_NE(Data, nullptr);145const zx_handle_t Vmar = Data->Vmar;146DCHECK_NE(Vmar, _zx_vmar_root_self());147// Destroying the vmar effectively unmaps the whole mapping.148CHECK_EQ(_zx_vmar_destroy(Vmar), ZX_OK);149CHECK_EQ(_zx_handle_close(Vmar), ZX_OK);150} else {151const zx_handle_t Vmar = (Data && Data->Vmar != ZX_HANDLE_INVALID)152? Data->Vmar153: _zx_vmar_root_self();154const zx_status_t Status =155_zx_vmar_unmap(Vmar, reinterpret_cast<uintptr_t>(Addr), Size);156if (UNLIKELY(Status != ZX_OK))157dieOnError(Status, "zx_vmar_unmap", Size);158}159if (Data) {160if (Data->Vmo != ZX_HANDLE_INVALID)161CHECK_EQ(_zx_handle_close(Data->Vmo), ZX_OK);162memset(Data, 0, sizeof(*Data));163}164}165166void setMemoryPermission(UNUSED uptr Addr, UNUSED uptr Size, UNUSED uptr Flags,167UNUSED MapPlatformData *Data) {168const zx_vm_option_t Prot =169(Flags & MAP_NOACCESS) ? 0 : (ZX_VM_PERM_READ | ZX_VM_PERM_WRITE);170DCHECK(Data);171DCHECK_NE(Data->Vmar, ZX_HANDLE_INVALID);172const zx_status_t Status = _zx_vmar_protect(Data->Vmar, Prot, Addr, Size);173if (Status != ZX_OK)174dieOnError(Status, "zx_vmar_protect", Size);175}176177void releasePagesToOS(UNUSED uptr BaseAddress, uptr Offset, uptr Size,178MapPlatformData *Data) {179// TODO: DCHECK the BaseAddress is consistent with the data in180// MapPlatformData.181DCHECK(Data);182DCHECK_NE(Data->Vmar, ZX_HANDLE_INVALID);183DCHECK_NE(Data->Vmo, ZX_HANDLE_INVALID);184const zx_status_t Status =185_zx_vmo_op_range(Data->Vmo, ZX_VMO_OP_DECOMMIT, Offset, Size, NULL, 0);186CHECK_EQ(Status, ZX_OK);187}188189const char *getEnv(const char *Name) { return getenv(Name); }190191// Note: we need to flag these methods with __TA_NO_THREAD_SAFETY_ANALYSIS192// because the Fuchsia implementation of sync_mutex_t has clang thread safety193// annotations. Were we to apply proper capability annotations to the top level194// HybridMutex class itself, they would not be needed. As it stands, the195// thread analysis thinks that we are locking the mutex and accidentally leaving196// it locked on the way out.197bool HybridMutex::tryLock() __TA_NO_THREAD_SAFETY_ANALYSIS {198// Size and alignment must be compatible between both types.199return sync_mutex_trylock(&M) == ZX_OK;200}201202void HybridMutex::lockSlow() __TA_NO_THREAD_SAFETY_ANALYSIS {203sync_mutex_lock(&M);204}205206void HybridMutex::unlock() __TA_NO_THREAD_SAFETY_ANALYSIS {207sync_mutex_unlock(&M);208}209210void HybridMutex::assertHeldImpl() __TA_NO_THREAD_SAFETY_ANALYSIS {}211212u64 getMonotonicTime() { return _zx_clock_get_monotonic(); }213u64 getMonotonicTimeFast() { return _zx_clock_get_monotonic(); }214215u32 getNumberOfCPUs() { return _zx_system_get_num_cpus(); }216217u32 getThreadID() { return 0; }218219bool getRandom(void *Buffer, uptr Length, UNUSED bool Blocking) {220static_assert(MaxRandomLength <= ZX_CPRNG_DRAW_MAX_LEN, "");221if (UNLIKELY(!Buffer || !Length || Length > MaxRandomLength))222return false;223_zx_cprng_draw(Buffer, Length);224return true;225}226227void outputRaw(const char *Buffer) {228__sanitizer_log_write(Buffer, strlen(Buffer));229}230231void setAbortMessage(const char *Message) {}232233} // namespace scudo234235#endif // SCUDO_FUCHSIA236237238