Add AtosSymbolizer and DlAddrSymbolizer as fallbacks for OS X
authorKuba Brecka <kuba.brecka@gmail.com>
Sun, 22 Mar 2015 10:02:50 +0000 (10:02 +0000)
committerKuba Brecka <kuba.brecka@gmail.com>
Sun, 22 Mar 2015 10:02:50 +0000 (10:02 +0000)
This patch changes the symbolizer chain on OS X (which currently only uses 1
symbolizer at most) to use this behavior:

* By default, use LLVMSymbolizer -> DlAddrSymbolizer.
* If the llvm-symbolizer binary is not found, use AtosSymbolizer
    -> DlAddrSymbolizer.
* If the user specifies ASAN_SYMBOLIZER_PATH=.../atos, then use AtosSymbolizer
    -> DlAddrSymbolizer.
* If neither llvm-symbolizer or atos is found, or external symbolication is
    disabled with ASAN_SYMBOLIZER_PATH="", use DlAddrSymbolizer.

Reviewed at http://reviews.llvm.org/D8285

llvm-svn: 232908

compiler-rt/lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc
compiler-rt/test/asan/TestCases/Darwin/atos-symbolizer.cc [new file with mode: 0644]
compiler-rt/test/asan/TestCases/Darwin/sandbox-symbolizer.cc [new file with mode: 0644]
compiler-rt/test/asan/TestCases/Darwin/suppressions-sandbox.cc [new file with mode: 0644]

index ceb0de5..efd284d 100644 (file)
@@ -23,6 +23,7 @@
 #include "sanitizer_procmaps.h"
 #include "sanitizer_symbolizer_internal.h"
 #include "sanitizer_symbolizer_libbacktrace.h"
+#include "sanitizer_symbolizer_mac.h"
 
 #include <unistd.h>
 
@@ -413,49 +414,87 @@ class POSIXSymbolizer : public Symbolizer {
   bool modules_fresh_;
 };
 
