Skip to content

[DNM] Address Sanitizer for Wasm/WASI #10673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 10 commits into
base: stable/20240723
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion clang/lib/Driver/ToolChains/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ void wasm::Linker::ConstructJob(Compilation &C, const JobAction &JA,
}

ToolChain.addProfileRTLibs(Args, CmdArgs);
// Emscripten has its own libasan_rt.a but we use libclang_rt.asan-wasm32.a
// for other platforms like WASI.
if (!ToolChain.getTriple().isOSEmscripten()) {
addSanitizerRuntimes(ToolChain, Args, CmdArgs);
}

CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
Expand Down Expand Up @@ -542,8 +547,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;
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/cmake/Modules/AllSupportedArchDefs.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/cmake/config-ix.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
52 changes: 32 additions & 20 deletions compiler-rt/lib/asan/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions compiler-rt/lib/asan/asan_interceptors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/asan/asan_interceptors.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/asan/asan_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/asan/asan_malloc_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/asan/asan_mapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
86 changes: 86 additions & 0 deletions compiler-rt/lib/asan/asan_mapping_wasi.h
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions compiler-rt/lib/asan/asan_poisoning.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/asan/asan_shadow_setup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
80 changes: 80 additions & 0 deletions compiler-rt/lib/asan/asan_wasi.cpp
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions compiler-rt/lib/asan/wasi/memcpy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#if __wasi__
#include <string.h>
#include <stdint.h>
#include <endian.h>

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
Loading