[ASan] Add interceptor for swapcontext to fight with false positives in some of its...
authorAlexey Samsonov <samsonov@google.com>
Fri, 23 Nov 2012 09:46:34 +0000 (09:46 +0000)
committerAlexey Samsonov <samsonov@google.com>
Fri, 23 Nov 2012 09:46:34 +0000 (09:46 +0000)
llvm-svn: 168508

compiler-rt/lib/asan/asan_intercepted_functions.h
compiler-rt/lib/asan/asan_interceptors.cc
compiler-rt/lib/asan/asan_internal.h
compiler-rt/lib/asan/asan_posix.cc
compiler-rt/lib/asan/asan_report.cc
compiler-rt/lib/asan/asan_win.cc
compiler-rt/lib/asan/lit_tests/swapcontext_test.cc [new file with mode: 0644]

index 03f126b..1f174fb 100644 (file)
@@ -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
index 48dfeb0..efd4bc5 100644 (file)
@@ -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
index f9a6149..201822c 100644 (file)
@@ -116,6 +116,7 @@ bool AsanInterceptsSignal(int signum);
 void SetAlternateSignalStack();
 void UnsetAlternateSignalStack();
 void InstallSignalHandlers();
+void ClearShadowMemoryForContext(void *context);
 void AsanPlatformThreadInit();
 
 // Wrapper for TLS/TSD.
index ceaf120..699af68 100644 (file)
@@ -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;
index 801827d..51b081e 100644 (file)
@@ -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;
index f04c73c..e620c71 100644 (file)
@@ -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 (file)
index 0000000..0404b4f
--- /dev/null
@@ -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;
+}