diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp index 2215495bc6c56..a60fafca236bd 100644 --- a/clang/lib/Driver/ToolChains/WebAssembly.cpp +++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp @@ -542,8 +542,13 @@ void WebAssembly::AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args, SanitizerMask WebAssembly::getSupportedSanitizers() const { SanitizerMask Res = ToolChain::getSupportedSanitizers(); if (getTriple().isOSEmscripten()) { - Res |= SanitizerKind::Vptr | SanitizerKind::Leak | SanitizerKind::Address; + Res |= SanitizerKind::Vptr | SanitizerKind::Leak; } + + if (getTriple().isOSEmscripten() || getTriple().isOSWASI()) { + Res |= SanitizerKind::Address; + } + // -fsanitize=function places two words before the function label, which are // -unsupported. Res &= ~SanitizerKind::Function; diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt index 6cf20ab7c183c..8b0b1b40d0b5c 100644 --- a/compiler-rt/CMakeLists.txt +++ b/compiler-rt/CMakeLists.txt @@ -458,7 +458,7 @@ else() set(SANITIZER_LIMIT_FRAME_SIZE FALSE) endif() -if(FUCHSIA OR UNIX) +if((FUCHSIA OR UNIX) AND NOT CMAKE_SYSTEM_NAME STREQUAL "WASI") set(SANITIZER_USE_SYMBOLS TRUE) else() set(SANITIZER_USE_SYMBOLS FALSE) diff --git a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake index 3323d9aae6f75..116abab469be6 100644 --- a/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake +++ b/compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake @@ -30,10 +30,10 @@ endif() set(ALL_SANITIZER_COMMON_SUPPORTED_ARCH ${X86} ${X86_64} ${PPC64} ${RISCV64} ${ARM32} ${ARM64} ${MIPS32} ${MIPS64} ${S390X} ${SPARC} ${SPARCV9} - ${HEXAGON} ${LOONGARCH64}) + ${HEXAGON} ${LOONGARCH64} ${WASM32}) set(ALL_ASAN_SUPPORTED_ARCH ${X86} ${X86_64} ${ARM32} ${ARM64} ${RISCV64} ${MIPS32} ${MIPS64} ${PPC64} ${S390X} ${SPARC} ${SPARCV9} ${HEXAGON} - ${LOONGARCH64}) + ${LOONGARCH64} ${WASM32}) set(ALL_ASAN_ABI_SUPPORTED_ARCH ${X86_64} ${ARM64} ${ARM64_32}) set(ALL_DFSAN_SUPPORTED_ARCH ${X86_64} ${MIPS64} ${ARM64} ${LOONGARCH64}) set(ALL_RTSAN_SUPPORTED_ARCH ${X86_64} ${ARM64}) diff --git a/compiler-rt/cmake/config-ix.cmake b/compiler-rt/cmake/config-ix.cmake index 2542ba6119d5f..7ee2ec04c82a5 100644 --- a/compiler-rt/cmake/config-ix.cmake +++ b/compiler-rt/cmake/config-ix.cmake @@ -740,7 +740,7 @@ set(COMPILER_RT_SANITIZERS_TO_BUILD all CACHE STRING 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|NetBSD|Fuchsia|SunOS" OR + (OS_NAME MATCHES "Android|Darwin|Linux|FreeBSD|NetBSD|Fuchsia|SunOS|WASI" OR (OS_NAME MATCHES "Windows" AND NOT CYGWIN AND (NOT MINGW OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")))) set(COMPILER_RT_HAS_SANITIZER_COMMON TRUE) diff --git a/compiler-rt/lib/asan/CMakeLists.txt b/compiler-rt/lib/asan/CMakeLists.txt index 463ea233b37aa..d6d19e762cc22 100644 --- a/compiler-rt/lib/asan/CMakeLists.txt +++ b/compiler-rt/lib/asan/CMakeLists.txt @@ -1,5 +1,12 @@ # Build for the AddressSanitizer runtime support library. +set(COMPILER_RT_ASAN_BUILD_SHARED_LIBS_default ON) +if (CMAKE_SYSTEM_NAME STREQUAL "WASI") + set(COMPILER_RT_ASAN_BUILD_SHARED_LIBS_default OFF) +endif() +option(COMPILER_RT_ASAN_BUILD_SHARED_LIBS + "Build AddressSanitizer shared libraries" ${COMPILER_RT_ASAN_BUILD_SHARED_LIBS_default}) + set(ASAN_SOURCES asan_allocator.cpp asan_activation.cpp @@ -29,10 +36,13 @@ set(ASAN_SOURCES asan_stats.cpp asan_suppressions.cpp asan_thread.cpp + asan_wasi.cpp asan_win.cpp + wasi/memset.c + wasi/memcpy.c ) -if (NOT WIN32 AND NOT APPLE) +if (NOT WIN32 AND NOT APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "WASI") list(APPEND ASAN_SOURCES asan_interceptors_vfork.S ) @@ -293,25 +303,27 @@ else() SanitizerCommonWeakInterception) endif() - add_compiler_rt_runtime(clang_rt.asan - SHARED - ARCHS ${arch} - OBJECT_LIBS ${ASAN_COMMON_RUNTIME_OBJECT_LIBS} - RTAsan_dynamic - # The only purpose of RTAsan_dynamic_version_script_dummy is to - # carry a dependency of the shared runtime on the version script. - # Replacing it with a straightforward - # add_dependencies(clang_rt.asan-dynamic-${arch} clang_rt.asan-dynamic-${arch}-version-list) - # generates an order-only dependency in ninja. - RTAsan_dynamic_version_script_dummy - RTUbsan_cxx - ${ASAN_DYNAMIC_WEAK_INTERCEPTION} - CFLAGS ${ASAN_DYNAMIC_CFLAGS} - LINK_FLAGS ${ASAN_DYNAMIC_LINK_FLAGS} - ${VERSION_SCRIPT_FLAG} - LINK_LIBS ${ASAN_DYNAMIC_LIBS} - DEFS ${ASAN_DYNAMIC_DEFINITIONS} - PARENT_TARGET asan) + if (COMPILER_RT_ASAN_BUILD_SHARED_LIBS) + add_compiler_rt_runtime(clang_rt.asan + SHARED + ARCHS ${arch} + OBJECT_LIBS ${ASAN_COMMON_RUNTIME_OBJECT_LIBS} + RTAsan_dynamic + # The only purpose of RTAsan_dynamic_version_script_dummy is to + # carry a dependency of the shared runtime on the version script. + # Replacing it with a straightforward + # add_dependencies(clang_rt.asan-dynamic-${arch} clang_rt.asan-dynamic-${arch}-version-list) + # generates an order-only dependency in ninja. + RTAsan_dynamic_version_script_dummy + RTUbsan_cxx + ${ASAN_DYNAMIC_WEAK_INTERCEPTION} + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + LINK_FLAGS ${ASAN_DYNAMIC_LINK_FLAGS} + ${VERSION_SCRIPT_FLAG} + LINK_LIBS ${ASAN_DYNAMIC_LIBS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS} + PARENT_TARGET asan) + endif() if (SANITIZER_USE_SYMBOLS AND NOT ${arch} STREQUAL "i386") add_sanitizer_rt_symbols(clang_rt.asan_cxx diff --git a/compiler-rt/lib/asan/asan_interceptors.cpp b/compiler-rt/lib/asan/asan_interceptors.cpp index 74af2e65e9bfa..19ae980a4dca7 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cpp +++ b/compiler-rt/lib/asan/asan_interceptors.cpp @@ -27,10 +27,10 @@ #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_libc.h" -// There is no general interception at all on Fuchsia. +// There is no general interception at all on Fuchsia and WASI. // Only the functions in asan_interceptors_memintrinsics.cpp are // really defined to replace libc functions. -#if !SANITIZER_FUCHSIA +#if !SANITIZER_FUCHSIA && !SANITIZER_WASI # if SANITIZER_POSIX # include "sanitizer_common/sanitizer_posix.h" diff --git a/compiler-rt/lib/asan/asan_interceptors.h b/compiler-rt/lib/asan/asan_interceptors.h index 826b45f5ada8c..4fe1371399673 100644 --- a/compiler-rt/lib/asan/asan_interceptors.h +++ b/compiler-rt/lib/asan/asan_interceptors.h @@ -33,7 +33,7 @@ void InitializePlatformInterceptors(); // Use macro to describe if specific function should be // intercepted on a given platform. -#if !SANITIZER_WINDOWS +#if !SANITIZER_WINDOWS && !SANITIZER_WASI # define ASAN_INTERCEPT__LONGJMP 1 # define ASAN_INTERCEPT_INDEX 1 # define ASAN_INTERCEPT_PTHREAD_CREATE 1 diff --git a/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp b/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp index bdf328f892063..b50845f254c76 100644 --- a/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp +++ b/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp @@ -71,7 +71,7 @@ void *__asan_memmove(void *to, const void *from, uptr size) { ASAN_MEMMOVE_IMPL(nullptr, to, from, size); } -#if SANITIZER_FUCHSIA +#if SANITIZER_FUCHSIA || SANITIZER_WASI // Fuchsia doesn't use sanitizer_common_interceptors.inc, but // the only things there it wants are these three. Just define them diff --git a/compiler-rt/lib/asan/asan_internal.h b/compiler-rt/lib/asan/asan_internal.h index 06dfc4b177339..9b1159f75e63e 100644 --- a/compiler-rt/lib/asan/asan_internal.h +++ b/compiler-rt/lib/asan/asan_internal.h @@ -35,7 +35,7 @@ // If set, values like allocator chunk size, as well as defaults for some flags // will be changed towards less memory overhead. #ifndef ASAN_LOW_MEMORY -# if SANITIZER_IOS || SANITIZER_ANDROID +# if SANITIZER_IOS || SANITIZER_ANDROID || SANITIZER_WASI # define ASAN_LOW_MEMORY 1 # else # define ASAN_LOW_MEMORY 0 diff --git a/compiler-rt/lib/asan/asan_malloc_linux.cpp b/compiler-rt/lib/asan/asan_malloc_linux.cpp index 08a63045c4e65..2e19b4a4c798a 100644 --- a/compiler-rt/lib/asan/asan_malloc_linux.cpp +++ b/compiler-rt/lib/asan/asan_malloc_linux.cpp @@ -15,7 +15,7 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_FREEBSD || SANITIZER_FUCHSIA || SANITIZER_LINUX || \ - SANITIZER_NETBSD || SANITIZER_SOLARIS + SANITIZER_NETBSD || SANITIZER_SOLARIS || SANITIZER_WASI # include "asan_allocator.h" # include "asan_interceptors.h" diff --git a/compiler-rt/lib/asan/asan_mapping.h b/compiler-rt/lib/asan/asan_mapping.h index 91fe60db6329a..40a2db52ee95a 100644 --- a/compiler-rt/lib/asan/asan_mapping.h +++ b/compiler-rt/lib/asan/asan_mapping.h @@ -272,6 +272,8 @@ extern uptr kHighMemEnd, kMidMemBeg, kMidMemEnd; // Initialized in __asan_init. # if defined(__sparc__) && SANITIZER_WORDSIZE == 64 # include "asan_mapping_sparc64.h" +# elif SANITIZER_WASI +# include "asan_mapping_wasi.h" # else # define MEM_TO_SHADOW(mem) \ (((mem) >> ASAN_SHADOW_SCALE) + (ASAN_SHADOW_OFFSET)) diff --git a/compiler-rt/lib/asan/asan_mapping_wasi.h b/compiler-rt/lib/asan/asan_mapping_wasi.h new file mode 100644 index 0000000000000..08c27a9dd9b07 --- /dev/null +++ b/compiler-rt/lib/asan/asan_mapping_wasi.h @@ -0,0 +1,86 @@ +//===-- asan_mapping_wasi.h ----------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// WASI-specific definitions for ASan memory mapping. +//===----------------------------------------------------------------------===// + +#ifndef ASAN_MAPPING_WASI_H +#define ASAN_MAPPING_WASI_H + +extern char __global_base; + +#define kLowMemBeg ((uptr) &__global_base) +#define kLowMemEnd ((kLowShadowBeg << ASAN_SHADOW_SCALE) - 1) + +#define kLowShadowBeg 0 +#define kLowShadowEnd ((uptr) &__global_base - 1) + +#define kHighMemBeg 0 + +#define kHighShadowBeg 0 +#define kHighShadowEnd 0 + +#define kMidShadowBeg 0 +#define kMidShadowEnd 0 + +#define kShadowGapBeg (kLowMemEnd + 1) +#define kShadowGapEnd 0xFFFFFFFF + +#define kShadowGap2Beg 0 +#define kShadowGap2End 0 + +#define kShadowGap3Beg 0 +#define kShadowGap3End 0 + +// The first 1/8 of the shadow memory space is shadowing itself. +// This allows attempted accesses into the shadow memory, as well as null +// pointer dereferences, to be detected properly. +// The shadow memory of the shadow memory is poisoned. +#define MEM_TO_SHADOW(mem) ((mem) >> ASAN_SHADOW_SCALE) +#define SHADOW_TO_MEM(mem) ((mem) << ASAN_SHADOW_SCALE) + +namespace __asan { + +static inline bool AddrIsInLowMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return a >= kLowMemBeg && a <= kLowMemEnd; +} + +static inline bool AddrIsInLowShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return a >= kLowShadowBeg && a <= kLowShadowEnd; +} + +static inline bool AddrIsInMidMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return false; +} + +static inline bool AddrIsInMidShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return false; +} + +static inline bool AddrIsInHighMem(uptr a) { + PROFILE_ASAN_MAPPING(); + return false; +} + +static inline bool AddrIsInHighShadow(uptr a) { + PROFILE_ASAN_MAPPING(); + return false; +} + +static inline bool AddrIsInShadowGap(uptr a) { + PROFILE_ASAN_MAPPING(); + return a >= kShadowGapBeg; +} + +} // namespace __asan + +#endif // ASAN_MAPPING_WASI_H diff --git a/compiler-rt/lib/asan/asan_poisoning.h b/compiler-rt/lib/asan/asan_poisoning.h index 600bd011f304c..6d89b73ebc6cc 100644 --- a/compiler-rt/lib/asan/asan_poisoning.h +++ b/compiler-rt/lib/asan/asan_poisoning.h @@ -51,6 +51,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, // probably provide higher-level interface for these operations. // For now, just memset on Windows. if (value || SANITIZER_WINDOWS == 1 || + SANITIZER_WASI == 1 || shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); } else { diff --git a/compiler-rt/lib/asan/asan_shadow_setup.cpp b/compiler-rt/lib/asan/asan_shadow_setup.cpp index fc6de39622b51..dee71ae44d868 100644 --- a/compiler-rt/lib/asan/asan_shadow_setup.cpp +++ b/compiler-rt/lib/asan/asan_shadow_setup.cpp @@ -14,7 +14,7 @@ #include "sanitizer_common/sanitizer_platform.h" // asan_fuchsia.cpp has their own InitializeShadowMemory implementation. -#if !SANITIZER_FUCHSIA +#if !SANITIZER_FUCHSIA && !SANITIZER_WASI # include "asan_internal.h" # include "asan_mapping.h" diff --git a/compiler-rt/lib/asan/asan_wasi.cpp b/compiler-rt/lib/asan/asan_wasi.cpp new file mode 100644 index 0000000000000..ae3472541e5e5 --- /dev/null +++ b/compiler-rt/lib/asan/asan_wasi.cpp @@ -0,0 +1,80 @@ +//===-- asan_wasi.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 a part of AddressSanitizer, an address sanity checker. +// +// WASI-specific details. +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_platform.h" +#include "sanitizer_common/sanitizer_internal_defs.h" + +#if SANITIZER_WASI + +#include "asan/asan_poisoning.h" +#include "asan_interceptors.h" +#include "asan_internal.h" + +namespace __asan { + +void InitializeShadowMemory() { + // Poison the shadow memory itself to catch invalid shadow accesses and + // also to catch null pointer dereferences. + FastPoisonShadow(kLowShadowBeg, kLowShadowEnd - kLowShadowBeg, kAsanGlobalRedzoneMagic); +} + +void InitializePlatformInterceptors() {} +void InitializePlatformExceptionHandlers() {} + +void AsanCheckDynamicRTPrereqs() {} +void AsanCheckIncompatibleRT() {} +void InitializeAsanInterceptors() {} + +void AsanOnDeadlySignal(int signo, void *siginfo, void *context) { + UNIMPLEMENTED(); +} + +bool PlatformUnpoisonStacks() { return false; } + +// Simple thread local storage implementation for WASI +static thread_local void *per_thread; + +void *AsanTSDGet() { return per_thread; } + +void AsanTSDSet(void *tsd) { per_thread = tsd; } + +void AsanTSDInit(void (*destructor)(void *tsd)) { + DCHECK(destructor == &PlatformTSDDtor); +} + +void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); } + +void AsanApplyToGlobals(globals_op_fptr op, const void *needle) { + UNIMPLEMENTED(); +} + +void InstallAtForkHandler() { + // WASI doesn't support fork +} + +void FlushUnneededASanShadowMemory(uptr p, uptr size) { + // No-op as madvise is not supported on WASI +} + +// On WASI, leak detection is not supported yet +void InstallAtExitCheckLeaks() {} + +// On WASI Preview 1, dlopen is not supported +bool HandleDlopenInit() { return false; } + +// WASI does not support ASLR +void TryReExecWithoutASLR() {} + +} // namespace __asan + +#endif // SANITIZER_WASI diff --git a/compiler-rt/lib/asan/wasi/memcpy.c b/compiler-rt/lib/asan/wasi/memcpy.c new file mode 100644 index 0000000000000..d97adf1789bf9 --- /dev/null +++ b/compiler-rt/lib/asan/wasi/memcpy.c @@ -0,0 +1,14 @@ +#if __wasi__ +#include +#include +#include + +void *__wasilibc_memcpy(void *restrict dest, const void *restrict src, size_t n) +{ + unsigned char *d = dest; + const unsigned char *s = src; + + for (; n; n--) *d++ = *s++; + return dest; +} +#endif diff --git a/compiler-rt/lib/asan/wasi/memset.c b/compiler-rt/lib/asan/wasi/memset.c new file mode 100644 index 0000000000000..9baa618c76757 --- /dev/null +++ b/compiler-rt/lib/asan/wasi/memset.c @@ -0,0 +1,94 @@ +#if defined(__wasi__) + +#include +#include + +void *__wasilibc_memset(void *dest, int c, size_t n) +{ + unsigned char *s = (unsigned char *)dest; + size_t k; + + /* Fill head and tail with minimal branching. Each + * conditional ensures that all the subsequently used + * offsets are well-defined and in the dest region. */ + + if (!n) return dest; + s[0] = c; + s[n-1] = c; + if (n <= 2) return dest; + s[1] = c; + s[2] = c; + s[n-2] = c; + s[n-3] = c; + if (n <= 6) return dest; + s[3] = c; + s[n-4] = c; + if (n <= 8) return dest; + + /* Advance pointer to align it at a 4-byte boundary, + * and truncate n to a multiple of 4. The previous code + * already took care of any head/tail that get cut off + * by the alignment. */ + + k = -(uintptr_t)s & 3; + s += k; + n -= k; + n &= -4; + +#ifdef __GNUC__ + typedef uint32_t __attribute__((__may_alias__)) u32; + typedef uint64_t __attribute__((__may_alias__)) u64; + + u32 c32 = ((u32)-1)/255 * (unsigned char)c; + + /* In preparation to copy 32 bytes at a time, aligned on + * an 8-byte bounary, fill head/tail up to 28 bytes each. + * As in the initial byte-based head/tail fill, each + * conditional below ensures that the subsequent offsets + * are valid (e.g. !(n<=24) implies n>=28). */ + + *(u32 *)(s+0) = c32; + *(u32 *)(s+n-4) = c32; + if (n <= 8) return dest; + *(u32 *)(s+4) = c32; + *(u32 *)(s+8) = c32; + *(u32 *)(s+n-12) = c32; + *(u32 *)(s+n-8) = c32; + if (n <= 24) return dest; + *(u32 *)(s+12) = c32; + *(u32 *)(s+16) = c32; + *(u32 *)(s+20) = c32; + *(u32 *)(s+24) = c32; + *(u32 *)(s+n-28) = c32; + *(u32 *)(s+n-24) = c32; + *(u32 *)(s+n-20) = c32; + *(u32 *)(s+n-16) = c32; + + /* Align to a multiple of 8 so we can fill 64 bits at a time, + * and avoid writing the same bytes twice as much as is + * practical without introducing additional branching. */ + + k = 24 + ((uintptr_t)s & 4); + s += k; + n -= k; + + /* If this loop is reached, 28 tail bytes have already been + * filled, so any remainder when n drops below 32 can be + * safely ignored. */ + + u64 c64 = c32 | ((u64)c32 << 32); + for (; n >= 32; n-=32, s+=32) { + *(u64 *)(s+0) = c64; + *(u64 *)(s+8) = c64; + *(u64 *)(s+16) = c64; + *(u64 *)(s+24) = c64; + } +#else + /* Pure C fallback with no aliasing violations. */ + for (; n; n--, s++) *s = c; +#endif + + return dest; +} + +#endif // defined(__wasi__) diff --git a/compiler-rt/lib/asan/wasi/mman.c b/compiler-rt/lib/asan/wasi/mman.c new file mode 100644 index 0000000000000..ceea0c271c5a3 --- /dev/null +++ b/compiler-rt/lib/asan/wasi/mman.c @@ -0,0 +1,157 @@ +// Userspace emulation of mmap and munmap. Restrictions apply. +// +// This is meant to be complete enough to be compatible with code that uses +// mmap for simple file I/O. It just allocates memory with malloc and reads +// and writes data with pread and pwrite. + +#define _WASI_EMULATED_MMAN +#include +#include +#include +#include +#include +#include +#include + +struct map { + int prot; + int flags; + off_t offset; + size_t length; +}; + +void *mmap(void *addr, size_t length, int prot, int flags, + int fd, off_t offset) { + // Check for unsupported flags. + if ((flags & (MAP_PRIVATE | MAP_SHARED)) == 0 || + (flags & MAP_FIXED) != 0 || +#ifdef MAP_SHARED_VALIDATE + (flags & MAP_SHARED_VALIDATE) == MAP_SHARED_VALIDATE || +#endif +#ifdef MAP_NORESERVE + (flags & MAP_NORESERVE) != 0 || +#endif +#ifdef MAP_GROWSDOWN + (flags & MAP_GROWSDOWN) != 0 || +#endif +#ifdef MAP_HUGETLB + (flags & MAP_HUGETLB) != 0 || +#endif +#ifdef MAP_FIXED_NOREPLACE + (flags & MAP_FIXED_NOREPLACE) != 0 || +#endif + 0) + { + errno = EINVAL; + return MAP_FAILED; + } + + // Check for unsupported protection requests. + if (prot == PROT_NONE || +#ifdef PROT_EXEC + (prot & PROT_EXEC) != 0 || +#endif + 0) + { + errno = EINVAL; + return MAP_FAILED; + } + + // To be consistent with POSIX. + if (length == 0) { + errno = EINVAL; + return MAP_FAILED; + } + + // Check for integer overflow. + size_t buf_len = 0; + if (__builtin_add_overflow(length, sizeof(struct map), &buf_len)) { + errno = ENOMEM; + return MAP_FAILED; + } + + // Allocate the memory. + struct map *map = (struct map *)malloc(buf_len); + if (!map) { + errno = ENOMEM; + return MAP_FAILED; + } + + // Initialize the header. + map->prot = prot; + map->flags = flags; + map->offset = offset; + map->length = length; + + // Initialize the main memory buffer, either with the contents of a file, + // or with zeros. + addr = map + 1; + if ((flags & MAP_ANON) == 0) { + char *body = (char *)addr; + while (length > 0) { + const ssize_t nread = pread(fd, body, length, offset); + if (nread < 0) { + if (errno == EINTR) + continue; + return MAP_FAILED; + } + if (nread == 0) + break; + length -= (size_t)nread; + offset += (size_t)nread; + body += (size_t)nread; + } + } else { + memset(addr, 0, length); + } + + return addr; +} + +int munmap(void *addr, size_t length) { + struct map *map = (struct map *)addr - 1; + + // We don't support partial munmapping. + if (map->length != length) { + errno = EINVAL; + return -1; + } + + // Release the memory. + free(map); + + // Success! + return 0; +} + +int mprotect(void *addr, size_t length, int prot) { + // Address must be page-aligned. + size_t begin = (size_t)addr; + if ((begin & (PAGESIZE - 1)) != 0) { + errno = EINVAL; + return -1; + } + + // Length must not be big enough to wrap around. + size_t end; + if (__builtin_add_overflow(begin, length, &end)) { + errno = ENOMEM; + return -1; + } + + // Range must be in bounds of linear memory. + size_t memory_size = __builtin_wasm_memory_size(0) * PAGESIZE; + if (end > memory_size) { + errno = ENOMEM; + return -1; + } + + // Can only protect memory as read/write (which is a no-op). + if (prot != (PROT_READ | PROT_WRITE)) { + errno = ENOTSUP; + return -1; + } + + // Success! + return 0; +} diff --git a/compiler-rt/lib/interception/interception.h b/compiler-rt/lib/interception/interception.h index 38c152952e323..e261c801c10d7 100644 --- a/compiler-rt/lib/interception/interception.h +++ b/compiler-rt/lib/interception/interception.h @@ -19,7 +19,7 @@ #if !SANITIZER_LINUX && !SANITIZER_FREEBSD && !SANITIZER_APPLE && \ !SANITIZER_NETBSD && !SANITIZER_WINDOWS && !SANITIZER_FUCHSIA && \ - !SANITIZER_SOLARIS + !SANITIZER_SOLARIS && !SANITIZER_WASI # error "Interception doesn't work on this operating system." #endif @@ -157,6 +157,10 @@ const interpose_substitution substitution_##func_name[] \ extern "C" ret_type func(__VA_ARGS__); # define DECLARE_WRAPPER_WINAPI(ret_type, func, ...) \ extern "C" __declspec(dllimport) ret_type __stdcall func(__VA_ARGS__); +#elif SANITIZER_WASI +# define WRAP(x) x +# define INTERCEPTOR_ATTRIBUTE +# define DECLARE_WRAPPER(ret_type, func, ...) #elif !SANITIZER_FUCHSIA // LINUX, FREEBSD, NETBSD, SOLARIS # define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) # if ASM_INTERCEPTOR_TRAMPOLINE_SUPPORT @@ -240,6 +244,10 @@ const interpose_substitution substitution_##func_name[] \ # define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default"))) # define REAL(x) __unsanitized_##x # define DECLARE_REAL(ret_type, func, ...) +#elif SANITIZER_WASI +# define REAL(x) __wasilibc_##x +# define DECLARE_REAL(ret_type, func, ...) \ + extern "C" ret_type REAL(func)(__VA_ARGS__); #elif !SANITIZER_APPLE # define PTR_TO_REAL(x) real_##x # define REAL(x) __interception::PTR_TO_REAL(x) @@ -278,7 +286,7 @@ const interpose_substitution substitution_##func_name[] \ // 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 !SANITIZER_APPLE && !SANITIZER_FUCHSIA +#if !SANITIZER_APPLE && !SANITIZER_FUCHSIA && !SANITIZER_WASI # define DEFINE_REAL(ret_type, func, ...) \ typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \ namespace __interception { \ @@ -365,7 +373,7 @@ inline void DoesNotSupportStaticLinking() {} #define INCLUDED_FROM_INTERCEPTION_LIB #if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \ - SANITIZER_SOLARIS + SANITIZER_SOLARIS || SANITIZER_WASI # include "interception_linux.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) diff --git a/compiler-rt/lib/interception/interception_linux.h b/compiler-rt/lib/interception/interception_linux.h index 2e01ff44578c3..6018961359e02 100644 --- a/compiler-rt/lib/interception/interception_linux.h +++ b/compiler-rt/lib/interception/interception_linux.h @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || \ - SANITIZER_SOLARIS + SANITIZER_SOLARIS || SANITIZER_WASI #if !defined(INCLUDED_FROM_INTERCEPTION_LIB) # error interception_linux.h should be included from interception library only diff --git a/compiler-rt/lib/sanitizer_common/CMakeLists.txt b/compiler-rt/lib/sanitizer_common/CMakeLists.txt index 66f2d259aa5fd..9b78713be468e 100644 --- a/compiler-rt/lib/sanitizer_common/CMakeLists.txt +++ b/compiler-rt/lib/sanitizer_common/CMakeLists.txt @@ -41,6 +41,7 @@ set(SANITIZER_SOURCES_NOTERMINATION sanitizer_thread_arg_retval.cpp sanitizer_thread_registry.cpp sanitizer_type_traits.cpp + sanitizer_wasi.cpp sanitizer_win.cpp ) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_errno_codes.h b/compiler-rt/lib/sanitizer_common/sanitizer_errno_codes.h index 3917b2817f2e9..ba34ec0502b7d 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_errno_codes.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_errno_codes.h @@ -21,11 +21,21 @@ namespace __sanitizer { -#define errno_ENOMEM 12 -#define errno_EBUSY 16 -#define errno_EINVAL 22 -#define errno_ENAMETOOLONG 36 -#define errno_ENOSYS 38 +#if defined(__wasi__) +# define errno_ENOMEM 48 +# define errno_EBUSY 10 +# define errno_EINVAL 28 +# define errno_ERANGE 68 +# define errno_ENAMETOOLONG 37 +# define errno_ENOSYS 52 +#else +# define errno_ENOMEM 12 +# define errno_EBUSY 16 +# define errno_EINVAL 22 +# define errno_ERANGE 34 +# define errno_ENAMETOOLONG 36 +# define errno_ENOSYS 38 +#endif // Those might not present or their value differ on different platforms. extern const int errno_EOWNERDEAD; diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h index eebfb00aad7ac..c64a6de34b26c 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_internal_defs.h @@ -184,7 +184,8 @@ typedef int pid_t; #if SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_APPLE || \ (SANITIZER_SOLARIS && (defined(_LP64) || _FILE_OFFSET_BITS == 64)) || \ (SANITIZER_LINUX && !SANITIZER_GLIBC && !SANITIZER_ANDROID) || \ - (SANITIZER_LINUX && (defined(__x86_64__) || defined(__hexagon__))) + (SANITIZER_LINUX && (defined(__x86_64__) || defined(__hexagon__))) || \ + SANITIZER_WASI typedef u64 OFF_T; #else typedef uptr OFF_T; @@ -400,9 +401,13 @@ void NORETURN CheckFailed(const char *file, int line, const char *cond, enum LinkerInitialized { LINKER_INITIALIZED = 0 }; #if !defined(_MSC_VER) || defined(__clang__) -# define GET_CALLER_PC() \ - ((__sanitizer::uptr)__builtin_extract_return_addr( \ - __builtin_return_address(0))) +# if __has_builtin(__builtin_return_address) +# define GET_CALLER_PC() (__sanitizer::uptr)(0) +# else +# define GET_CALLER_PC() \ + ((__sanitizer::uptr)__builtin_extract_return_addr( \ + __builtin_return_address(0))) +# endif # define GET_CURRENT_FRAME() ((__sanitizer::uptr)__builtin_frame_address(0)) inline void Trap() { __builtin_trap(); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h index 57966403c92a9..7e65e4fc2ec2c 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform.h @@ -14,7 +14,8 @@ #if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \ !defined(__APPLE__) && !defined(_WIN32) && !defined(__Fuchsia__) && \ - !(defined(__sun__) && defined(__svr4__)) + !(defined(__sun__) && defined(__svr4__)) && \ + !defined(__wasi__) # error "This operating system is not supported" #endif @@ -55,6 +56,12 @@ # define SANITIZER_SOLARIS 0 #endif +#if defined(__wasi__) +# define SANITIZER_WASI 1 +#else +# define SANITIZER_WASI 0 +#endif + // - SANITIZER_APPLE: all Apple code // - TARGET_OS_OSX: macOS // - SANITIZER_IOS: devices (iOS and iOS-like) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h index 05cd2d71cbbae..fd9d3bd3e09b2 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -29,6 +29,12 @@ #define SI_WINDOWS 1 #endif +#if !SANITIZER_WASI +#define SI_WASI 0 +#else +#define SI_WASI 1 +#endif + #if SI_WINDOWS && SI_POSIX #error "Windows is not POSIX!" #endif @@ -145,7 +151,7 @@ #define SANITIZER_INTERCEPT_STRCASESTR SI_POSIX #define SANITIZER_INTERCEPT_STRTOK SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_STRCHR SI_NOT_FUCHSIA -#define SANITIZER_INTERCEPT_STRCHRNUL SI_POSIX_NOT_MAC +#define SANITIZER_INTERCEPT_STRCHRNUL (SI_POSIX_NOT_MAC || SI_WASI) #define SANITIZER_INTERCEPT_STRRCHR SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_STRSPN SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_STRPBRK SI_NOT_FUCHSIA @@ -500,7 +506,7 @@ #define SANITIZER_INTERCEPT_WCSLEN 1 #define SANITIZER_INTERCEPT_WCSCAT SI_POSIX #define SANITIZER_INTERCEPT_WCSDUP SI_POSIX -#define SANITIZER_INTERCEPT_SIGNAL_AND_SIGACTION (!SI_WINDOWS && SI_NOT_FUCHSIA) +#define SANITIZER_INTERCEPT_SIGNAL_AND_SIGACTION (!SI_WINDOWS && SI_NOT_FUCHSIA && !SI_WASI) #define SANITIZER_INTERCEPT_BSD_SIGNAL SI_ANDROID #define SANITIZER_INTERCEPT_ACCT (SI_NETBSD || SI_FREEBSD) diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_redefine_builtins.h b/compiler-rt/lib/sanitizer_common/sanitizer_redefine_builtins.h index d24b179ef320c..e29bb9f93c51d 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_redefine_builtins.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_redefine_builtins.h @@ -15,7 +15,7 @@ # define SANITIZER_REDEFINE_BUILTINS_H // The asm hack only works with GCC and Clang. -# if !defined(_WIN32) +# if !defined(_WIN32) && !defined(__wasi__) asm("memcpy = __sanitizer_internal_memcpy"); asm("memmove = __sanitizer_internal_memmove"); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_wasi.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_wasi.cpp new file mode 100644 index 0000000000000..132a07adcec91 --- /dev/null +++ b/compiler-rt/lib/sanitizer_common/sanitizer_wasi.cpp @@ -0,0 +1,444 @@ +//===-- sanitizer_wasi.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 ThreadSanitizer +// run-time libraries and implements WASI-specific functions from +// sanitizer_posix.h. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" + +#if SANITIZER_WASI + +# include +# include +# include +# include +# include +# include +# include + +# include "sanitizer_common.h" +# include "sanitizer_libc.h" +# include "sanitizer_file.h" +# include "sanitizer_symbolizer.h" +# include "sanitizer_atomic.h" +# include "sanitizer_mutex.h" +# include "sanitizer_stacktrace.h" +# include "sanitizer_symbolizer_internal.h" + +namespace __sanitizer { + +extern "C" void *__libc_malloc(uptr); +extern "C" void __libc_free(void *); + +void InitializePlatformEarly() {} +void InitTlsSize() {} + +const char *GetEnv(const char *name) { return nullptr; } + +uptr GetPageSize() { return PAGESIZE; } + +class EmulatedMmap { + + struct MmapEntry { + void *addr; + void *addr_to_free; + uptr size; + const char *mem_type; + MmapEntry *next; + } __attribute__((aligned(1))); + + SpinMutex head_lock_; + MmapEntry *head_ SANITIZER_GUARDED_BY(head_lock_); + +public: + EmulatedMmap() : head_lock_(), head_(nullptr) {} + + bool Unmap(void *addr, uptr size) { + SpinMutexLock l(&head_lock_); + MmapEntry *entry = head_; + MmapEntry *prev = nullptr; + while (entry) { + if (entry->addr == addr) { + // Remove the entry from the linked list + if (prev) { + prev->next = entry->next; + } else { + head_ = entry->next; + } + // Free the memory allocated for the entry + __libc_free(entry->addr_to_free); + return true; + } + prev = entry; + entry = entry->next; + } + return false; + } + + void *Mmap(uptr size, const char *mem_type, uptr alignment = GetPageSize()) { + uptr mmap_size = size + alignment; + uptr malloc_size = mmap_size + sizeof(MmapEntry); + void *ptr = __libc_malloc(malloc_size); + if (!ptr) { + return nullptr; + } + MmapEntry *entry = (MmapEntry *)(((uptr)ptr) + mmap_size); + entry->addr = (void *)RoundUpTo((uptr)ptr, alignment); + entry->addr_to_free = ptr; + entry->size = size; + entry->mem_type = mem_type; + { + // Add the entry to the linked list + SpinMutexLock l(&head_lock_); + entry->next = head_; + head_ = entry; + } + return entry->addr; + } + + void *MmapAligned(uptr size, const char *mem_type, uptr alignment) { + uptr mmap_size = size + alignment; + uptr malloc_size = mmap_size + sizeof(MmapEntry); + void *ptr = __libc_malloc(malloc_size); + if (!ptr) { + return nullptr; + } + MmapEntry *entry = (MmapEntry *)(((uptr)ptr) + mmap_size); + entry->addr = (void *)RoundUpTo((uptr)ptr, alignment); + entry->addr_to_free = ptr; + entry->size = size; + entry->mem_type = mem_type; + { + // Add the entry to the linked list + SpinMutexLock l(&head_lock_); + entry->next = head_; + head_ = entry; + } + return entry->addr; + } +}; + +static EmulatedMmap emulated_mmap; + +void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) { + void *ptr = emulated_mmap.Mmap(size, mem_type); + if (!ptr) { + if (raw_report) { + Report("MmapOrDie: failed to allocate %u bytes\n", size); + } + Die(); + } + return ptr; +} + +void UnmapOrDie(void *addr, uptr size, bool raw_report) { + if (!addr || !size) return; + if (!emulated_mmap.Unmap(addr, size)) { + if (raw_report) { + Report("UnmapOrDie: failed to unmap %u bytes at %p\n", size, addr); + } + Die(); + } +} + +void *MmapNoReserveOrDie(uptr size, const char *mem_type) { + return emulated_mmap.Mmap(size, mem_type); +} + +void DumpProcessMap() { + return; +} + +// Implement mandatory functions for ASan WASI build +void CheckASLR() {} +void PlatformPrepareForSandboxing(void *args) {} +void DisableCoreDumperIfNecessary() {} +void InstallDeadlySignalHandlers(void (*cb)(int, void *, void *)) {} +int Atexit(void (*function)(void)) { return 0; } +uptr GetMaxUserVirtualAddress() { return (1ULL << 30); } // 1GB for WASI +uptr GetMmapGranularity() { return GetPageSize(); } +uptr internal_sched_yield() { return 0; } +void internal_join_thread(void *th) {} +bool MemoryRangeIsAvailable(uptr beg, uptr size) { return true; } +void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, + uptr *tls_addr, uptr *tls_size) { + *stk_addr = 0; + *stk_size = 0; + *tls_addr = 0; + *tls_size = 0; +} + +uptr internal_getpid() { return 1; } +void SetAlternateSignalStack() {} +uptr GetThreadSelf() { return 0; } + +void Symbolizer::LateInitialize() { + Symbolizer::GetOrInit(); +} + +// Additional mandatory functions +void *MmapOrDieOnFatalError(uptr size, const char *mem_type) { + void *ptr = emulated_mmap.Mmap(size, mem_type); + if (!ptr) { + Report("MmapOrDieOnFatalError: failed to allocate %u bytes\n", size); + Die(); + } + IncreaseTotalMmap(size); + return ptr; +} +void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, const char *mem_type) { + CHECK(IsPowerOfTwo(size)); + CHECK(IsPowerOfTwo(alignment)); + uptr map_res = (uptr)emulated_mmap.MmapAligned(size, mem_type, alignment); + if (UNLIKELY(!map_res)) + return nullptr; + return (void*)map_res; +} + +uptr ReadLongProcessName(char *buf, uptr buf_len) { + internal_strncpy(buf, "wasi-process", buf_len); + return internal_strlen("wasi-process"); +} + +uptr ReadBinaryName(char *buf, uptr buf_len) { + internal_strncpy(buf, "wasi-binary", buf_len); + return internal_strlen("wasi-binary"); +} + +char **GetArgv() { return nullptr; } + +void internal_usleep(u64 useconds) { } + +bool WriteToFile(int fd, const void *buf, uptr count, uptr *written, int *errno_p) { + __wasi_ciovec_t iov; + iov.buf = static_cast(buf); + iov.buf_len = count; + + __wasi_size_t wasi_written; + __wasi_errno_t error = __wasi_fd_write(fd, &iov, 1, &wasi_written); + + if (written) + *written = wasi_written; + if (errno_p) + *errno_p = error; + + return error == 0; +} + +void CloseFile(fd_t fd) { + __wasi_errno_t error = __wasi_fd_close(fd); + (void)error; +} + +int OpenFile(const char *filename, FileAccessMode mode, int *errno_p) { + if (errno_p) *errno_p = 0; + return -1; +} + +bool IsPathSeparator(char c) { + return c == '/'; +} + +bool DirExists(const char *path) { + return false; +} + +// More mandatory functions +bool ReadFromFile(int fd, void *buff, uptr buff_size, uptr *bytes_read, int *error_p) { + __wasi_iovec_t iov; + iov.buf = static_cast(buff); + iov.buf_len = buff_size; + + __wasi_size_t wasi_bytes_read; + __wasi_errno_t error = __wasi_fd_read(fd, &iov, 1, &wasi_bytes_read); + + if (bytes_read) + *bytes_read = wasi_bytes_read; + if (error_p) + *error_p = error; + + return error == 0; +} + +bool CreateDir(const char *path) { + return false; +} + +void InitializePlatformCommonFlags(CommonFlags *cf) {} + +void FutexWait(atomic_uint32_t *p, u32 cmp) {} + +void FutexWake(atomic_uint32_t *p, u32 count) {} + +bool FileExists(const char *filename) { + return false; +} + +bool IsAbsolutePath(const char *path) { + return path && path[0] == '/'; +} + +void Abort() { + __builtin_unreachable(); +} + +void NORETURN internal__exit(int exitcode) { + __wasi_proc_exit(exitcode); +} + +tid_t GetTid() { + return 0; +} + +bool MmapFixedSuperNoReserve(uptr fixed_addr, uptr size, const char *name) { + __builtin_unreachable(); + return false; +} + +bool DontDumpShadowMemory(uptr addr, uptr length) { + return false; +} + +bool MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { + __builtin_unreachable(); + return false; +} + +void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) { + __builtin_unreachable(); + return nullptr; +} + +bool MprotectReadOnly(uptr addr, uptr size) { + return false; +} + +void *internal_start_thread(void *(*func)(void *arg), void *arg) { + return nullptr; +} + +u64 MonotonicNanoTime() { + return 0; +} + +// Additional functions needed for symbolizer + +// These functions need to be properly defined +#if SANITIZER_WASI +void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) { + size = 0; +} + +void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { + size = 0; +} + + +class WASISymbolizerTool final : public SymbolizerTool { + public: + bool SymbolizePC(uptr addr, SymbolizedStack *stack) override; + bool SymbolizeData(uptr addr, DataInfo *info) override { + return false; + } + const char *Demangle(const char *name) override { + return name; + } +}; + +bool WASISymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) { + return false; +} + +static void ChooseSymbolizerTools(IntrusiveList *list, + LowLevelAllocator *allocator) { + if (!common_flags()->symbolize) { + VReport(2, "Symbolizer is disabled.\n"); + return; + } + + list->push_back(new(*allocator) WASISymbolizerTool()); +} + + +Symbolizer *Symbolizer::PlatformInit() { + IntrusiveList list; + list.clear(); + ChooseSymbolizerTools(&list, &symbolizer_allocator_); + + return new(symbolizer_allocator_) Symbolizer(list); +} + +void ListOfModules::init() { + modules_.Initialize(2); + + char name[] = "wasi-binary"; + + LoadedModule main_module; + main_module.set(name, 0); + + // Emscripten represents program counters as offsets into WebAssembly + // modules. For JavaScript code, the "program counter" is the line number + // of the JavaScript code with the high bit set. + // Therefore, PC values 0x80000000 and beyond represents JavaScript code. + // As a result, 0x00000000 to 0x7FFFFFFF represents PC values for WASM code. + // We consider WASM code as main_module. + main_module.addAddressRange(0, 0x7FFFFFFF, /*executable*/ true, + /*writable*/ false); + modules_.push_back(main_module); +} + +void ListOfModules::fallbackInit() {} + +const char *Symbolizer::PlatformDemangle(const char *name) { + return name; +} + +bool SupportsColoredOutput(fd_t fd) { + __wasi_fdstat_t statbuf; + int err = __wasi_fd_fdstat_get(fd, &statbuf); + if (err != 0) { + errno = err; + return 0; + } + return statbuf.fs_filetype == __WASI_FILETYPE_CHARACTER_DEVICE; +} + +bool SignalContext::IsStackOverflow() const { + return false; +} + +const char *SignalContext::Describe() const { + return "WASI signal"; +} + +bool IsAccessibleMemoryRange(uptr addr, uptr size) { + return false; +} + +void SignalContext::DumpAllRegisters(void *context) {} +#endif // SANITIZER_WASI + +} // namespace __sanitizer + +namespace __sanitizer { +// ReportFile implementation for WASI +void ReportFile::Write(const char *buffer, uptr length) { + // Use stderr for error reporting (fd 2 in WASI) + __wasi_ciovec_t iov; + iov.buf = (const uint8_t*)buffer; + iov.buf_len = length; + + __wasi_size_t written; + __wasi_errno_t error = __wasi_fd_write(2, &iov, 1, &written); + (void)error; +} +} + +#endif diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index af38057db9d71..3e31398d366ce 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -118,7 +118,7 @@ static const uint64_t kNetBSD_ShadowOffset64 = 1ULL << 46; static const uint64_t kNetBSDKasan_ShadowOffset64 = 0xdfff900000000000; static const uint64_t kPS_ShadowOffset64 = 1ULL << 40; static const uint64_t kWindowsShadowOffset32 = 3ULL << 28; -static const uint64_t kEmscriptenShadowOffset = 0; +static const uint64_t kWebAssemblyShadowOffset = 0; // The shadow memory space is dynamically allocated. static const uint64_t kWindowsShadowOffset64 = kDynamicShadowSentinel; @@ -499,8 +499,8 @@ static ShadowMapping getShadowMapping(const Triple &TargetTriple, int LongSize, bool IsRISCV64 = TargetTriple.getArch() == Triple::riscv64; bool IsWindows = TargetTriple.isOSWindows(); bool IsFuchsia = TargetTriple.isOSFuchsia(); - bool IsEmscripten = TargetTriple.isOSEmscripten(); bool IsAMDGPU = TargetTriple.isAMDGPU(); + bool IsWasm = TargetTriple.isWasm(); ShadowMapping Mapping; @@ -524,8 +524,8 @@ static ShadowMapping getShadowMapping(const Triple &TargetTriple, int LongSize, Mapping.Offset = kDynamicShadowSentinel; else if (IsWindows) Mapping.Offset = kWindowsShadowOffset32; - else if (IsEmscripten) - Mapping.Offset = kEmscriptenShadowOffset; + else if (IsWasm) + Mapping.Offset = kWebAssemblyShadowOffset; else Mapping.Offset = kDefaultShadowOffset32; } else { // LongSize == 64