Skip to content

Commit

Permalink
ASAN improvements (#41675)
Browse files Browse the repository at this point in the history
* Move sanitizer compiler definitions to platform.h.

* Define ASAN defaults in the loader executable.

* Default to no RTLD_DEEPBIND for LBT when using ASAN.

* Simplify contrib/asan/

Co-authored-by: Takafumi Arakaki <aka.tkf@gmail.com>
  • Loading branch information
maleadt and tkf authored Jul 27, 2021
1 parent 5f4c719 commit 8ece865
Show file tree
Hide file tree
Showing 18 changed files with 70 additions and 75 deletions.
16 changes: 16 additions & 0 deletions cli/loader_exe.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ extern "C" {

JULIA_DEFINE_FAST_TLS

#ifdef _COMPILER_ASAN_ENABLED_
JL_DLLEXPORT const char* __asan_default_options()
{
return "allow_user_segv_handler=1:detect_leaks=0";
// FIXME: enable LSAN after fixing leaks & defining __lsan_default_suppressions(),
// or defining __lsan_default_options = exitcode=0 once publicly available
// (here and in flisp/flmain.c)
}
#endif

#ifdef _OS_WINDOWS_
int mainCRTStartup(void)
{
Expand All @@ -25,6 +35,12 @@ int main(int argc, char * argv[])
{
#endif

#ifdef _COMPILER_ASAN_ENABLED_
// ASAN does not support RTLD_DEEPBIND
// https://github.com/google/sanitizers/issues/611
putenv("LBT_USE_RTLD_DEEPBIND=0");
#endif

// Convert Windows wchar_t values to UTF8
#ifdef _OS_WINDOWS_
for (int i = 0; i < argc; i++) {
Expand Down
3 changes: 0 additions & 3 deletions contrib/asan/Make.user.asan
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,3 @@ override JULIA_BUILD_MODE=debug

# make ASAN consume less memory
export ASAN_OPTIONS=detect_leaks=0:fast_unwind_on_malloc=0:allow_user_segv_handler=1:malloc_context_size=2

# tell libblastrampoline to not use RTLD_DEEPBIND
export LBT_USE_RTLD_DEEPBIND=0
7 changes: 1 addition & 6 deletions contrib/asan/check.jl
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,7 @@ function main(args = ARGS)::Int
timeout = Threads.Atomic{Bool}(false)
isstarted = false
mktemp() do tmppath, tmpio
cmd = addenv(
`$julia -e $code $tmppath`,
"ASAN_OPTIONS" =>
"detect_leaks=0:fast_unwind_on_malloc=0:allow_user_segv_handler=1:malloc_context_size=2",
"LBT_USE_RTLD_DEEPBIND" => "0",
)
cmd = `$julia -e $code $tmppath`
# Note: Ideally, we set ASAN_SYMBOLIZER_PATH here. But there is no easy
# way to find out the path from just a Julia binary.

Expand Down
12 changes: 6 additions & 6 deletions src/aotcompile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -664,13 +664,13 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "loopinfo" as LLVM parallel loop
if (dump_native)
PM->add(createMultiVersioningPass());
#if defined(JL_ASAN_ENABLED)
#if defined(_COMPILER_ASAN_ENABLED_)
PM->add(createAddressSanitizerFunctionPass());
#endif
#if defined(JL_MSAN_ENABLED)
#if defined(_COMPILER_MSAN_ENABLED_)
PM->add(createMemorySanitizerPass(true));
#endif
#if defined(JL_TSAN_ENABLED)
#if defined(_COMPILER_TSAN_ENABLED_)
PM->add(createThreadSanitizerLegacyPassPass());
#endif
return;
Expand Down Expand Up @@ -813,13 +813,13 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
}
PM->add(createCombineMulAddPass());
PM->add(createDivRemPairsPass());
#if defined(JL_ASAN_ENABLED)
#if defined(_COMPILER_ASAN_ENABLED_)
PM->add(createAddressSanitizerFunctionPass());
#endif
#if defined(JL_MSAN_ENABLED)
#if defined(_COMPILER_MSAN_ENABLED_)
PM->add(createMemorySanitizerPass(true));
#endif
#if defined(JL_TSAN_ENABLED)
#if defined(_COMPILER_TSAN_ENABLED_)
PM->add(createThreadSanitizerLegacyPassPass());
#endif
}
Expand Down
2 changes: 1 addition & 1 deletion src/atomics.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ enum jl_memory_order {
__atomic_load_n(obj, __ATOMIC_SEQ_CST)
# define jl_atomic_load_acquire(obj) \
__atomic_load_n(obj, __ATOMIC_ACQUIRE)
#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_TSAN_ENABLED_
// For the sake of tsan, call these loads consume ordering since they will act
// as such on the processors we support while normally, the compiler would
// upgrade this to acquire ordering, which is strong (and slower) than we want.
Expand Down
4 changes: 2 additions & 2 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1776,7 +1776,7 @@ static void jl_init_function(Function *F)
#ifdef JL_DISABLE_FPO
F->addFnAttr("frame-pointer", "all");
#endif
#if !defined(JL_ASAN_ENABLED) && !defined(_OS_WINDOWS_)
#if !defined(_COMPILER_ASAN_ENABLED_) && !defined(_OS_WINDOWS_)
// ASAN won't like us accessing undefined memory causing spurious issues,
// and Windows has platform-specific handling which causes it to mishandle
// this annotation. Other platforms should just ignore this if they don't
Expand Down Expand Up @@ -6343,7 +6343,7 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
f->addFnAttr(Attribute::StackProtectStrong);
#endif

#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_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);
Expand Down
2 changes: 1 addition & 1 deletion src/dlload.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ JL_DLLEXPORT void *jl_dlopen(const char *filename, unsigned flags) JL_NOTSAFEPOI
#ifdef RTLD_NOLOAD
| JL_RTLD(flags, NOLOAD)
#endif
#if defined(RTLD_DEEPBIND) && !(defined(JL_ASAN_ENABLED) || defined(JL_TSAN_ENABLED) || defined(JL_MSAN_ENABLED))
#if defined(RTLD_DEEPBIND) && !(defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_TSAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_))
| JL_RTLD(flags, DEEPBIND)
#endif
#ifdef RTLD_FIRST
Expand Down
2 changes: 1 addition & 1 deletion src/gc-stacks.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ void sweep_stack_pools(void)
t->stkbuf = NULL;
_jl_free_stack(ptls2, stkbuf, bufsz);
}
#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_TSAN_ENABLED_
if (t->ctx.tsan_state) {
__tsan_destroy_fiber(t->ctx.tsan_state);
t->ctx.tsan_state = NULL;
Expand Down
2 changes: 1 addition & 1 deletion src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ void jl_init_stack_limits(int ismaster, void **stack_lo, void **stack_hi)
static void jl_prep_sanitizers(void)
{
#if !defined(_OS_WINDOWS_)
#if defined(JL_ASAN_ENABLED) || defined(JL_MSAN_ENABLED)
#if defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_)
struct rlimit rl;

// When using the sanitizers, increase stack size because they bloat
Expand Down
11 changes: 0 additions & 11 deletions src/jlapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,17 +501,6 @@ JL_DLLEXPORT int jl_set_fenv_rounding(int i)
return fesetround(i);
}


#ifdef JL_ASAN_ENABLED
JL_DLLEXPORT const char* __asan_default_options()
{
return "allow_user_segv_handler=1:detect_leaks=0";
// FIXME: enable LSAN after fixing leaks & defining __lsan_default_suppressions(),
// or defining __lsan_default_options = exitcode=0 once publicly available
// (here and in flisp/flmain.c)
}
#endif

static int exec_program(char *program)
{
JL_TRY {
Expand Down
21 changes: 1 addition & 20 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,6 @@
# define JL_THREAD_LOCAL
#endif

#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)
#if __clang_major__ < 11
#error Thread sanitizer runtime libraries in clang < 11 leak memory and cannot be used
#endif
#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 @@ -1860,7 +1841,7 @@ typedef struct _jl_task_t {
struct jl_stack_context_t copy_stack_ctx;
#endif
};
#if defined(JL_TSAN_ENABLED)
#if defined(_COMPILER_TSAN_ENABLED_)
void *tsan_state;
#endif
void *stkbuf; // malloc'd memory (either copybuf or stack)
Expand Down
6 changes: 3 additions & 3 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
#ifdef __cplusplus
extern "C" {
#endif
#ifdef JL_ASAN_ENABLED
#ifdef _COMPILER_ASAN_ENABLED_
void __sanitizer_start_switch_fiber(void**, const void*, size_t);
void __sanitizer_finish_switch_fiber(void*, const void**, size_t*);
#endif
#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_TSAN_ENABLED_
void *__tsan_create_fiber(unsigned flags);
void *__tsan_get_current_fiber(void);
void __tsan_destroy_fiber(void *fiber);
Expand Down Expand Up @@ -406,7 +406,7 @@ jl_value_t *jl_permbox64(jl_datatype_t *t, int64_t x);
jl_svec_t *jl_perm_symsvec(size_t n, ...);

// 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))
#if !defined(__clang_analyzer__) && !(defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_TSAN_ENABLED_))
#ifdef __GNUC__
#define jl_perm_symsvec(n, ...) \
(jl_perm_symsvec)(__extension__({ \
Expand Down
2 changes: 1 addition & 1 deletion src/julia_threads.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ struct jl_stack_context_t {
typedef struct jl_stack_context_t jl_ucontext_t;
#endif
#if defined(JL_HAVE_ASYNCIFY)
#if defined(JL_TSAN_ENABLED)
#if defined(_COMPILER_TSAN_ENABLED_)
#error TSAN not currently supported with asyncify
#endif
typedef struct {
Expand Down
12 changes: 5 additions & 7 deletions src/options.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// This file is a part of Julia. License is MIT: https://julialang.org/license

#include "platform.h"

#ifndef JL_OPTIONS_H
#define JL_OPTIONS_H

Expand Down Expand Up @@ -158,23 +160,19 @@

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

#ifndef JULIA_H
#error "Must be included after julia.h"
#endif

// Automatically enable MEMDEBUG and KEEP_BODIES for the sanitizers
#if defined(JL_ASAN_ENABLED) || defined(JL_MSAN_ENABLED)
#if defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_)
#define MEMDEBUG
#define KEEP_BODIES
#endif

// TSAN doesn't like COPY_STACKS
#if defined(JL_TSAN_ENABLED) && defined(COPY_STACKS)
#if defined(_COMPILER_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)
#if defined(_COMPILER_MSAN_ENABLED_)
// todo: fix the llvm MemoryManager to work with small memory model
#endif

Expand Down
19 changes: 19 additions & 0 deletions src/support/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@
#define _COMPILER_GCC_
#endif

#if defined(__has_feature) // Clang flavor
#if __has_feature(address_sanitizer)
#define _COMPILER_ASAN_ENABLED_
#endif
#if __has_feature(memory_sanitizer)
#define _COMPILER_MSAN_ENABLED_
#endif
#if __has_feature(thread_sanitizer)
#if __clang_major__ < 11
#error Thread sanitizer runtime libraries in clang < 11 leak memory and cannot be used
#endif
#define _COMPILER_TSAN_ENABLED_
#endif
#else // GCC flavor
#if defined(__SANITIZE_ADDRESS__)
#define _COMPILER_ASAN_ENABLED_
#endif
#endif // __has_feature

/*******************************************************************************
* OS *
*******************************************************************************/
Expand Down
2 changes: 1 addition & 1 deletion src/support/win32_ucontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ typedef struct {
size_t ss_size;
} uc_stack;
jmp_buf uc_mcontext;
#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_TSAN_ENABLED_
void *tsan_state;
#endif
} win32_ucontext_t;
Expand Down
4 changes: 2 additions & 2 deletions src/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
#include <intrin.h>
#endif

#ifdef JL_MSAN_ENABLED
#ifdef _COMPILER_MSAN_ENABLED_
#include <sanitizer/msan_interface.h>
#endif

Expand Down Expand Up @@ -796,7 +796,7 @@ JL_DLLEXPORT const char *jl_pathname_for_handle(void *handle)

struct link_map *map;
dlinfo(handle, RTLD_DI_LINKMAP, &map);
#ifdef JL_MSAN_ENABLED
#ifdef _COMPILER_MSAN_ENABLED_
__msan_unpoison(&map,sizeof(struct link_map*));
if (map) {
__msan_unpoison(map, sizeof(struct link_map));
Expand Down
18 changes: 9 additions & 9 deletions src/task.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
extern "C" {
#endif

#if defined(JL_ASAN_ENABLED)
#if defined(_COMPILER_ASAN_ENABLED_)
static inline void sanitizer_start_switch_fiber(const void* bottom, size_t size) {
__sanitizer_start_switch_fiber(NULL, bottom, size);
}
Expand All @@ -53,7 +53,7 @@ static inline void sanitizer_start_switch_fiber(const void* bottom, size_t size)
static inline void sanitizer_finish_switch_fiber(void) {}
#endif

#if defined(JL_TSAN_ENABLED)
#if defined(_COMPILER_TSAN_ENABLED_)
static inline void tsan_destroy_ctx(jl_ptls_t ptls, void *state) {
if (state != &ptls->root_task->state) {
__tsan_destroy_fiber(ctx->state);
Expand Down Expand Up @@ -321,7 +321,7 @@ JL_DLLEXPORT jl_task_t *jl_get_next_task(void) JL_NOTSAFEPOINT
return ct;
}

#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_TSAN_ENABLED_
const char tsan_state_corruption[] = "TSAN state corrupted. Exiting HARD!\n";
#endif

Expand All @@ -334,7 +334,7 @@ static void ctx_switch(jl_task_t *lastt)
// none of these locks should be held across a task switch
assert(ptls->locks.len == 0);

#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_TSAN_ENABLED_
if (lastt->ctx.tsan_state != __tsan_get_current_fiber()) {
// Something went really wrong - don't even assume that we can
// use assert/abort which involve lots of signal handling that
Expand Down Expand Up @@ -400,7 +400,7 @@ static void ctx_switch(jl_task_t *lastt)
#endif
jl_set_pgcstack(&t->gcstack);

#if defined(JL_TSAN_ENABLED)
#if defined(_COMPILER_TSAN_ENABLED_)
tsan_switch_to_ctx(&t->tsan_state);
if (killed)
tsan_destroy_ctx(ptls, &lastt->tsan_state);
Expand Down Expand Up @@ -765,7 +765,7 @@ JL_DLLEXPORT jl_task_t *jl_new_task(jl_function_t *start, jl_value_t *completion
memcpy(&t->ctx, &ct->ptls->base_ctx, sizeof(t->ctx));
}
#endif
#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_TSAN_ENABLED_
t->tsan_state = __tsan_create_fiber(0);
#endif
return t;
Expand Down Expand Up @@ -1147,7 +1147,7 @@ static void jl_start_fiber_set(jl_ucontext_t *t)
#endif

#if defined(JL_HAVE_SIGALTSTACK)
#if defined(JL_TSAN_ENABLED)
#if defined(_COMPILER_TSAN_ENABLED_)
#error TSAN support not currently implemented for this tasking model
#endif

Expand Down Expand Up @@ -1237,7 +1237,7 @@ static void jl_set_fiber(jl_ucontext_t *t)
#endif

#if defined(JL_HAVE_ASYNCIFY)
#if defined(JL_TSAN_ENABLED)
#if defined(_COMPILER_TSAN_ENABLED_)
#error TSAN support not currently implemented for this tasking model
#endif

Expand Down Expand Up @@ -1313,7 +1313,7 @@ void jl_init_root_task(jl_ptls_t ptls, void *stack_lo, void *stack_hi)
jl_set_pgcstack(&ct->gcstack);
assert(jl_current_task == ct);

#ifdef JL_TSAN_ENABLED
#ifdef _COMPILER_TSAN_ENABLED_
ct->tsan_state = __tsan_get_current_fiber();
#endif

Expand Down

0 comments on commit 8ece865

Please sign in to comment.