Index: compiler-rt/trunk/lib/esan/esan.cpp =================================================================== --- compiler-rt/trunk/lib/esan/esan.cpp +++ compiler-rt/trunk/lib/esan/esan.cpp @@ -197,6 +197,9 @@ } initializeShadow(); + if (__esan_which_tool == ESAN_WorkingSet) + initializeShadowWorkingSet(); + initializeInterceptors(); if (__esan_which_tool == ESAN_CacheFrag) { Index: compiler-rt/trunk/lib/esan/esan_interceptors.cpp =================================================================== --- compiler-rt/trunk/lib/esan/esan_interceptors.cpp +++ compiler-rt/trunk/lib/esan/esan_interceptors.cpp @@ -17,6 +17,7 @@ #include "interception/interception.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_stacktrace.h" using namespace __esan; // NOLINT @@ -397,6 +398,11 @@ // This is required to properly use internal_sigaction. namespace __sanitizer { int real_sigaction(int signum, const void *act, void *oldact) { + if (REAL(sigaction) == nullptr) { + // With an instrumented allocator, this is called during interceptor init + // and we need a raw syscall solution. + return internal_sigaction_syscall(signum, act, oldact); + } return REAL(sigaction)(signum, (const struct sigaction *)act, (struct sigaction *)oldact); } Index: compiler-rt/trunk/lib/esan/working_set.h =================================================================== --- compiler-rt/trunk/lib/esan/working_set.h +++ compiler-rt/trunk/lib/esan/working_set.h @@ -21,6 +21,7 @@ namespace __esan { void initializeWorkingSet(); +void initializeShadowWorkingSet(); int finalizeWorkingSet(); void processRangeAccessWorkingSet(uptr PC, uptr Addr, SIZE_T Size, bool IsWrite); Index: compiler-rt/trunk/lib/esan/working_set.cpp =================================================================== --- compiler-rt/trunk/lib/esan/working_set.cpp +++ compiler-rt/trunk/lib/esan/working_set.cpp @@ -182,10 +182,13 @@ } } -void initializeWorkingSet() { +// Initialization that must be done before any instrumented code is executed. +void initializeShadowWorkingSet() { CHECK(getFlags()->cache_line_size == CacheLineSize); registerMemoryFaultHandler(); +} +void initializeWorkingSet() { if (getFlags()->record_snapshots) { for (u32 i = 0; i < NumFreq; ++i) SizePerFreq[i].initialize(CircularBufferSizes[i]); Index: compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt +++ compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt @@ -12,6 +12,7 @@ sanitizer_libignore.cc sanitizer_linux.cc sanitizer_linux_s390.cc + sanitizer_linux_x86_64.S sanitizer_mac.cc sanitizer_persistent_allocator.cc sanitizer_platform_limits_linux.cc @@ -133,6 +134,17 @@ append_list_if(COMPILER_RT_HAS_WGLOBAL_CONSTRUCTORS_FLAG -Wglobal-constructors SANITIZER_CFLAGS) +if (LLVM_ENABLE_PEDANTIC) + # With -pedantic, our .S files raise warnings about empty macro arguments + # from __USER_LABEL_PREFIX__ being an empty arg to GLUE(). Unfortunately, + # there is no simple way to test for an empty define, nor to disable just + # that warning or to disable -pedantic. There is also no simple way to + # remove -pedantic from just this file (we'd have to remove from + # CMAKE_C*_FLAGS and re-add as a source property to all the non-.S files). + set_source_files_properties(sanitizer_linux_x86_64.S + PROPERTIES COMPILE_FLAGS "-w") +endif () + if(APPLE) set(OS_OPTION OS ${SANITIZER_COMMON_SUPPORTED_OS}) endif() Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.h @@ -42,6 +42,10 @@ // (like the process-wide error reporting SEGV handler) must use // internal_sigaction instead. int internal_sigaction_norestorer(int signum, const void *act, void *oldact); +#if defined(__x86_64__) && !SANITIZER_GO +// Uses a raw system call to avoid interceptors. +int internal_sigaction_syscall(int signum, const void *act, void *oldact); +#endif void internal_sigdelset(__sanitizer_sigset_t *set, int signum); #if defined(__x86_64__) || defined(__mips__) || defined(__aarch64__) \ || defined(__powerpc64__) || defined(__s390__) Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux.cc @@ -99,6 +99,12 @@ # define SANITIZER_LINUX_USES_64BIT_SYSCALLS 0 #endif +#if defined(__x86_64__) +extern "C" { +extern void internal_sigreturn(); +} +#endif + namespace __sanitizer { #if SANITIZER_LINUX && defined(__x86_64__) @@ -616,7 +622,8 @@ #if SANITIZER_LINUX #define SA_RESTORER 0x04000000 -// Doesn't set sa_restorer, use with caution (see below). +// Doesn't set sa_restorer if the caller did not set it, so use with caution +//(see below). int internal_sigaction_norestorer(int signum, const void *act, void *oldact) { __sanitizer_kernel_sigaction_t k_act, k_oldact; internal_memset(&k_act, 0, sizeof(__sanitizer_kernel_sigaction_t)); @@ -660,6 +667,23 @@ } return result; } + +// Invokes sigaction via a raw syscall with a restorer, but does not support +// all platforms yet. +// We disable for Go simply because we have not yet added to buildgo.sh. +#if defined(__x86_64__) && !SANITIZER_GO +int internal_sigaction_syscall(int signum, const void *act, void *oldact) { + __sanitizer_sigaction u_adjust; + internal_memcpy(&u_adjust, act, sizeof(u_adjust)); +#if !SANITIZER_ANDROID || !SANITIZER_MIPS32 + if (u_adjust.sa_restorer == nullptr) { + u_adjust.sa_restorer = internal_sigreturn; + } +#endif + return internal_sigaction_norestorer(signum, (const void *)&u_adjust, + oldact); +} +#endif // defined(__x86_64__) && !SANITIZER_GO #endif // SANITIZER_LINUX uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux_x86_64.S =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux_x86_64.S +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_linux_x86_64.S @@ -0,0 +1,25 @@ +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. + +// Avoid being marked as needing an executable stack: +#if defined(__linux__) && defined(__ELF__) +.section .note.GNU-stack,"",%progbits +#endif + +// Further contents are x86_64-only: +#if defined(__linux__) && defined(__x86_64__) + +#include "../builtins/assembly.h" + +// If the "naked" function attribute were supported for x86 we could +// do this via inline asm. +.text +.balign 4 +DEFINE_COMPILERRT_FUNCTION(internal_sigreturn) + mov $0xf, %eax // 0xf == SYS_rt_sigreturn + mov %rcx, %r10 + syscall + ret // Won't normally reach here. +END_COMPILERRT_FUNCTION(internal_sigreturn) + +#endif // defined(__linux__) && defined(__x86_64__) Index: compiler-rt/trunk/test/esan/TestCases/workingset-early-fault.c =================================================================== --- compiler-rt/trunk/test/esan/TestCases/workingset-early-fault.c +++ compiler-rt/trunk/test/esan/TestCases/workingset-early-fault.c @@ -0,0 +1,33 @@ +// Test shadow faults during esan initialization as well as +// faults during dlsym's calloc during interceptor init. +// +// RUN: %clang_esan_wset %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +#include +#include +#include + +// Our goal is to emulate an instrumented allocator, whose calloc +// invoked from dlsym will trigger shadow faults, to test an +// early shadow fault during esan interceptor init. +// We do this by replacing calloc: +void *calloc(size_t size, size_t n) { + // Unfortunately we can't print anything to make the test + // ensure we got here b/c the sanitizer interceptors can't + // handle that during interceptor init. + + // Ensure we trigger a shadow write fault: + int x[16]; + x[0] = size; + // Now just emulate calloc. + void *res = malloc(size*n); + memset(res, 0, size*n); + return res; +} + +int main(int argc, char **argv) { + printf("all done\n"); + return 0; +} +// CHECK: all done