diff options
-rw-r--r-- | compiler-rt/lib/asan/asan_intercepted_functions.h | 8 | ||||
-rw-r--r-- | compiler-rt/lib/asan/asan_interceptors.cc | 25 | ||||
-rw-r--r-- | compiler-rt/lib/asan/asan_internal.h | 1 | ||||
-rw-r--r-- | compiler-rt/lib/asan/asan_posix.cc | 12 | ||||
-rw-r--r-- | compiler-rt/lib/asan/asan_report.cc | 2 | ||||
-rw-r--r-- | compiler-rt/lib/asan/asan_win.cc | 4 | ||||
-rw-r--r-- | compiler-rt/lib/asan/lit_tests/swapcontext_test.cc | 66 |
7 files changed, 117 insertions, 1 deletions
diff --git a/compiler-rt/lib/asan/asan_intercepted_functions.h b/compiler-rt/lib/asan/asan_intercepted_functions.h index 03f126b91f3..1f174fb6318 100644 --- a/compiler-rt/lib/asan/asan_intercepted_functions.h +++ b/compiler-rt/lib/asan/asan_intercepted_functions.h @@ -53,8 +53,10 @@ using __sanitizer::uptr; #if !defined(ANDROID) && !defined(_WIN32) # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 1 +# define ASAN_INTERCEPT_SWAPCONTEXT 1 #else # define ASAN_INTERCEPT_SIGNAL_AND_SIGACTION 0 +# define ASAN_INTERCEPT_SWAPCONTEXT 0 #endif // On Darwin siglongjmp tailcalls longjmp, so we don't want to intercept it @@ -88,6 +90,12 @@ DECLARE_FUNCTION_AND_WRAPPER(int, sigaction, int sig, DECLARE_FUNCTION_AND_WRAPPER(void*, signal, int signum, void *handler); # endif +// ucontext.h +# if ASAN_INTERCEPT_SWAPCONTEXT +DECLARE_FUNCTION_AND_WRAPPER(int, swapcontext, struct ucontext_t *oucp, + struct ucontext_t *ucp); +# endif + // setjmp.h DECLARE_FUNCTION_AND_WRAPPER(void, longjmp, void *env, int value); # if ASAN_INTERCEPT__LONGJMP diff --git a/compiler-rt/lib/asan/asan_interceptors.cc b/compiler-rt/lib/asan/asan_interceptors.cc index 48dfeb0018d..efd4bc53a2b 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cc +++ b/compiler-rt/lib/asan/asan_interceptors.cc @@ -136,6 +136,28 @@ DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact); #endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION +#if ASAN_INTERCEPT_SWAPCONTEXT +INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp, + struct ucontext_t *ucp) { + static bool reported_warning = false; + if (!reported_warning) { + Report("WARNING: ASan doesn't fully support makecontext/swapcontext " + "functions and may produce false positives in some cases!\n"); + reported_warning = true; + } + // Clear shadow memory for new context (it may share stack + // with current context). + ClearShadowMemoryForContext(ucp); + int res = REAL(swapcontext)(oucp, ucp); + // swapcontext technically does not return, but program may swap context to + // "oucp" later, that would look as if swapcontext() returned 0. + // We need to clear shadow for ucp once again, as it may be in arbitrary + // state. + ClearShadowMemoryForContext(ucp); + return res; +} +#endif + INTERCEPTOR(void, longjmp, void *env, int val) { __asan_handle_no_return(); REAL(longjmp)(env, val); @@ -689,6 +711,9 @@ void InitializeAsanInterceptors() { ASAN_INTERCEPT_FUNC(sigaction); ASAN_INTERCEPT_FUNC(signal); #endif +#if ASAN_INTERCEPT_SWAPCONTEXT + ASAN_INTERCEPT_FUNC(swapcontext); +#endif #if ASAN_INTERCEPT__LONGJMP ASAN_INTERCEPT_FUNC(_longjmp); #endif diff --git a/compiler-rt/lib/asan/asan_internal.h b/compiler-rt/lib/asan/asan_internal.h index f9a6149245e..201822c62ae 100644 --- a/compiler-rt/lib/asan/asan_internal.h +++ b/compiler-rt/lib/asan/asan_internal.h @@ -116,6 +116,7 @@ bool AsanInterceptsSignal(int signum); void SetAlternateSignalStack(); void UnsetAlternateSignalStack(); void InstallSignalHandlers(); +void ClearShadowMemoryForContext(void *context); void AsanPlatformThreadInit(); // Wrapper for TLS/TSD. diff --git a/compiler-rt/lib/asan/asan_posix.cc b/compiler-rt/lib/asan/asan_posix.cc index ceaf120fc80..699af68123f 100644 --- a/compiler-rt/lib/asan/asan_posix.cc +++ b/compiler-rt/lib/asan/asan_posix.cc @@ -27,6 +27,7 @@ #include <stdlib.h> #include <sys/time.h> #include <sys/resource.h> +#include <ucontext.h> #include <unistd.h> static const uptr kAltStackSize = SIGSTKSZ * 4; // SIGSTKSZ is not enough. @@ -95,6 +96,17 @@ void InstallSignalHandlers() { MaybeInstallSigaction(SIGBUS, ASAN_OnSIGSEGV); } +void ClearShadowMemoryForContext(void *context) { + ucontext_t *ucp = (ucontext_t*)context; + uptr sp = (uptr)ucp->uc_stack.ss_sp; + uptr size = ucp->uc_stack.ss_size; + // Align to page size. + uptr bottom = sp & ~(kPageSize - 1); + size += sp - bottom; + size = RoundUpTo(size, kPageSize); + PoisonShadow(bottom, size, 0); +} + // ---------------------- TSD ---------------- {{{1 static pthread_key_t tsd_key; diff --git a/compiler-rt/lib/asan/asan_report.cc b/compiler-rt/lib/asan/asan_report.cc index 801827dbc8c..51b081e1534 100644 --- a/compiler-rt/lib/asan/asan_report.cc +++ b/compiler-rt/lib/asan/asan_report.cc @@ -180,7 +180,7 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) { Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); } Printf("HINT: this may be a false positive if your program uses " - "some custom stack unwind mechanism\n" + "some custom stack unwind mechanism or swapcontext\n" " (longjmp and C++ exceptions *are* supported)\n"); DescribeThread(t->summary()); return true; diff --git a/compiler-rt/lib/asan/asan_win.cc b/compiler-rt/lib/asan/asan_win.cc index f04c73cf7c8..e620c717b86 100644 --- a/compiler-rt/lib/asan/asan_win.cc +++ b/compiler-rt/lib/asan/asan_win.cc @@ -139,6 +139,10 @@ void AsanPlatformThreadInit() { // Nothing here for now. } +void ClearShadowMemoryForContext(void *context) { + UNIMPLEMENTED(); +} + } // namespace __asan // ---------------------- Interface ---------------- {{{1 diff --git a/compiler-rt/lib/asan/lit_tests/swapcontext_test.cc b/compiler-rt/lib/asan/lit_tests/swapcontext_test.cc new file mode 100644 index 00000000000..0404b4f602b --- /dev/null +++ b/compiler-rt/lib/asan/lit_tests/swapcontext_test.cc @@ -0,0 +1,66 @@ +// Check that ASan plays well with easy cases of makecontext/swapcontext. + +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O3 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O1 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O2 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m32 -O3 %s -o %t && %t 2>&1 | FileCheck %s + +#include <stdio.h> +#include <ucontext.h> +#include <unistd.h> + +ucontext_t orig_context; +ucontext_t child_context; + +void Child(int mode) { + char x[32] = {0}; // Stack gets poisoned. + printf("Child: %p\n", x); + // (a) Do nothing, just return to parent function. + // (b) Jump into the original function. Stack remains poisoned unless we do + // something. + if (mode == 1) { + if (swapcontext(&child_context, &orig_context) < 0) { + perror("swapcontext"); + _exit(0); + } + } +} + +int Run(int arg, int mode) { + const int kStackSize = 1 << 20; + char child_stack[kStackSize + 1]; + printf("Child stack: %p\n", child_stack); + // Setup child context. + getcontext(&child_context); + child_context.uc_stack.ss_sp = child_stack; + child_context.uc_stack.ss_size = kStackSize / 2; + if (mode == 0) { + child_context.uc_link = &orig_context; + } + makecontext(&child_context, (void (*)())Child, 1, mode); + if (swapcontext(&orig_context, &child_context) < 0) { + perror("swapcontext"); + return 0; + } + // Touch childs's stack to make sure it's unpoisoned. + for (int i = 0; i < kStackSize; i++) { + child_stack[i] = i; + } + return child_stack[arg]; +} + +int main(int argc, char **argv) { + // CHECK: WARNING: ASan doesn't fully support makecontext/swapcontext + int ret = 0; + ret += Run(argc - 1, 0); + printf("Test1 passed\n"); + // CHECK: Test1 passed + ret += Run(argc - 1, 1); + printf("Test2 passed\n"); + // CHECK: Test2 passed + return ret; +} |