-static SymbolizerTool *ChooseSymbolizer(LowLevelAllocator *allocator) {
+static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) {
+  const char *path = common_flags()->external_symbolizer_path;
+  const char *binary_name = path ? StripModuleName(path) : "";
+  if (path && path[0] == '\0') {
+    VReport(2, "External symbolizer is explicitly disabled.\n");
+    return nullptr;
+  } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) {
+    VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path);
+    return new(*allocator) LLVMSymbolizer(path, allocator);
+  } else if (!internal_strcmp(binary_name, "atos")) {
+#if SANITIZER_MAC
+    VReport(2, "Using atos at user-specified path: %s\n", path);
+    return new(*allocator) AtosSymbolizer(path, allocator);
+#else  // SANITIZER_MAC
+    Report("ERROR: Using `atos` is only supported on Darwin.\n");
+    Die();
+#endif  // SANITIZER_MAC
+  } else if (!internal_strcmp(binary_name, "addr2line")) {
+    VReport(2, "Using addr2line at user-specified path: %s\n", path);
+    return new(*allocator) Addr2LinePool(path, allocator);
+  } else if (path) {
+    Report("ERROR: External symbolizer path is set to '%s' which isn't "
+           "a known symbolizer. Please set the path to the llvm-symbolizer "
+           "binary or other known tool.\n", path);
+    Die();
+  }
+
+  // Otherwise symbolizer program is unknown, let's search $PATH
+  CHECK(path == nullptr);
+  if (const char *found_path = FindPathToBinary("llvm-symbolizer")) {
+    VReport(2, "Using llvm-symbolizer found at: %s\n", found_path);
+    return new(*allocator) LLVMSymbolizer(found_path, allocator);
+  }
+#if SANITIZER_MAC
+  if (const char *found_path = FindPathToBinary("atos")) {
+    VReport(2, "Using atos found at: %s\n", found_path);
+    return new(*allocator) AtosSymbolizer(found_path, allocator);
+  }
+#endif  // SANITIZER_MAC
+  if (common_flags()->allow_addr2line) {
+    if (const char *found_path = FindPathToBinary("addr2line")) {
+      VReport(2, "Using addr2line found at: %s\n", found_path);
+      return new(*allocator) Addr2LinePool(found_path, allocator);
+    }
+  }
+  return nullptr;
+}
+
+static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list,
+                                  LowLevelAllocator *allocator) {
   if (!common_flags()->symbolize) {
     VReport(2, "Symbolizer is disabled.\n");
-    return nullptr;
+    return;
   }
   if (SymbolizerTool *tool = InternalSymbolizer::get(allocator)) {
     VReport(2, "Using internal symbolizer.\n");
-    return tool;
+    list->push_back(tool);
+    return;
   }
   if (SymbolizerTool *tool = LibbacktraceSymbolizer::get(allocator)) {
     VReport(2, "Using libbacktrace symbolizer.\n");
-    return tool;
-  }
-  const char *path_to_external = common_flags()->external_symbolizer_path;
-  if (path_to_external && path_to_external[0] == '\0') {
-    // External symbolizer is explicitly disabled. Do nothing.
-    return nullptr;
+    list->push_back(tool);
+    return;
   }
-  // Find path to llvm-symbolizer if it's not provided.
-  if (!path_to_external) {
-    path_to_external = FindPathToBinary("llvm-symbolizer");
-  }
-  if (path_to_external) {
-    VReport(2, "Using llvm-symbolizer at path: %s\n", path_to_external);
-    return new(*allocator) LLVMSymbolizer(path_to_external, allocator);
-  }
-  if (common_flags()->allow_addr2line) {
-    // If llvm-symbolizer is not found, try to use addr2line.
-    if (const char *addr2line_path = FindPathToBinary("addr2line")) {
-      VReport(2, "Using addr2line at path: %s\n", addr2line_path);
-      return new(*allocator) Addr2LinePool(addr2line_path, allocator);
-    }
+
+  if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
+    list->push_back(tool);
+  } else {
+    VReport(2, "No internal or external symbolizer found.\n");
   }
-  VReport(2, "No internal or external symbolizer found.\n");
-  return nullptr;
+
+#if SANITIZER_MAC
+  VReport(2, "Using dladdr symbolizer.\n");
+  list->push_back(new(*allocator) DlAddrSymbolizer());
+#endif  // SANITIZER_MAC
 }
 
 Symbolizer *Symbolizer::PlatformInit() {
   IntrusiveList<SymbolizerTool> list;
   list.clear();
-  if (SymbolizerTool *tool = ChooseSymbolizer(&symbolizer_allocator_)) {
-    list.push_back(tool);
-  }
+  ChooseSymbolizerTools(&list, &symbolizer_allocator_);
   return new(symbolizer_allocator_) POSIXSymbolizer(list);
 }
 
