Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -276,6 +276,11 @@ append_list_if(COMPILER_RT_HAS_GCC_S_LIB gcc_s SANITIZER_COMMON_LINK_LIBS) endif() +if("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") + list(APPEND SANITIZER_COMMON_LINK_FLAGS -Wl,-z,defs,-z,now,-z,relro) + list(APPEND SANITIZER_COMMON_LINK_LIBS magenta) +endif() + # Warnings to turn off for all libraries, not just sanitizers. append_string_if(COMPILER_RT_HAS_WUNUSED_PARAMETER_FLAG -Wno-unused-parameter CMAKE_C_FLAGS CMAKE_CXX_FLAGS) Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -460,7 +460,7 @@ list_replace(COMPILER_RT_SANITIZERS_TO_BUILD all "${ALL_SANITIZERS}") if (SANITIZER_COMMON_SUPPORTED_ARCH AND NOT LLVM_USE_SANITIZER AND - (OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD" OR + (OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD|Fuchsia" OR (OS_NAME MATCHES "Windows" AND (NOT MINGW AND NOT CYGWIN)))) set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE) else() @@ -523,7 +523,7 @@ endif() if (COMPILER_RT_HAS_SANITIZER_COMMON AND UBSAN_SUPPORTED_ARCH AND - OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android") + OS_NAME MATCHES "Darwin|Linux|FreeBSD|Windows|Android|Fuchsia") set(COMPILER_RT_HAS_UBSAN TRUE) else() set(COMPILER_RT_HAS_UBSAN FALSE) Index: lib/interception/interception.h =================================================================== --- lib/interception/interception.h +++ lib/interception/interception.h @@ -16,7 +16,7 @@ #define INTERCEPTION_H #if !defined(__linux__) && !defined(__FreeBSD__) && \ - !defined(__APPLE__) && !defined(_WIN32) + !defined(__APPLE__) && !defined(_WIN32) && !defined(__Fuchsia__) # error "Interception doesn't work on this operating system." #endif @@ -139,7 +139,7 @@ # define DECLARE_WRAPPER(ret_type, func, ...) \ extern "C" ret_type func(__VA_ARGS__) \ __attribute__((alias("__interceptor_" #func), visibility("default"))); -#else +#elif !defined(__Fuchsia__) # define WRAP(x) __interceptor_ ## x # define WRAPPER_NAME(x) "__interceptor_" #x # define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) @@ -148,7 +148,15 @@ __attribute__((weak, alias("__interceptor_" #func), visibility("default"))); #endif -#if !defined(__APPLE__) +#if defined(__Fuchsia__) +// There is no general interception at all on Fuchsia. +// Sanitizer runtimes just define functions directly to preempt them, +// and have bespoke ways to access the underlying libc functions. +# include +# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) +# define REAL(x) __unsanitized_##x +# define DECLARE_REAL(ret_type, func, ...) +#elif !defined(__APPLE__) # define PTR_TO_REAL(x) real_##x # define REAL(x) __interception::PTR_TO_REAL(x) # define FUNC_TYPE(x) x##_f @@ -166,15 +174,19 @@ # define ASSIGN_REAL(x, y) #endif // __APPLE__ +#if !defined(__Fuchsia__) #define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) \ DECLARE_REAL(ret_type, func, __VA_ARGS__) \ extern "C" ret_type WRAP(func)(__VA_ARGS__); +#else +#define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...) +#endif // Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR // macros does its job. In exceptional cases you may need to call REAL(foo) // without defining INTERCEPTOR(..., foo, ...). For example, if you override // foo with an interceptor for other function. -#if !defined(__APPLE__) +#if !defined(__APPLE__) && !defined(__Fuchsia__) # define DEFINE_REAL(ret_type, func, ...) \ typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ namespace __interception { \ @@ -184,7 +196,20 @@ # define DEFINE_REAL(ret_type, func, ...) #endif -#if !defined(__APPLE__) +#if defined(__Fuchsia__) + +// We need to define the __interceptor_func name just to get +// sanitizer_common/scripts/gen_dynamic_list.py to export func. +// But we don't need to export __interceptor_func to get that. +#define INTERCEPTOR(ret_type, func, ...) \ + extern "C" [[gnu::alias(#func), gnu::visibility("hidden")]] \ + ret_type __interceptor_##func(__VA_ARGS__); \ + extern "C" \ + INTERCEPTOR_ATTRIBUTE \ + ret_type func(__VA_ARGS__) + +#elif !defined(__APPLE__) + #define INTERCEPTOR(ret_type, func, ...) \ DEFINE_REAL(ret_type, func, __VA_ARGS__) \ DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \ @@ -251,7 +276,7 @@ # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func) # define INTERCEPT_FUNCTION_VER(func, symver) \ INTERCEPT_FUNCTION_VER_MAC(func, symver) -#else // defined(_WIN32) +#elif defined(_WIN32) # include "interception_win.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_WIN(func) # define INTERCEPT_FUNCTION_VER(func, symver) \ Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -10,6 +10,7 @@ sanitizer_file.cc sanitizer_flags.cc sanitizer_flag_parser.cc + sanitizer_fuchsia.cc sanitizer_libc.cc sanitizer_libignore.cc sanitizer_linux.cc @@ -30,6 +31,7 @@ sanitizer_stoptheworld_mac.cc sanitizer_suppressions.cc sanitizer_symbolizer.cc + sanitizer_symbolizer_fuchsia.cc sanitizer_symbolizer_libbacktrace.cc sanitizer_symbolizer_mac.cc sanitizer_symbolizer_win.cc @@ -101,6 +103,7 @@ sanitizer_flag_parser.h sanitizer_flags.h sanitizer_flags.inc + sanitizer_fuchsia.h sanitizer_interface_internal.h sanitizer_internal_defs.h sanitizer_lfstack.h Index: lib/sanitizer_common/sanitizer_common_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_common_libcdep.cc +++ lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -26,12 +26,26 @@ namespace __sanitizer { +#if !SANITIZER_FUCHSIA + bool ReportFile::SupportsColors() { SpinMutexLock l(mu); ReopenIfNecessary(); return SupportsColoredOutput(fd); } +static INLINE bool ReportSupportsColors() { + return report_file.SupportsColors(); +} + +#else // SANITIZER_FUCHSIA + +static INLINE bool ReportSupportsColors() { + return true; +} + +#endif // !SANITIZER_FUCHSIA + bool ColorizeReports() { // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color // printing on Windows. @@ -40,7 +54,7 @@ const char *flag = common_flags()->color; return internal_strcmp(flag, "always") == 0 || - (internal_strcmp(flag, "auto") == 0 && report_file.SupportsColors()); + (internal_strcmp(flag, "auto") == 0 && ReportSupportsColors()); } static void (*sandboxing_callback)(); Index: lib/sanitizer_common/sanitizer_file.cc =================================================================== --- lib/sanitizer_common/sanitizer_file.cc +++ lib/sanitizer_common/sanitizer_file.cc @@ -17,6 +17,8 @@ #include "sanitizer_common.h" #include "sanitizer_file.h" +#if !SANITIZER_FUCHSIA + namespace __sanitizer { void CatastrophicErrorWrite(const char *buffer, uptr length) { @@ -169,3 +171,5 @@ report_file.fd_pid = internal_getpid(); } } // extern "C" + +#endif // !SANITIZER_FUCHSIA Index: lib/sanitizer_common/sanitizer_fuchsia.h =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_fuchsia.h @@ -0,0 +1,35 @@ +//===-- sanitizer_fuchsia.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// Fuchsia-specific sanitizer support. +// +//===---------------------------------------------------------------------===// +#ifndef SANITIZER_FUCHSIA_H +#define SANITIZER_FUCHSIA_H + +#include "sanitizer_common.h" +#if SANITIZER_FUCHSIA + +#include + +namespace __sanitizer { + +struct DataInfo; + +void RenderStackFrame(InternalScopedString *buffer, + unsigned int frame_no, uptr pc); +void RenderDataInfo(InternalScopedString *buffer, const DataInfo *DI); + +extern uptr MainThreadStackBase, MainThreadStackSize; +extern sanitizer_shadow_bounds_t ShadowBounds; + +} // namespace __sanitizer + +#endif // SANITIZER_FUCHSIA +#endif // SANITIZER_FUCHSIA_H Index: lib/sanitizer_common/sanitizer_fuchsia.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_fuchsia.cc @@ -0,0 +1,538 @@ +//===-- sanitizer_fuchsia.cc ---------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// 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 "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_mutex.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_stacktrace.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace __sanitizer { + +void NORETURN internal__exit(int exitcode) { + _mx_process_exit(exitcode); +} + +uptr internal_sched_yield() { + mx_status_t status = _mx_nanosleep(0); + CHECK_EQ(status, MX_OK); + return 0; // Why doesn't this return void? +} + +static void internal_nanosleep(mx_time_t ns) { + mx_status_t status = _mx_nanosleep(_mx_deadline_after(ns)); + CHECK_EQ(status, MX_OK); +} + +unsigned int internal_sleep(unsigned int seconds) { + internal_nanosleep(MX_SEC(seconds)); + return 0; +} + +u64 NanoTime() { + return _mx_time_get(MX_CLOCK_UTC); +} + +uptr internal_getpid() { + mx_info_handle_basic_t info; + mx_status_t status = _mx_object_get_info(_mx_process_self(), + MX_INFO_HANDLE_BASIC, + &info, sizeof(info), NULL, NULL); + CHECK_EQ(status, MX_OK); + uptr pid = static_cast(info.koid); + CHECK_EQ(pid, info.koid); + return pid; +} + +uptr GetThreadSelf() { + return reinterpret_cast(thrd_current()); +} + +uptr GetTid() { + return GetThreadSelf(); +} + +void Abort() { + abort(); +} + +int Atexit(void (*function)(void)) { + return atexit(function); +} + +void SleepForSeconds(int seconds) { + internal_sleep(seconds); +} + +void SleepForMillis(int millis) { + internal_nanosleep(MX_MSEC(millis)); +} + +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(base); + *stack_top = *stack_bottom + size; +} + +void MaybeReexec() {} +void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) {} +void DisableCoreDumperIfNecessary() {} +void InstallDeadlySignalHandlers(SignalHandlerType handler) {} +void SetAlternateSignalStack() {} +void UnsetAlternateSignalStack() {} +void InitTlsSize() {} + +void PrintModuleMap() {} + +struct UnwindTraceArg { + BufferedStackTrace *stack; + u32 max_depth; +}; + +_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { + UnwindTraceArg *arg = static_cast(param); + CHECK_LT(arg->stack->size, arg->max_depth); + uptr pc = _Unwind_GetIP(ctx); + if (pc < PAGE_SIZE) + return _URC_NORMAL_STOP; + arg->stack->trace_buffer[arg->stack->size++] = pc; + return (arg->stack->size == arg->max_depth ? + _URC_NORMAL_STOP : _URC_NO_REASON); +} + +void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { + CHECK_GE(max_depth, 2); + size = 0; + UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; + _Unwind_Backtrace(Unwind_Trace, &arg); + CHECK_GT(size, 0); + // We need to pop a few frames so that pc is on top. + uptr to_pop = LocatePcInTrace(pc); + // trace_buffer[0] belongs to the current function so we always pop it, + // unless there is only 1 frame in the stack trace (1 frame is always better + // than 0!). + PopStackFrames(Min(to_pop, static_cast(1))); + trace_buffer[0] = pc; +} + +void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, + u32 max_depth) { + CHECK_NE(context, nullptr); + UNREACHABLE("signal context doesn't exist"); +} + +enum MutexState : int { + MtxUnlocked = 0, + MtxLocked = 1, + MtxSleeping = 2 +}; + +BlockingMutex::BlockingMutex() { + // NOTE! It's important that this use internal_memset, because plain + // memset might be intercepted (e.g., actually be __asan_memset). + // Defining this so the compiler initializes each field, e.g.: + // BlockingMutex::BlockingMutex() : BlockingMutex(LINKER_INITIALIZED) {} + // might result in the compiler generating a call to memset, which would + // have the same problem. + internal_memset(this, 0, sizeof(*this)); +} + +void BlockingMutex::Lock() { + CHECK_EQ(owner_, 0); + atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); + if (atomic_exchange(m, MtxLocked, memory_order_acquire) == MtxUnlocked) + return; + while (atomic_exchange(m, MtxSleeping, memory_order_acquire) != MtxUnlocked) { + mx_status_t status = _mx_futex_wait(reinterpret_cast(m), + MtxSleeping, MX_TIME_INFINITE); + if (status != MX_ERR_BAD_STATE) // Normal race. + CHECK_EQ(status, MX_OK); + } +} + +void BlockingMutex::Unlock() { + atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); + u32 v = atomic_exchange(m, MtxUnlocked, memory_order_release); + CHECK_NE(v, MtxUnlocked); + if (v == MtxSleeping) { + mx_status_t status = _mx_futex_wake(reinterpret_cast(m), 1); + CHECK_EQ(status, MX_OK); + } +} + +void BlockingMutex::CheckLocked() { + atomic_uint32_t *m = reinterpret_cast(&opaque_storage_); + CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed)); +} + +uptr GetPageSize() { + return PAGE_SIZE; +} + +uptr GetMmapGranularity() { + return PAGE_SIZE; +} + +sanitizer_shadow_bounds_t ShadowBounds; + +uptr GetMaxVirtualAddress() { + ShadowBounds = __sanitizer_shadow_bounds(); + return ShadowBounds.memory_limit - 1; +} + +static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type, + bool raw_report, bool die_for_nomem) { + size = RoundUpTo(size, PAGE_SIZE); + + mx_handle_t vmo; + mx_status_t status = _mx_vmo_create(size, 0, &vmo); + if (status != MX_OK) { + if (status != MX_ERR_NO_MEMORY || die_for_nomem) + ReportMmapFailureAndDie(size, mem_type, "mx_vmo_create", + status, raw_report); + return nullptr; + } + _mx_object_set_property(vmo, MX_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 = _mx_vmar_map(_mx_vmar_root_self(), 0, vmo, 0, size, + MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE, &addr); + _mx_handle_close(vmo); + + if (status != MX_OK) { + if (status != MX_ERR_NO_MEMORY || die_for_nomem) + ReportMmapFailureAndDie(size, mem_type, "mx_vmar_map", + status, raw_report); + return nullptr; + } + + IncreaseTotalMmap(size); + + return reinterpret_cast(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); +} + +// MmapNoAccess and MmapFixedOrDie are used only by sanitizer_allocator. +// Instead of doing exactly what they say, we make MmapNoAccess actually +// just allocate a VMAR to reserve the address space. Then MmapFixedOrDie +// uses that VMAR instead of the root. + +mx_handle_t allocator_vmar = MX_HANDLE_INVALID; +uintptr_t allocator_vmar_base; +size_t allocator_vmar_size; + +void *MmapNoAccess(uptr size) { + size = RoundUpTo(size, PAGE_SIZE); + CHECK_EQ(allocator_vmar, MX_HANDLE_INVALID); + uintptr_t base; + mx_status_t status = _mx_vmar_allocate(_mx_vmar_root_self(), 0, size, + MX_VM_FLAG_CAN_MAP_READ | + MX_VM_FLAG_CAN_MAP_WRITE | + MX_VM_FLAG_CAN_MAP_SPECIFIC, + &allocator_vmar, &base); + if (status != MX_OK) + ReportMmapFailureAndDie(size, "sanitizer allocator address space", + "mx_vmar_allocate", status); + + allocator_vmar_base = base; + allocator_vmar_size = size; + return reinterpret_cast(base); +} + +constexpr const char kAllocatorVmoName[] = "sanitizer_allocator"; + +static void *DoMmapFixedOrDie(uptr fixed_addr, uptr size, bool die_for_nomem) { + size = RoundUpTo(size, PAGE_SIZE); + + mx_handle_t vmo; + mx_status_t status = _mx_vmo_create(size, 0, &vmo); + if (status != MX_OK) { + if (status != MX_ERR_NO_MEMORY || die_for_nomem) + ReportMmapFailureAndDie(size, kAllocatorVmoName, + "mx_vmo_create", status); + return nullptr; + } + _mx_object_set_property(vmo, MX_PROP_NAME, + kAllocatorVmoName, sizeof(kAllocatorVmoName) - 1); + + DCHECK_GE(fixed_addr, allocator_vmar_base); + uintptr_t offset = fixed_addr - allocator_vmar_base; + DCHECK_LE(size, allocator_vmar_size); + DCHECK_GE(allocator_vmar_size - offset, size); + + uintptr_t addr; + status = _mx_vmar_map(allocator_vmar, offset, vmo, 0, size, + MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE | + MX_VM_FLAG_SPECIFIC, + &addr); + _mx_handle_close(vmo); + if (status != MX_OK) { + if (status != MX_ERR_NO_MEMORY || die_for_nomem) + ReportMmapFailureAndDie(size, kAllocatorVmoName, "mx_vmar_map", status); + return nullptr; + } + + IncreaseTotalMmap(size); + + return reinterpret_cast(addr); +} + +void *MmapFixedOrDie(uptr fixed_addr, uptr size) { + return DoMmapFixedOrDie(fixed_addr, size, true); +} + +void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size) { + return DoMmapFixedOrDie(fixed_addr, size, false); +} + +// This should never be called. +void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) { + UNIMPLEMENTED(); +} + +void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, + const char *mem_type) { + CHECK_GE(size, PAGE_SIZE); + CHECK(IsPowerOfTwo(size)); + CHECK(IsPowerOfTwo(alignment)); + + mx_handle_t vmo; + mx_status_t status = _mx_vmo_create(size, 0, &vmo); + if (status != MX_OK) { + if (status != MX_ERR_NO_MEMORY) + ReportMmapFailureAndDie(size, mem_type, "mx_vmo_create", status, false); + return nullptr; + } + _mx_object_set_property(vmo, MX_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 = _mx_vmar_map(_mx_vmar_root_self(), 0, vmo, 0, map_size, + MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE, &addr); + if (status == MX_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) { + mx_info_vmar_t info; + status = _mx_object_get_info(_mx_vmar_root_self(), MX_INFO_VMAR, + &info, sizeof(info), NULL, NULL); + if (status == MX_OK) { + uintptr_t new_addr; + status = _mx_vmar_map(_mx_vmar_root_self(), addr - info.base, + vmo, 0, size, + MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE | + MX_VM_FLAG_SPECIFIC_OVERWRITE, + &new_addr); + if (status == MX_OK) + CHECK_EQ(new_addr, addr); + } + } + if (status == MX_OK && addr != map_addr) + status = _mx_vmar_unmap(_mx_vmar_root_self(), map_addr, addr - map_addr); + if (status == MX_OK && end != map_end) + status = _mx_vmar_unmap(_mx_vmar_root_self(), end, map_end - end); + } + _mx_handle_close(vmo); + + if (status != MX_OK) { + if (status != MX_ERR_NO_MEMORY) + ReportMmapFailureAndDie(size, mem_type, "mx_vmar_map", status, false); + return nullptr; + } + + IncreaseTotalMmap(size); + + return reinterpret_cast(addr); +} + +void UnmapOrDie(void *addr, uptr size) { + if (!addr || !size) return; + size = RoundUpTo(size, PAGE_SIZE); + + mx_status_t status = _mx_vmar_unmap(_mx_vmar_root_self(), + reinterpret_cast(addr), size); + if (status != MX_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); +} + +// This is used on the shadow mapping, which cannot be changed. +// Magenta doesn't have anything like MADV_DONTNEED. +void ReleaseMemoryPagesToOS(uptr beg, uptr end) { +} + +void DumpProcessMap() { + UNIMPLEMENTED(); // TODO(mcgrathr): write it +} + +bool IsAccessibleMemoryRange(uptr beg, uptr size) { + // TODO(mcgrathr): Figure out a better way. + mx_handle_t vmo; + mx_status_t status = _mx_vmo_create(size, 0, &vmo); + if (status == MX_OK) { + while (size > 0) { + size_t wrote; + status = _mx_vmo_write(vmo, reinterpret_cast(beg), 0, size, + &wrote); + if (status != MX_OK) + break; + CHECK_GT(wrote, 0); + CHECK_LE(wrote, size); + beg += wrote; + size -= wrote; + } + _mx_handle_close(vmo); + } + return status == MX_OK; +} + +// FIXME implement on this platform. +void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { } + +bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, + uptr *read_len, uptr max_len, error_t *errno_p) { + mx_handle_t vmo; + mx_status_t status = __sanitizer_get_configuration(file_name, &vmo); + if (status == MX_OK) { + uint64_t vmo_size; + status = _mx_vmo_get_size(vmo, &vmo_size); + if (status == MX_OK) { + if (vmo_size < max_len) + max_len = vmo_size; + size_t map_size = RoundUpTo(max_len, PAGE_SIZE); + uintptr_t addr; + status = _mx_vmar_map(_mx_vmar_root_self(), 0, vmo, 0, map_size, + MX_VM_FLAG_PERM_READ, &addr); + if (status == MX_OK) { + *buff = reinterpret_cast(addr); + *buff_size = map_size; + *read_len = max_len; + } + } + _mx_handle_close(vmo); + } + if (status != MX_OK && errno_p) + *errno_p = status; + return status == MX_OK; +} + +void RawWrite(const char *buffer) { + __sanitizer_log_write(buffer, internal_strlen(buffer)); +} + +void CatastrophicErrorWrite(const char *buffer, uptr length) { + __sanitizer_log_write(buffer, length); +} + +char **StoredArgv; +char **StoredEnviron; + +char** GetArgv() { + return StoredArgv; +} + +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 = StoredArgv[0]; + if (!argv0) + argv0 = ""; + 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; + +} // namespace __sanitizer + +using namespace __sanitizer; // NOLINT + +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(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"); +} +} // extern "C" + +#endif // SANITIZER_FUCHSIA Index: lib/sanitizer_common/sanitizer_platform.h =================================================================== --- lib/sanitizer_common/sanitizer_platform.h +++ lib/sanitizer_common/sanitizer_platform.h @@ -14,7 +14,7 @@ #define SANITIZER_PLATFORM_H #if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \ - !defined(__APPLE__) && !defined(_WIN32) + !defined(__APPLE__) && !defined(_WIN32) && !defined(__Fuchsia__) # error "This operating system is not supported" #endif @@ -85,6 +85,12 @@ # define SANITIZER_ANDROID 0 #endif +#if defined(__Fuchsia__) +# define SANITIZER_FUCHSIA 1 +#else +# define SANITIZER_FUCHSIA 0 +#endif + #define SANITIZER_POSIX \ (SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_MAC || SANITIZER_NETBSD) @@ -270,7 +276,8 @@ // pthread_exit() performs unwinding that leads to dlopen'ing libgcc_s.so. // dlopen mallocs "libgcc_s.so" string which confuses LSan, it fails to realize // that this allocation happens in dynamic linker and should be ignored. -#if SANITIZER_PPC || defined(__thumb__) +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) && \ + (SANITIZER_PPC || defined(__thumb__)) # define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 1 #else # define SANITIZER_SUPPRESS_LEAK_ON_PTHREAD_EXIT 0 Index: lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_interceptors.h +++ lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -18,11 +18,19 @@ #if !SANITIZER_WINDOWS # define SI_WINDOWS 0 -# define SI_NOT_WINDOWS 1 -# include "sanitizer_platform_limits_posix.h" #else # define SI_WINDOWS 1 +#endif + +// TODO(mcgrathr): Should be renamed SI_UNIX or something. +#if SANITIZER_WINDOWS || SANITIZER_FUCHSIA # define SI_NOT_WINDOWS 0 +#else +# define SI_NOT_WINDOWS 1 +#endif + +#if SI_NOT_WINDOWS +# include "sanitizer_platform_limits_posix.h" #endif #if SANITIZER_POSIX @@ -75,7 +83,15 @@ # define SI_IOS 0 #endif -#if !SANITIZER_WINDOWS && !SANITIZER_MAC +#if SANITIZER_FUCHSIA +# define SI_FUCHSIA 1 +# define SI_NOT_FUCHSIA 0 +#else +# define SI_FUCHSIA 0 +# define SI_NOT_FUCHSIA 1 +#endif + +#if !SANITIZER_WINDOWS && !SANITIZER_MAC && !SANITIZER_FUCHSIA # define SI_UNIX_NOT_MAC 1 #else # define SI_UNIX_NOT_MAC 0 @@ -87,23 +103,23 @@ # define SI_LINUX_NOT_FREEBSD 0 #endif -#define SANITIZER_INTERCEPT_STRLEN 1 -#define SANITIZER_INTERCEPT_STRNLEN SI_NOT_MAC -#define SANITIZER_INTERCEPT_STRCMP 1 -#define SANITIZER_INTERCEPT_STRSTR 1 +#define SANITIZER_INTERCEPT_STRLEN SI_NOT_FUCHSIA +#define SANITIZER_INTERCEPT_STRNLEN SI_NOT_MAC && SI_NOT_FUCHSIA +#define SANITIZER_INTERCEPT_STRCMP SI_NOT_FUCHSIA +#define SANITIZER_INTERCEPT_STRSTR SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS -#define SANITIZER_INTERCEPT_STRTOK 1 -#define SANITIZER_INTERCEPT_STRCHR 1 +#define SANITIZER_INTERCEPT_STRTOK SI_NOT_FUCHSIA +#define SANITIZER_INTERCEPT_STRCHR SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_STRCHRNUL SI_UNIX_NOT_MAC -#define SANITIZER_INTERCEPT_STRRCHR 1 -#define SANITIZER_INTERCEPT_STRSPN 1 -#define SANITIZER_INTERCEPT_STRPBRK 1 +#define SANITIZER_INTERCEPT_STRRCHR SI_NOT_FUCHSIA +#define SANITIZER_INTERCEPT_STRSPN SI_NOT_FUCHSIA +#define SANITIZER_INTERCEPT_STRPBRK SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MEMSET 1 #define SANITIZER_INTERCEPT_MEMMOVE 1 #define SANITIZER_INTERCEPT_MEMCPY 1 -#define SANITIZER_INTERCEPT_MEMCMP 1 +#define SANITIZER_INTERCEPT_MEMCMP SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_STRNDUP SI_POSIX #define SANITIZER_INTERCEPT___STRNDUP SI_LINUX_NOT_FREEBSD #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ @@ -116,8 +132,7 @@ // FIXME: enable memmem on Windows. #define SANITIZER_INTERCEPT_MEMMEM \ (SI_NOT_WINDOWS && !SI_MAC_DEPLOYMENT_BELOW_10_7) -#define SANITIZER_INTERCEPT_MEMCHR 1 -#define SANITIZER_INTERCEPT_MEMRCHR (SI_FREEBSD || SI_LINUX || SI_NETBSD) +#define SANITIZER_INTERCEPT_MEMCHR SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS @@ -153,7 +168,7 @@ # define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID #endif -#define SANITIZER_INTERCEPT_FREXP 1 +#define SANITIZER_INTERCEPT_FREXP SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS @@ -369,10 +384,12 @@ (SI_LINUX_NOT_ANDROID || SI_MAC || SI_FREEBSD || SI_NETBSD) #define SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO \ - (!SI_FREEBSD && !SI_MAC && !SI_NETBSD) + (!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_FUCHSIA) #define SANITIZER_INTERCEPT_MEMALIGN (!SI_FREEBSD && !SI_MAC && !SI_NETBSD) -#define SANITIZER_INTERCEPT_PVALLOC (!SI_FREEBSD && !SI_MAC && !SI_NETBSD) -#define SANITIZER_INTERCEPT_CFREE (!SI_FREEBSD && !SI_MAC && !SI_NETBSD) +#define SANITIZER_INTERCEPT_PVALLOC \ + (!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_FUCHSIA) +#define SANITIZER_INTERCEPT_CFREE \ + (!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_FUCHSIA) #define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC) #define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC) #define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID Index: lib/sanitizer_common/sanitizer_printf.cc =================================================================== --- lib/sanitizer_common/sanitizer_printf.cc +++ lib/sanitizer_common/sanitizer_printf.cc @@ -256,7 +256,7 @@ RAW_CHECK_MSG(needed_length < kLen, \ "Buffer in Report is too short!\n"); \ } - if (append_pid) { + if (!SANITIZER_FUCHSIA && append_pid) { int pid = internal_getpid(); const char *exe_name = GetProcessName(); if (common_flags()->log_exe_name && exe_name) { Index: lib/sanitizer_common/sanitizer_stacktrace_printer.cc =================================================================== --- lib/sanitizer_common/sanitizer_stacktrace_printer.cc +++ lib/sanitizer_common/sanitizer_stacktrace_printer.cc @@ -13,6 +13,7 @@ #include "sanitizer_stacktrace_printer.h" #include "sanitizer_file.h" +#include "sanitizer_fuchsia.h" namespace __sanitizer { @@ -30,6 +31,10 @@ void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no, const AddressInfo &info, bool vs_style, const char *strip_path_prefix, const char *strip_func_prefix) { +#if SANITIZER_FUCHSIA + // Fuchsia uses its own bespoke format to enable offline symbolization. + RenderStackFrame(buffer, frame_no, info.address); +#else if (0 == internal_strcmp(format, "DEFAULT")) format = kDefaultFormat; for (const char *p = format; *p != '\0'; p++) { @@ -116,10 +121,15 @@ Die(); } } +#endif } void RenderData(InternalScopedString *buffer, const char *format, const DataInfo *DI, const char *strip_path_prefix) { +#if SANITIZER_FUCHSIA + // Fuchsia uses its own bespoke format to enable offline symbolization. + RenderDataInfo(buffer, DI); +#else for (const char *p = format; *p != '\0'; p++) { if (*p != '%') { buffer->append("%c", *p); @@ -145,6 +155,7 @@ Die(); } } +#endif } void RenderSourceLocation(InternalScopedString *buffer, const char *file, Index: lib/sanitizer_common/sanitizer_suppressions.cc =================================================================== --- lib/sanitizer_common/sanitizer_suppressions.cc +++ lib/sanitizer_common/sanitizer_suppressions.cc @@ -51,6 +51,7 @@ if (filename[0] == '\0') return; +#if !SANITIZER_FUCHSIA // If we cannot find the file, check if its location is relative to // the location of the executable. InternalScopedString new_file_path(kMaxPathLength); @@ -59,6 +60,7 @@ new_file_path.size())) { filename = new_file_path.data(); } +#endif // !SANITIZER_FUCHSIA // Read the file. VPrintf(1, "%s: reading suppressions file at %s\n", Index: lib/sanitizer_common/sanitizer_symbolizer_fuchsia.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_symbolizer_fuchsia.cc @@ -0,0 +1,111 @@ +//===-- sanitizer_symbolizer_fuchsia.cc -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between various sanitizers' runtime libraries. +// +// Implementation of Fuchsia-specific symbolizer. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_FUCHSIA + +#include "sanitizer_fuchsia.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +// For Fuchsia we don't do any actual symbolization per se. +// Instead, we emit text containing raw addresses and raw linkage +// symbol names, embedded in Fuchsia's symbolization markup format. +// Fuchsia's logging infrastructure emits enough information about +// process memory layout that a post-processing filter can do the +// symbolization and pretty-print the markup. +// TODO(mcgrathr): URL to markup format document + +// This is used by UBSan for type names, and by ASan for global variable names. +constexpr const char *kFormatDemangle = "{{{symbol:%s}}}"; +constexpr uptr kFormatDemangleMax = 1024; // Arbitrary. + +// Function name or equivalent from PC location. +constexpr const char *kFormatFunction = "{{{pc:%p}}}"; +constexpr uptr kFormatFunctionMax = 64; // More than big enough for 64-bit hex. + +// Global variable name or equivalent from data memory address. +constexpr const char *kFormatData = "{{{data:%p}}}"; + +// One frame in a backtrace (printed on a line by itself). +constexpr const char *kFormatFrame = "{{{bt:%u:%p}}}"; + +// Just a trivial singleton. It could be static if the generic +// Symbolizer class supported linker initialization. +Symbolizer *Symbolizer::GetOrInit() { + SpinMutexLock l(&init_mu_); + if (!symbolizer_) { + symbolizer_ = new(symbolizer_allocator_) Symbolizer({}); + CHECK(symbolizer_); + } + return symbolizer_; +} + +// Nothing to do. +void Symbolizer::LateInitialize() { } + +// This is used by UBSan for type names, and by ASan for global variable names. +// It's expected to return a static buffer that will be reused on each call. +const char *Symbolizer::Demangle(const char *name) { + static char buffer[kFormatDemangleMax]; + internal_snprintf(buffer, sizeof(buffer), kFormatDemangle, name); + return buffer; +} + +// This is used mostly for suppression matching. Making it work +// would enable "interceptor_via_lib" suppressions. It's also used +// once in UBSan to say "in module ..." in a message that also +// includes an address in the module, so post-processing can already +// pretty-print that so as to indicate the module. +bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, + uptr *module_address) { + return false; +} + +// This is used in some places for suppression checking, which we +// don't really support for Fuchsia. It's also used in UBSan to +// identify a PC location to a function name, so we always fill in +// the function member with a string containing markup around the PC +// value. +// TODO(mcgrathr): Under SANITIZER_GO, it's currently used by TSan +// to render stack frames, but that should be changed to use +// RenderStackFrame. +SymbolizedStack *Symbolizer::SymbolizePC(uptr addr) { + SymbolizedStack *s = SymbolizedStack::New(addr); + char buffer[kFormatFunctionMax]; + internal_snprintf(buffer, sizeof(buffer), kFormatFunction, addr); + s->info.function = internal_strdup(buffer); + return s; +} + +// Always claim we succeeded, so that RenderDataInfo will be called. +bool Symbolizer::SymbolizeData(uptr addr, DataInfo *info) { + info->Clear(); + info->start = addr; + return true; +} + +void RenderDataInfo(InternalScopedString *buffer, const DataInfo *DI) { + buffer->append(kFormatData, DI->start); +} + +void RenderStackFrame(InternalScopedString *buffer, + unsigned int frame_no, uptr pc) { + buffer->append(kFormatFrame, frame_no, pc); +} + +} // namespace __sanitizer + +#endif // SANITIZER_FUCHSIA Index: lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc +++ lib/sanitizer_common/sanitizer_symbolizer_libcdep.cc @@ -14,6 +14,7 @@ #include "sanitizer_allocator_internal.h" #include "sanitizer_internal_defs.h" #include "sanitizer_symbolizer_internal.h" +#if !SANITIZER_FUCHSIA namespace __sanitizer { @@ -473,3 +474,5 @@ } } // namespace __sanitizer + +#endif // !SANITIZER_FUCHSIA Index: lib/sanitizer_common/sanitizer_thread_registry.h =================================================================== --- lib/sanitizer_common/sanitizer_thread_registry.h +++ lib/sanitizer_common/sanitizer_thread_registry.h @@ -115,7 +115,7 @@ void SetThreadNameByUserId(uptr user_id, const char *name); void DetachThread(u32 tid, void *arg); void JoinThread(u32 tid, void *arg); - void FinishThread(u32 tid); + void FinishThread(u32 tid, bool aborted = false); void StartThread(u32 tid, tid_t os_id, bool workerthread, void *arg); private: Index: lib/sanitizer_common/sanitizer_thread_registry.cc =================================================================== --- lib/sanitizer_common/sanitizer_thread_registry.cc +++ lib/sanitizer_common/sanitizer_thread_registry.cc @@ -204,7 +204,8 @@ CHECK_LT(tid, n_contexts_); ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); - CHECK_EQ(ThreadStatusRunning, tctx->status); + CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning, + tctx->status); tctx->SetName(name); } @@ -251,18 +252,26 @@ QuarantinePush(tctx); } -void ThreadRegistry::FinishThread(u32 tid) { +void ThreadRegistry::FinishThread(u32 tid, bool aborted) { BlockingMutexLock l(&mtx_); CHECK_GT(alive_threads_, 0); alive_threads_--; - CHECK_GT(running_threads_, 0); - running_threads_--; + if (!aborted) { + CHECK_GT(running_threads_, 0); + running_threads_--; + } CHECK_LT(tid, n_contexts_); ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); - CHECK_EQ(ThreadStatusRunning, tctx->status); + if (aborted) { + // The thread never really existed. + CHECK_EQ(ThreadStatusCreated, tctx->status); + tctx->detached = false; + } else { + CHECK_EQ(ThreadStatusRunning, tctx->status); + } tctx->SetFinished(); - if (tctx->detached) { + if (aborted || tctx->detached) { tctx->SetDead(); QuarantinePush(tctx); } Index: lib/ubsan/ubsan_platform.h =================================================================== --- lib/ubsan/ubsan_platform.h +++ lib/ubsan/ubsan_platform.h @@ -19,7 +19,7 @@ defined(__aarch64__) || defined(__mips__) || defined(__powerpc64__) || \ defined(__s390__)) # define CAN_SANITIZE_UB 1 -#elif defined(_WIN32) +#elif defined(_WIN32) || defined(__Fuchsia__) # define CAN_SANITIZE_UB 1 #else # define CAN_SANITIZE_UB 0