[compiler-rt] r260554 - [LSan] Fix a crash when LSan hits a guard page while scanning thread stack for pointers.

Alexey Samsonov via llvm-commits llvm-commits at lists.llvm.org
Thu Feb 11 10:07:18 PST 2016


Author: samsonov
Date: Thu Feb 11 12:07:17 2016
New Revision: 260554

URL: http://llvm.org/viewvc/llvm-project?rev=260554&view=rev
Log:
[LSan] Fix a crash when LSan hits a guard page while scanning thread stack for pointers.

Summary:
In some cases stack pointer register (SP) doesn't point into the thread
stack: e.g. if one is using swapcontext(). In this case LSan
conservatively tries to scan the whole thread stack for pointers.
However, thread stack (at least in glibc implementation) may also
include guard pages, causing LSan to crash when it's reading from them.

One of the solutions is to use a pthread_attr_getguardsize() to adjust
the calculated stack boundaries. However, here we're just using
IsAccessibleMemoryRange to skip guard pages and make the code (slightly)
less platform-specific.

Reviewers: kcc

Subscribers: llvm-commits

Differential Revision: http://reviews.llvm.org/D17116

Added:
    compiler-rt/trunk/test/lsan/TestCases/guard-page.c
Modified:
    compiler-rt/trunk/lib/lsan/lsan_common.cc

Modified: compiler-rt/trunk/lib/lsan/lsan_common.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/lsan/lsan_common.cc?rev=260554&r1=260553&r2=260554&view=diff
==============================================================================
--- compiler-rt/trunk/lib/lsan/lsan_common.cc (original)
+++ compiler-rt/trunk/lib/lsan/lsan_common.cc Thu Feb 11 12:07:17 2016
@@ -221,9 +221,18 @@ static void ProcessThreads(SuspendedThre
       LOG_THREADS("Stack at %p-%p (SP = %p).\n", stack_begin, stack_end, sp);
       if (sp < stack_begin || sp >= stack_end) {
         // SP is outside the recorded stack range (e.g. the thread is running a
-        // signal handler on alternate stack). Again, consider the entire stack
-        // range to be reachable.
+        // signal handler on alternate stack, or swapcontext was used).
+        // Again, consider the entire stack range to be reachable.
         LOG_THREADS("WARNING: stack pointer not in stack range.\n");
+        uptr page_size = GetPageSizeCached();
+        int skipped = 0;
+        while (stack_begin < stack_end &&
+               !IsAccessibleMemoryRange(stack_begin, 1)) {
+          skipped++;
+          stack_begin += page_size;
+        }
+        LOG_THREADS("Skipped %d guard page(s) to obtain stack %p-%p.\n",
+                    skipped, stack_begin, stack_end);
       } else {
         // Shrink the stack range to ignore out-of-scope values.
         stack_begin = sp;

Added: compiler-rt/trunk/test/lsan/TestCases/guard-page.c
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/test/lsan/TestCases/guard-page.c?rev=260554&view=auto
==============================================================================
--- compiler-rt/trunk/test/lsan/TestCases/guard-page.c (added)
+++ compiler-rt/trunk/test/lsan/TestCases/guard-page.c Thu Feb 11 12:07:17 2016
@@ -0,0 +1,60 @@
+// Check that if LSan finds that SP doesn't point into thread stack (e.g.
+// if swapcontext is used), LSan will not hit the guard page.
+// RUN: %clang_lsan %s -o %t && %run %t
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <ucontext.h>
+
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+int ctxfunc_started = 0;
+
+static void die(const char* msg, int err) {
+  if (err == 0)
+    err = errno;
+  fprintf(stderr, "%s: %s\n", msg, strerror(err));
+  exit(EXIT_FAILURE);
+}
+
+static void ctxfunc() {
+  pthread_mutex_lock(&mutex);
+  ctxfunc_started = 1;
+  pthread_cond_signal(&cond);
+  pthread_mutex_unlock(&mutex);
+  // Leave this context alive when the program exits.
+  for (;;);
+}
+
+static void* thread(void* arg) {
+  (void)arg;
+  ucontext_t ctx;
+  void* stack;
+
+  if (getcontext(&ctx) < 0)
+    die("getcontext", 0);
+  stack = malloc(1 << 10);
+  if (stack == NULL)
+    die("malloc", 0);
+  ctx.uc_stack.ss_sp = stack;
+  ctx.uc_stack.ss_size = 1 << 10;
+  makecontext(&ctx, ctxfunc, 0);
+  setcontext(&ctx);
+  die("setcontext", 0);
+  return NULL;
+}
+
+int main() {
+  pthread_t tid;
+  int i;
+
+  pthread_mutex_lock(&mutex);
+  i = pthread_create(&tid, NULL, thread, NULL);
+  if (i != 0)
+    die("pthread_create", i);
+  while (!ctxfunc_started) pthread_cond_wait(&cond, &mutex);
+  pthread_mutex_unlock(&mutex);
+  return 0;
+}




More information about the llvm-commits mailing list