diff --git a/compiler-rt/test/asan/TestCases/Darwin/atos-symbolizer.cc b/compiler-rt/test/asan/TestCases/Darwin/atos-symbolizer.cc
new file mode 100644 (file)
index 0000000..a4df3a2
--- /dev/null
@@ -0,0 +1,28 @@
+// Check that the `atos` symbolizer works.
+
+// RUN: %clangxx_asan -O0 %s -o %t 
+// RUN: ASAN_OPTIONS=verbosity=2 ASAN_SYMBOLIZER_PATH=$(which atos) not %run %t 2>&1 | FileCheck %s
+
+// Check that when having a DYLD_ROOT_PATH set, the symbolizer still works.
+// RUN: DYLD_ROOT_PATH="/" ASAN_OPTIONS=verbosity=2 ASAN_SYMBOLIZER_PATH=$(which atos) \
+// RUN:   not %run %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+#include <string.h>
+int main(int argc, char **argv) {
+  char *x = (char*)malloc(10 * sizeof(char));
+  memset(x, 0, 10);
+  int res = x[argc];
+  free(x);
+  free(x + argc - 1);  // BOOM
+  // CHECK: AddressSanitizer: attempting double-free{{.*}}in thread T0
+  // CHECK: Using atos at user-specified path:
+  // CHECK: #0 0x{{.*}} in {{.*}}free
+  // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer.cc:[[@LINE-4]]
+  // CHECK: freed by thread T0 here:
+  // CHECK: #0 0x{{.*}} in {{.*}}free
+  // CHECK: #1 0x{{.*}} in main {{.*}}atos-symbolizer.cc:[[@LINE-8]]
+  // CHECK: allocated by thread T0 here:
+  // CHECK: atos-symbolizer.cc:[[@LINE-13]]
+  return res;
+}
diff --git a/compiler-rt/test/asan/TestCases/Darwin/sandbox-symbolizer.cc b/compiler-rt/test/asan/TestCases/Darwin/sandbox-symbolizer.cc
new file mode 100644 (file)
index 0000000..4313371
--- /dev/null
@@ -0,0 +1,29 @@
+// In a non-forking sandbox, we can't spawn an external symbolizer, but dladdr()
+// should still work and provide function names. No line numbers though.
+// Second, `atos` symbolizer can't inspect a process that has an inaccessible
+// task port, in which case we should again fallback to dladdr gracefully.
+
+// RUN: %clangxx_asan -O0 %s -o %t 
+// RUN: not %run sandbox-exec -p '(version 1)(allow default)(deny process-fork)' %t 2>&1 | FileCheck %s
+// RUN: not %run sandbox-exec -p '(version 1)(allow default)(deny mach-priv-task-port)' %t 2>&1 | FileCheck %s
+// RUN: ASAN_SYMBOLIZER_PATH="" not %run sandbox-exec -p '(version 1)(allow default)(deny mach-priv-task-port)' %t 2>&1 | FileCheck %s
+// RUN: %clangxx_asan -O3 %s -o %t 
+// RUN: not %run sandbox-exec -p '(version 1)(allow default)(deny process-fork)' %t 2>&1 | FileCheck %s
+// RUN: not %run sandbox-exec -p '(version 1)(allow default)(deny mach-priv-task-port)' %t 2>&1 | FileCheck %s
+// RUN: ASAN_SYMBOLIZER_PATH="" not %run sandbox-exec -p '(version 1)(allow default)(deny mach-priv-task-port)' %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+int main() {
+  char *x = (char*)malloc(10 * sizeof(char));
+  free(x);
+  return x[5];
+  // CHECK: {{.*ERROR: AddressSanitizer: heap-use-after-free on address}}
+  // CHECK: {{READ of size 1 at 0x.* thread T0}}
+  // CHECK: {{    #0 0x.* in main}}
+  // CHECK: {{freed by thread T0 here:}}
+  // CHECK: {{    #0 0x.* in wrap_free}}
+  // CHECK: {{    #1 0x.* in main}}
+  // CHECK: {{previously allocated by thread T0 here:}}
+  // CHECK: {{    #0 0x.* in wrap_malloc}}
+  // CHECK: {{    #1 0x.* in main}}
+}
diff --git a/compiler-rt/test/asan/TestCases/Darwin/suppressions-sandbox.cc b/compiler-rt/test/asan/TestCases/Darwin/suppressions-sandbox.cc
new file mode 100644 (file)
index 0000000..59fe475
--- /dev/null
@@ -0,0 +1,26 @@
+// Check that without suppressions, we catch the issue.
+// RUN: %clangxx_asan -O0 %s -o %t -framework Foundation
+// RUN: not %run %t 2>&1 | FileCheck --check-prefix=CHECK-CRASH %s
+
+// Check that suppressing a function name works within a no-fork sandbox
+// RUN: echo "interceptor_via_fun:CFStringCreateWithBytes" > %t.supp
+// RUN: ASAN_OPTIONS=suppressions=%t.supp \
+// RUN:   sandbox-exec -p '(version 1)(allow default)(deny process-fork)' \
+// RUN:   %run %t 2>&1 | FileCheck --check-prefix=CHECK-IGNORE %s
+
+#include <CoreFoundation/CoreFoundation.h>
+
+int main() {
+  char *a = (char *)malloc(6);
+  strcpy(a, "hello");
+  CFStringRef str =
+      CFStringCreateWithBytes(kCFAllocatorDefault, (unsigned char *)a, 10,
+                              kCFStringEncodingUTF8, FALSE);  // BOOM
+  fprintf(stderr, "Ignored.\n");
+  free(a);
+}
+
+// CHECK-CRASH: AddressSanitizer: heap-buffer-overflow
+// CHECK-CRASH-NOT: Ignored.
+// CHECK-IGNORE-NOT: AddressSanitizer: heap-buffer-overflow
+// CHECK-IGNORE: Ignored.