Skip to content

Commit

Permalink
add ThreadSanitizer support (JuliaLang#27167)
Browse files Browse the repository at this point in the history
- enables building with TSAN for the runtime library as well as Julia code
- updates the handling of `SANITIZE=1` in Make.inc
- moves sanitizer to late in the pipeline, copies what Clang does
- cleans up `options.h`, and `julia_internal.h` w.r.t sanitizers
- update devdocs for sanitizer
- adds a patch for TSAN to deal with Julia's usage of address spaces
- don't use COPY_STACKS with TSAN
- don't use DEEPBIND by default if a sanitizer is enabled
  • Loading branch information
vchuravy authored Aug 5, 2020
1 parent c342ef8 commit d862442
Show file tree
Hide file tree
Showing 14 changed files with 145 additions and 44 deletions.
26 changes: 18 additions & 8 deletions Make.inc
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ ifeq ($(USE_LIBCPP),1)
$(error USE_LIBCPP only supported with clang. Try setting USE_LIBCPP=0)
endif
ifeq ($(SANITIZE),1)
$(error Address Sanitizer only supported with clang. Try setting SANITIZE=0)
$(error Sanitizers are only supported with clang. Try setting SANITIZE=0)
endif
CC := $(CROSS_COMPILE)gcc
CXX := $(CROSS_COMPILE)g++
Expand Down Expand Up @@ -539,7 +539,7 @@ ifeq ($(USE_LIBCPP),1)
$(error USE_LIBCPP only supported with clang. Try setting USE_LIBCPP=0)
endif
ifeq ($(SANITIZE),1)
$(error Address Sanitizer only supported with clang. Try setting SANITIZE=0)
$(error Sanitizers only supported with clang. Try setting SANITIZE=0)
endif
CC := icc
CXX := icpc
Expand Down Expand Up @@ -670,17 +670,27 @@ endif
endif

ifeq ($(SANITIZE),1)
SANITIZE_OPTS :=
SANITIZE_LDFLAGS :=
ifeq ($(SANITIZE_MEMORY),1)
SANITIZE_OPTS := -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer
SANITIZE_LDFLAGS := $(SANITIZE_OPTS)
else
SANITIZE_OPTS := -fsanitize=address -mllvm -asan-stack=0
SANITIZE_LDFLAGS := -fsanitize=address
SANITIZE_OPTS += -fsanitize=memory -fsanitize-memory-track-origins -fno-omit-frame-pointer
SANITIZE_LDFLAGS += $(SANITIZE_OPTS)
endif
ifeq ($(SANITIZE_ADDRESS),1)
SANITIZE_OPTS += -fsanitize=address -mllvm -asan-stack=0
SANITIZE_LDFLAGS += -fsanitize=address
endif
ifeq ($(SANITIZE_THREAD),1)
SANITIZE_OPTS += -fsanitize=thread
SANITIZE_LDFLAGS += -fsanitize=thread
endif
ifeq ($(SANITIZE_OPTS),)
$(error SANITIZE=1, but no sanitizer selected, set either SANITIZE_MEMORY, SANITIZE_THREAD, or SANITIZE_ADDRESS)
endif
JCXXFLAGS += $(SANITIZE_OPTS)
JCFLAGS += $(SANITIZE_OPTS)
JLDFLAGS += $(SANITIZE_LDFLAGS)
endif
endif # SANITIZE

TAR := $(shell which gtar 2>/dev/null || which tar 2>/dev/null)
TAR_TEST := $(shell $(TAR) --help 2>&1 | egrep 'bsdtar|strip-components')
Expand Down
3 changes: 3 additions & 0 deletions deps/llvm.mk
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ $(eval $(call LLVM_PATCH,llvm-8.0-D66657-codegen-degenerate)) # remove for 10.0
$(eval $(call LLVM_PATCH,llvm-8.0-D71495-vectorize-freduce)) # remove for 10.0
$(eval $(call LLVM_PATCH,llvm-8.0-D75072-SCEV-add-type))
$(eval $(call LLVM_PATCH,llvm-8.0-D65174-limit-merge-stores)) # remove for 10.0
$(eval $(call LLVM_PATCH,llvm-julia-tsan-custom-as))
endif # LLVM_VER 8.0

ifeq ($(LLVM_VER_SHORT),9.0)
Expand All @@ -427,6 +428,7 @@ $(eval $(call LLVM_PATCH,llvm-D75072-SCEV-add-type))
$(eval $(call LLVM_PATCH,llvm-9.0-D65174-limit-merge-stores)) # remove for 10.0
$(eval $(call LLVM_PATCH,llvm9-D71443-PPC-MC-redef-symbol)) # remove for 10.0
$(eval $(call LLVM_PATCH,llvm-9.0-D78196)) # remove for 11.0
$(eval $(call LLVM_PATCH,llvm-julia-tsan-custom-as))
endif # LLVM_VER 9.0

ifeq ($(LLVM_VER_SHORT),10.0)
Expand All @@ -441,6 +443,7 @@ $(eval $(call LLVM_PATCH,llvm7-revert-D44485))
$(eval $(call LLVM_PATCH,llvm-D75072-SCEV-add-type))
$(eval $(call LLVM_PATCH,llvm-10.0-PPC_SELECT_CC)) # delete for LLVM 11
$(eval $(call LLVM_PATCH,llvm-10.0-PPC-LI-Elimination)) # delete for LLVM 11
$(eval $(call LLVM_PATCH,llvm-julia-tsan-custom-as))
endif # LLVM_VER 10.0

# Add a JL prefix to the version map. DO NOT REMOVE
Expand Down
28 changes: 28 additions & 0 deletions deps/patches/llvm-julia-tsan-custom-as.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
From bd41be423127b8946daea805290ad2eb19e66be4 Mon Sep 17 00:00:00 2001
From: Valentin Churavy <[email protected]>
Date: Sat, 19 May 2018 11:56:55 -0400
Subject: [PATCH] [TSAN] Allow for custom address spaces

Julia uses addressspaces for GC and we want these to be sanitized as well.
---
lib/Transforms/Instrumentation/ThreadSanitizer.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
index ec6904486e1..9d673353f43 100644
--- a/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
+++ b/lib/Transforms/Instrumentation/ThreadSanitizer.cpp
@@ -296,7 +296,9 @@ static bool shouldInstrumentReadWriteFromAddress(const Module *M, Value *Addr) {
// with them.
if (Addr) {
Type *PtrTy = cast<PointerType>(Addr->getType()->getScalarType());
- if (PtrTy->getPointerAddressSpace() != 0)
+ auto AS = PtrTy->getPointerAddressSpace();
+ // Allow for custom addresspaces
+ if (AS != 0 && AS < 10)
return false;
}

--
2.17.0

10 changes: 9 additions & 1 deletion doc/src/devdocs/sanitizers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ An easy solution is to have an dedicated build folder for providing a matching t
with `BUILD_LLVM_CLANG=1`. You can then refer to this toolchain from another build
folder by specifying `USECLANG=1` while overriding the `CC` and `CXX` variables.

To use one of of the sanitizers set `SANITIZE=1` and then the appropriate flag for the sanitizer you
want to use.

## Address Sanitizer (ASAN)

For detecting or debugging memory bugs, you can use Clang's [address sanitizer (ASAN)](https://clang.llvm.org/docs/AddressSanitizer.html).
By compiling with `SANITIZE=1` you enable ASAN for the Julia compiler and its generated code.
By compiling with `SANITIZE_ADDRESS=1` you enable ASAN for the Julia compiler and its generated code.
In addition, you can specify `LLVM_SANITIZE=1` to sanitize the LLVM library as well. Note that
these options incur a high performance and memory cost. For example, using ASAN for Julia and
LLVM makes `testall1` takes 8-10 times as long while using 20 times as much memory (this can be
Expand All @@ -31,3 +34,8 @@ the future.

For detecting use of uninitialized memory, you can use Clang's [memory sanitizer (MSAN)](https://clang.llvm.org/docs/MemorySanitizer.html)
by compiling with `SANITIZE_MEMORY=1`.

## Thread Sanitizer (TSAN)

For debugging data-races and other threading related issues you can use Clang's [thread sanitizer (TSAN)](https://clang.llvm.org/docs/ThreadSanitizer.html)
by compiling with `SANITIZE_THREAD=1`.
11 changes: 10 additions & 1 deletion src/aotcompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include "llvm-version.h"
#include "platform.h"
#include "options.h"

// target support
#include <llvm/ADT/Triple.h>
Expand All @@ -24,6 +23,9 @@
#if defined(JL_ASAN_ENABLED)
#include <llvm/Transforms/Instrumentation/AddressSanitizer.h>
#endif
#if defined(JL_TSAN_ENABLED)
#include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
#endif
#include <llvm/Transforms/Scalar/GVN.h>
#include <llvm/Transforms/IPO/AlwaysInliner.h>
#include <llvm/Transforms/InstCombine/InstCombine.h>
Expand Down Expand Up @@ -624,6 +626,7 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
}
PM->add(createMemCpyOptPass());
PM->add(createAlwaysInlinerLegacyPass()); // Respect always_inline
PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "loopinfo" as LLVM parallel loop
if (lower_intrinsics) {
PM->add(createBarrierNoopPass());
PM->add(createLowerExcHandlersPass());
Expand All @@ -641,6 +644,9 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
#endif
#if defined(JL_MSAN_ENABLED)
PM->add(createMemorySanitizerPass(true));
#endif
#if defined(JL_TSAN_ENABLED)
PM->add(createThreadSanitizerLegacyPassPass());
#endif
return;
}
Expand Down Expand Up @@ -766,6 +772,9 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
#if defined(JL_MSAN_ENABLED)
PM->add(createMemorySanitizerPass(true));
#endif
#if defined(JL_TSAN_ENABLED)
PM->add(createThreadSanitizerLegacyPassPass());
#endif
}

// An LLVM module pass that just runs all julia passes in order. Useful for
Expand Down
1 change: 0 additions & 1 deletion src/cgmemmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include "llvm-version.h"
#include "platform.h"
#include "options.h"

#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include "julia.h"
Expand Down
7 changes: 6 additions & 1 deletion src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

#include "llvm-version.h"
#include "platform.h"
#include "options.h"
#if defined(_OS_WINDOWS_)
// use ELF because RuntimeDyld COFF i686 support didn't exist
// use ELF because RuntimeDyld COFF X86_64 doesn't seem to work (fails to generate function pointers)?
Expand Down Expand Up @@ -5787,6 +5786,12 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
f->addFnAttr(Attribute::StackProtectStrong);
#endif

#ifdef JL_TSAN_ENABLED
// TODO: enable this only when a argument like `-race` is passed to Julia
// add a macro for no_sanitize_thread
f->addFnAttr(llvm::Attribute::SanitizeThread);
#endif

// add the optimization level specified for this module, if any
int optlevel = jl_get_module_optlevel(ctx.module);
if (optlevel >= 0 && optlevel <= 3) {
Expand Down
2 changes: 1 addition & 1 deletion src/dlload.c
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ JL_DLLEXPORT void *jl_dlopen(const char *filename, unsigned flags)
#ifdef RTLD_NOLOAD
| JL_RTLD(flags, NOLOAD)
#endif
#if defined(RTLD_DEEPBIND) && !defined(JL_ASAN_ENABLED)
#if defined(RTLD_DEEPBIND) && !(defined(JL_ASAN_ENABLED) || defined(JL_TSAN_ENABLED) || defined(JL_MSAN_ENABLED))
| JL_RTLD(flags, DEEPBIND)
#endif
#ifdef RTLD_FIRST
Expand Down
1 change: 0 additions & 1 deletion src/jitlayers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include "llvm-version.h"
#include "platform.h"
#include "options.h"

#include <llvm/Transforms/Utils/Cloning.h>
#include <llvm/Transforms/Utils/ModuleUtils.h>
Expand Down
2 changes: 2 additions & 0 deletions src/julia.expmap
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
global:
__asan*;
__tsan*;
pthread*;
__stack_chk_guard;
asprintf;
bitvector_*;
Expand Down
21 changes: 21 additions & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,23 @@
# define JL_THREAD_LOCAL
#endif

// Duplicated from options.h
#if defined(__has_feature) // Clang flavor
#if __has_feature(address_sanitizer)
#define JL_ASAN_ENABLED
#endif
#if __has_feature(memory_sanitizer)
#define JL_MSAN_ENABLED
#endif
#if __has_feature(thread_sanitizer)
#define JL_TSAN_ENABLED
#endif
#else // GCC flavor
#if defined(__SANITIZE_ADDRESS__)
#define JL_ASAN_ENABLED
#endif
#endif // __has_feature

#define container_of(ptr, type, member) \
((type *) ((char *)(ptr) - offsetof(type, member)))

Expand Down Expand Up @@ -1788,6 +1805,10 @@ typedef struct _jl_task_t {
unsigned int copy_stack:31; // sizeof stack for copybuf
unsigned int started:1;

#if defined(JL_TSAN_ENABLED)
void *tsan_state;
#endif

// current exception handler
jl_handler_t *eh;
// saved gc stack top for context switches
Expand Down
26 changes: 9 additions & 17 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,20 @@
#define sleep(x) Sleep(1000*x)
#endif

#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#define JL_ASAN_ENABLED // Clang flavor
#endif
#elif defined(__SANITIZE_ADDRESS__)
#define JL_ASAN_ENABLED // GCC flavor
#endif

#ifdef JL_ASAN_ENABLED
#ifdef __cplusplus
extern "C" {
#endif
#ifdef JL_ASAN_ENABLED
void __sanitizer_start_switch_fiber(void**, const void*, size_t);
void __sanitizer_finish_switch_fiber(void*, const void**, size_t*);
#ifdef __cplusplus
}
#endif
#endif

#if defined(__has_feature)
#if __has_feature(memory_sanitizer)
#define JL_MSAN_ENABLED
#ifdef JL_TSAN_ENABLED
void *__tsan_create_fiber(unsigned flags);
// void __tsan_destroy_fiber(void *fiber);
void __tsan_switch_to_fiber(void *fiber, unsigned flags);
#endif
#ifdef __cplusplus
}
#endif

// Remove when C11 is required for C code.
Expand Down Expand Up @@ -321,7 +312,8 @@ jl_value_t *jl_permbox32(jl_datatype_t *t, int32_t x);
jl_value_t *jl_permbox64(jl_datatype_t *t, int64_t x);
jl_svec_t *jl_perm_symsvec(size_t n, ...);

#if !defined(__clang_analyzer__) && !defined(JL_ASAN_ENABLED) // this sizeof(__VA_ARGS__) trick can't be computed until C11, but that only matters to Clang in some situations
// this sizeof(__VA_ARGS__) trick can't be computed until C11, but that only matters to Clang in some situations
#if !defined(__clang_analyzer__) && !(defined(JL_ASAN_ENABLED) || defined(JL_TSAN_ENABLED))
#ifdef __GNUC__
#define jl_perm_symsvec(n, ...) \
(jl_perm_symsvec)(__extension__({ \
Expand Down
19 changes: 7 additions & 12 deletions src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,8 @@

// sanitizer defaults ---------------------------------------------------------

// XXX: these macros are duplicated from julia_internal.h
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#define JL_ASAN_ENABLED
#endif
#elif defined(__SANITIZE_ADDRESS__)
#define JL_ASAN_ENABLED
#endif
#if defined(__has_feature)
#if __has_feature(memory_sanitizer)
#define JL_MSAN_ENABLED
#endif
#ifndef JULIA_H
#error "Must be included after julia.h"
#endif

// Automatically enable MEMDEBUG and KEEP_BODIES for the sanitizers
Expand All @@ -174,6 +164,11 @@
#define KEEP_BODIES
#endif

// TSAN doesn't like COPY_STACKS
#if defined(JL_TSAN_ENABLED) && defined(COPY_STACKS)
#undef COPY_STACKS
#endif

// Memory sanitizer needs TLS, which llvm only supports for the small memory model
#if defined(JL_MSAN_ENABLED)
// todo: fix the llvm MemoryManager to work with small memory model
Expand Down
Loading

0 comments on commit d862442

Please sign in to comment.