Prior to this, any allocations coming from the SizeClassAllocator would just have the vmo name "SizeClassAllocator". This will instead use the string passed to the Map functions if provided which provide slightly more info on what the vmo is used for. That is, is it for region data, metadata, or freearray space. Differential Revision: https://reviews.llvm.org/D158537
509 lines
16 KiB
C++
509 lines
16 KiB
C++
//===-- sanitizer_fuchsia.cpp ---------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file is shared between AddressSanitizer and other sanitizer
|
|
// run-time libraries and implements Fuchsia-specific functions from
|
|
// sanitizer_common.h.
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "sanitizer_fuchsia.h"
|
|
#if SANITIZER_FUCHSIA
|
|
|
|
# include <pthread.h>
|
|
# include <stdlib.h>
|
|
# include <unistd.h>
|
|
# include <zircon/errors.h>
|
|
# include <zircon/process.h>
|
|
# include <zircon/syscalls.h>
|
|
# include <zircon/utc.h>
|
|
|
|
# include "sanitizer_common.h"
|
|
# include "sanitizer_interface_internal.h"
|
|
# include "sanitizer_libc.h"
|
|
# include "sanitizer_mutex.h"
|
|
|
|
namespace __sanitizer {
|
|
|
|
void NORETURN internal__exit(int exitcode) { _zx_process_exit(exitcode); }
|
|
|
|
uptr internal_sched_yield() {
|
|
zx_status_t status = _zx_thread_legacy_yield(0u);
|
|
CHECK_EQ(status, ZX_OK);
|
|
return 0; // Why doesn't this return void?
|
|
}
|
|
|
|
void internal_usleep(u64 useconds) {
|
|
zx_status_t status = _zx_nanosleep(_zx_deadline_after(ZX_USEC(useconds)));
|
|
CHECK_EQ(status, ZX_OK);
|
|
}
|
|
|
|
u64 NanoTime() {
|
|
zx_handle_t utc_clock = _zx_utc_reference_get();
|
|
CHECK_NE(utc_clock, ZX_HANDLE_INVALID);
|
|
zx_time_t time;
|
|
zx_status_t status = _zx_clock_read(utc_clock, &time);
|
|
CHECK_EQ(status, ZX_OK);
|
|
return time;
|
|
}
|
|
|
|
u64 MonotonicNanoTime() { return _zx_clock_get_monotonic(); }
|
|
|
|
uptr internal_getpid() {
|
|
zx_info_handle_basic_t info;
|
|
zx_status_t status =
|
|
_zx_object_get_info(_zx_process_self(), ZX_INFO_HANDLE_BASIC, &info,
|
|
sizeof(info), NULL, NULL);
|
|
CHECK_EQ(status, ZX_OK);
|
|
uptr pid = static_cast<uptr>(info.koid);
|
|
CHECK_EQ(pid, info.koid);
|
|
return pid;
|
|
}
|
|
|
|
int internal_dlinfo(void *handle, int request, void *p) { UNIMPLEMENTED(); }
|
|
|
|
uptr GetThreadSelf() { return reinterpret_cast<uptr>(thrd_current()); }
|
|
|
|
tid_t GetTid() { return GetThreadSelf(); }
|
|
|
|
void Abort() { abort(); }
|
|
|
|
int Atexit(void (*function)(void)) { return atexit(function); }
|
|
|
|
void GetThreadStackTopAndBottom(bool, uptr *stack_top, uptr *stack_bottom) {
|
|
pthread_attr_t attr;
|
|
CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0);
|
|
void *base;
|
|
size_t size;
|
|
CHECK_EQ(pthread_attr_getstack(&attr, &base, &size), 0);
|
|
CHECK_EQ(pthread_attr_destroy(&attr), 0);
|
|
|
|
*stack_bottom = reinterpret_cast<uptr>(base);
|
|
*stack_top = *stack_bottom + size;
|
|
}
|
|
|
|
void InitializePlatformEarly() {}
|
|
void CheckASLR() {}
|
|
void CheckMPROTECT() {}
|
|
void PlatformPrepareForSandboxing(void *args) {}
|
|
void DisableCoreDumperIfNecessary() {}
|
|
void InstallDeadlySignalHandlers(SignalHandlerType handler) {}
|
|
void SetAlternateSignalStack() {}
|
|
void UnsetAlternateSignalStack() {}
|
|
void InitTlsSize() {}
|
|
|
|
bool SignalContext::IsStackOverflow() const { return false; }
|
|
void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); }
|
|
const char *SignalContext::Describe() const { UNIMPLEMENTED(); }
|
|
|
|
void FutexWait(atomic_uint32_t *p, u32 cmp) {
|
|
zx_status_t status = _zx_futex_wait(reinterpret_cast<zx_futex_t *>(p), cmp,
|
|
ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
|
|
if (status != ZX_ERR_BAD_STATE) // Normal race.
|
|
CHECK_EQ(status, ZX_OK);
|
|
}
|
|
|
|
void FutexWake(atomic_uint32_t *p, u32 count) {
|
|
zx_status_t status = _zx_futex_wake(reinterpret_cast<zx_futex_t *>(p), count);
|
|
CHECK_EQ(status, ZX_OK);
|
|
}
|
|
|
|
uptr GetPageSize() { return _zx_system_get_page_size(); }
|
|
|
|
uptr GetMmapGranularity() { return _zx_system_get_page_size(); }
|
|
|
|
sanitizer_shadow_bounds_t ShadowBounds;
|
|
|
|
void InitShadowBounds() { ShadowBounds = __sanitizer_shadow_bounds(); }
|
|
|
|
uptr GetMaxUserVirtualAddress() {
|
|
InitShadowBounds();
|
|
return ShadowBounds.memory_limit - 1;
|
|
}
|
|
|
|
uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); }
|
|
|
|
bool ErrorIsOOM(error_t err) { return err == ZX_ERR_NO_MEMORY; }
|
|
|
|
static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type,
|
|
bool raw_report, bool die_for_nomem) {
|
|
size = RoundUpTo(size, GetPageSize());
|
|
|
|
zx_handle_t vmo;
|
|
zx_status_t status = _zx_vmo_create(size, 0, &vmo);
|
|
if (status != ZX_OK) {
|
|
if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
|
|
ReportMmapFailureAndDie(size, mem_type, "zx_vmo_create", status,
|
|
raw_report);
|
|
return nullptr;
|
|
}
|
|
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
|
|
internal_strlen(mem_type));
|
|
|
|
// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
|
|
uintptr_t addr;
|
|
status =
|
|
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
|
|
vmo, 0, size, &addr);
|
|
_zx_handle_close(vmo);
|
|
|
|
if (status != ZX_OK) {
|
|
if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
|
|
ReportMmapFailureAndDie(size, mem_type, "zx_vmar_map", status,
|
|
raw_report);
|
|
return nullptr;
|
|
}
|
|
|
|
IncreaseTotalMmap(size);
|
|
|
|
return reinterpret_cast<void *>(addr);
|
|
}
|
|
|
|
void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) {
|
|
return DoAnonymousMmapOrDie(size, mem_type, raw_report, true);
|
|
}
|
|
|
|
void *MmapNoReserveOrDie(uptr size, const char *mem_type) {
|
|
return MmapOrDie(size, mem_type);
|
|
}
|
|
|
|
void *MmapOrDieOnFatalError(uptr size, const char *mem_type) {
|
|
return DoAnonymousMmapOrDie(size, mem_type, false, false);
|
|
}
|
|
|
|
uptr ReservedAddressRange::Init(uptr init_size, const char *name,
|
|
uptr fixed_addr) {
|
|
init_size = RoundUpTo(init_size, GetPageSize());
|
|
DCHECK_EQ(os_handle_, ZX_HANDLE_INVALID);
|
|
uintptr_t base;
|
|
zx_handle_t vmar;
|
|
zx_status_t status = _zx_vmar_allocate(
|
|
_zx_vmar_root_self(),
|
|
ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0,
|
|
init_size, &vmar, &base);
|
|
if (status != ZX_OK)
|
|
ReportMmapFailureAndDie(init_size, name, "zx_vmar_allocate", status);
|
|
base_ = reinterpret_cast<void *>(base);
|
|
size_ = init_size;
|
|
name_ = name;
|
|
os_handle_ = vmar;
|
|
|
|
return reinterpret_cast<uptr>(base_);
|
|
}
|
|
|
|
static uptr DoMmapFixedOrDie(zx_handle_t vmar, uptr fixed_addr, uptr map_size,
|
|
void *base, const char *name, bool die_for_nomem) {
|
|
uptr offset = fixed_addr - reinterpret_cast<uptr>(base);
|
|
map_size = RoundUpTo(map_size, GetPageSize());
|
|
zx_handle_t vmo;
|
|
zx_status_t status = _zx_vmo_create(map_size, 0, &vmo);
|
|
if (status != ZX_OK) {
|
|
if (status != ZX_ERR_NO_MEMORY || die_for_nomem)
|
|
ReportMmapFailureAndDie(map_size, name, "zx_vmo_create", status);
|
|
return 0;
|
|
}
|
|
_zx_object_set_property(vmo, ZX_PROP_NAME, name, internal_strlen(name));
|
|
DCHECK_GE(base + size_, map_size + offset);
|
|
uintptr_t addr;
|
|
|
|
status =
|
|
_zx_vmar_map(vmar, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC,
|
|
offset, vmo, 0, map_size, &addr);
|
|
_zx_handle_close(vmo);
|
|
if (status != ZX_OK) {
|
|
if (status != ZX_ERR_NO_MEMORY || die_for_nomem) {
|
|
ReportMmapFailureAndDie(map_size, name, "zx_vmar_map", status);
|
|
}
|
|
return 0;
|
|
}
|
|
IncreaseTotalMmap(map_size);
|
|
return addr;
|
|
}
|
|
|
|
uptr ReservedAddressRange::Map(uptr fixed_addr, uptr map_size,
|
|
const char *name) {
|
|
return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
|
|
name ? name : name_, false);
|
|
}
|
|
|
|
uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr map_size,
|
|
const char *name) {
|
|
return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_,
|
|
name ? name : name_, true);
|
|
}
|
|
|
|
void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) {
|
|
if (!addr || !size)
|
|
return;
|
|
size = RoundUpTo(size, GetPageSize());
|
|
|
|
zx_status_t status =
|
|
_zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size);
|
|
if (status != ZX_OK) {
|
|
Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n",
|
|
SanitizerToolName, size, size, addr);
|
|
CHECK("unable to unmap" && 0);
|
|
}
|
|
|
|
DecreaseTotalMmap(size);
|
|
}
|
|
|
|
void ReservedAddressRange::Unmap(uptr addr, uptr size) {
|
|
CHECK_LE(size, size_);
|
|
const zx_handle_t vmar = static_cast<zx_handle_t>(os_handle_);
|
|
if (addr == reinterpret_cast<uptr>(base_)) {
|
|
if (size == size_) {
|
|
// Destroying the vmar effectively unmaps the whole mapping.
|
|
_zx_vmar_destroy(vmar);
|
|
_zx_handle_close(vmar);
|
|
os_handle_ = static_cast<uptr>(ZX_HANDLE_INVALID);
|
|
DecreaseTotalMmap(size);
|
|
return;
|
|
}
|
|
} else {
|
|
CHECK_EQ(addr + size, reinterpret_cast<uptr>(base_) + size_);
|
|
}
|
|
// Partial unmapping does not affect the fact that the initial range is still
|
|
// reserved, and the resulting unmapped memory can't be reused.
|
|
UnmapOrDieVmar(reinterpret_cast<void *>(addr), size, vmar);
|
|
}
|
|
|
|
// This should never be called.
|
|
void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) {
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
bool MprotectNoAccess(uptr addr, uptr size) {
|
|
return _zx_vmar_protect(_zx_vmar_root_self(), 0, addr, size) == ZX_OK;
|
|
}
|
|
|
|
bool MprotectReadOnly(uptr addr, uptr size) {
|
|
return _zx_vmar_protect(_zx_vmar_root_self(), ZX_VM_PERM_READ, addr, size) ==
|
|
ZX_OK;
|
|
}
|
|
|
|
bool MprotectReadWrite(uptr addr, uptr size) {
|
|
return _zx_vmar_protect(_zx_vmar_root_self(),
|
|
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, addr,
|
|
size) == ZX_OK;
|
|
}
|
|
|
|
void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment,
|
|
const char *mem_type) {
|
|
CHECK_GE(size, GetPageSize());
|
|
CHECK(IsPowerOfTwo(size));
|
|
CHECK(IsPowerOfTwo(alignment));
|
|
|
|
zx_handle_t vmo;
|
|
zx_status_t status = _zx_vmo_create(size, 0, &vmo);
|
|
if (status != ZX_OK) {
|
|
if (status != ZX_ERR_NO_MEMORY)
|
|
ReportMmapFailureAndDie(size, mem_type, "zx_vmo_create", status, false);
|
|
return nullptr;
|
|
}
|
|
_zx_object_set_property(vmo, ZX_PROP_NAME, mem_type,
|
|
internal_strlen(mem_type));
|
|
|
|
// TODO(mcgrathr): Maybe allocate a VMAR for all sanitizer heap and use that?
|
|
|
|
// Map a larger size to get a chunk of address space big enough that
|
|
// it surely contains an aligned region of the requested size. Then
|
|
// overwrite the aligned middle portion with a mapping from the
|
|
// beginning of the VMO, and unmap the excess before and after.
|
|
size_t map_size = size + alignment;
|
|
uintptr_t addr;
|
|
status =
|
|
_zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0,
|
|
vmo, 0, map_size, &addr);
|
|
if (status == ZX_OK) {
|
|
uintptr_t map_addr = addr;
|
|
uintptr_t map_end = map_addr + map_size;
|
|
addr = RoundUpTo(map_addr, alignment);
|
|
uintptr_t end = addr + size;
|
|
if (addr != map_addr) {
|
|
zx_info_vmar_t info;
|
|
status = _zx_object_get_info(_zx_vmar_root_self(), ZX_INFO_VMAR, &info,
|
|
sizeof(info), NULL, NULL);
|
|
if (status == ZX_OK) {
|
|
uintptr_t new_addr;
|
|
status = _zx_vmar_map(
|
|
_zx_vmar_root_self(),
|
|
ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE,
|
|
addr - info.base, vmo, 0, size, &new_addr);
|
|
if (status == ZX_OK)
|
|
CHECK_EQ(new_addr, addr);
|
|
}
|
|
}
|
|
if (status == ZX_OK && addr != map_addr)
|
|
status = _zx_vmar_unmap(_zx_vmar_root_self(), map_addr, addr - map_addr);
|
|
if (status == ZX_OK && end != map_end)
|
|
status = _zx_vmar_unmap(_zx_vmar_root_self(), end, map_end - end);
|
|
}
|
|
_zx_handle_close(vmo);
|
|
|
|
if (status != ZX_OK) {
|
|
if (status != ZX_ERR_NO_MEMORY)
|
|
ReportMmapFailureAndDie(size, mem_type, "zx_vmar_map", status, false);
|
|
return nullptr;
|
|
}
|
|
|
|
IncreaseTotalMmap(size);
|
|
|
|
return reinterpret_cast<void *>(addr);
|
|
}
|
|
|
|
void UnmapOrDie(void *addr, uptr size) {
|
|
UnmapOrDieVmar(addr, size, _zx_vmar_root_self());
|
|
}
|
|
|
|
void ReleaseMemoryPagesToOS(uptr beg, uptr end) {
|
|
uptr beg_aligned = RoundUpTo(beg, GetPageSize());
|
|
uptr end_aligned = RoundDownTo(end, GetPageSize());
|
|
if (beg_aligned < end_aligned) {
|
|
zx_handle_t root_vmar = _zx_vmar_root_self();
|
|
CHECK_NE(root_vmar, ZX_HANDLE_INVALID);
|
|
zx_status_t status =
|
|
_zx_vmar_op_range(root_vmar, ZX_VMAR_OP_DECOMMIT, beg_aligned,
|
|
end_aligned - beg_aligned, nullptr, 0);
|
|
CHECK_EQ(status, ZX_OK);
|
|
}
|
|
}
|
|
|
|
void DumpProcessMap() {
|
|
// TODO(mcgrathr): write it
|
|
return;
|
|
}
|
|
|
|
bool IsAccessibleMemoryRange(uptr beg, uptr size) {
|
|
// TODO(mcgrathr): Figure out a better way.
|
|
zx_handle_t vmo;
|
|
zx_status_t status = _zx_vmo_create(size, 0, &vmo);
|
|
if (status == ZX_OK) {
|
|
status = _zx_vmo_write(vmo, reinterpret_cast<const void *>(beg), 0, size);
|
|
_zx_handle_close(vmo);
|
|
}
|
|
return status == ZX_OK;
|
|
}
|
|
|
|
// FIXME implement on this platform.
|
|
void GetMemoryProfile(fill_profile_f cb, uptr *stats) {}
|
|
|
|
bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size,
|
|
uptr *read_len, uptr max_len, error_t *errno_p) {
|
|
*errno_p = ZX_ERR_NOT_SUPPORTED;
|
|
return false;
|
|
}
|
|
|
|
void RawWrite(const char *buffer) {
|
|
constexpr size_t size = 128;
|
|
static _Thread_local char line[size];
|
|
static _Thread_local size_t lastLineEnd = 0;
|
|
static _Thread_local size_t cur = 0;
|
|
|
|
while (*buffer) {
|
|
if (cur >= size) {
|
|
if (lastLineEnd == 0)
|
|
lastLineEnd = size;
|
|
__sanitizer_log_write(line, lastLineEnd);
|
|
internal_memmove(line, line + lastLineEnd, cur - lastLineEnd);
|
|
cur = cur - lastLineEnd;
|
|
lastLineEnd = 0;
|
|
}
|
|
if (*buffer == '\n')
|
|
lastLineEnd = cur + 1;
|
|
line[cur++] = *buffer++;
|
|
}
|
|
// Flush all complete lines before returning.
|
|
if (lastLineEnd != 0) {
|
|
__sanitizer_log_write(line, lastLineEnd);
|
|
internal_memmove(line, line + lastLineEnd, cur - lastLineEnd);
|
|
cur = cur - lastLineEnd;
|
|
lastLineEnd = 0;
|
|
}
|
|
}
|
|
|
|
void CatastrophicErrorWrite(const char *buffer, uptr length) {
|
|
__sanitizer_log_write(buffer, length);
|
|
}
|
|
|
|
char **StoredArgv;
|
|
char **StoredEnviron;
|
|
|
|
char **GetArgv() { return StoredArgv; }
|
|
char **GetEnviron() { return StoredEnviron; }
|
|
|
|
const char *GetEnv(const char *name) {
|
|
if (StoredEnviron) {
|
|
uptr NameLen = internal_strlen(name);
|
|
for (char **Env = StoredEnviron; *Env != 0; Env++) {
|
|
if (internal_strncmp(*Env, name, NameLen) == 0 && (*Env)[NameLen] == '=')
|
|
return (*Env) + NameLen + 1;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
uptr ReadBinaryName(/*out*/ char *buf, uptr buf_len) {
|
|
const char *argv0 = "<UNKNOWN>";
|
|
if (StoredArgv && StoredArgv[0]) {
|
|
argv0 = StoredArgv[0];
|
|
}
|
|
internal_strncpy(buf, argv0, buf_len);
|
|
return internal_strlen(buf);
|
|
}
|
|
|
|
uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len) {
|
|
return ReadBinaryName(buf, buf_len);
|
|
}
|
|
|
|
uptr MainThreadStackBase, MainThreadStackSize;
|
|
|
|
bool GetRandom(void *buffer, uptr length, bool blocking) {
|
|
CHECK_LE(length, ZX_CPRNG_DRAW_MAX_LEN);
|
|
_zx_cprng_draw(buffer, length);
|
|
return true;
|
|
}
|
|
|
|
u32 GetNumberOfCPUs() { return zx_system_get_num_cpus(); }
|
|
|
|
uptr GetRSS() { UNIMPLEMENTED(); }
|
|
|
|
void *internal_start_thread(void *(*func)(void *arg), void *arg) { return 0; }
|
|
void internal_join_thread(void *th) {}
|
|
|
|
void InitializePlatformCommonFlags(CommonFlags *cf) {}
|
|
|
|
} // namespace __sanitizer
|
|
|
|
using namespace __sanitizer;
|
|
|
|
extern "C" {
|
|
void __sanitizer_startup_hook(int argc, char **argv, char **envp,
|
|
void *stack_base, size_t stack_size) {
|
|
__sanitizer::StoredArgv = argv;
|
|
__sanitizer::StoredEnviron = envp;
|
|
__sanitizer::MainThreadStackBase = reinterpret_cast<uintptr_t>(stack_base);
|
|
__sanitizer::MainThreadStackSize = stack_size;
|
|
}
|
|
|
|
void __sanitizer_set_report_path(const char *path) {
|
|
// Handle the initialization code in each sanitizer, but no other calls.
|
|
// This setting is never consulted on Fuchsia.
|
|
DCHECK_EQ(path, common_flags()->log_path);
|
|
}
|
|
|
|
void __sanitizer_set_report_fd(void *fd) {
|
|
UNREACHABLE("not available on Fuchsia");
|
|
}
|
|
|
|
const char *__sanitizer_get_report_path() {
|
|
UNREACHABLE("not available on Fuchsia");
|
|
}
|
|
} // extern "C"
|
|
|
|
#endif // SANITIZER_FUCHSIA
|