#include "sanitizer_procmaps.h"
#include "sanitizer_symbolizer_internal.h"
#include "sanitizer_symbolizer_libbacktrace.h"
+#include "sanitizer_symbolizer_mac.h"
#include <unistd.h>
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);
}
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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}}
+}
--- /dev/null
+// 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.