Index: lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ lib/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -23,6 +23,7 @@ #include "sanitizer_procmaps.h" #include "sanitizer_symbolizer_internal.h" #include "sanitizer_symbolizer_libbacktrace.h" +#include "sanitizer_symbolizer_mac.h" #include @@ -413,49 +414,87 @@ 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 *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; + list->push_back(tool); + return; } - 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; - } - // 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 list; list.clear(); - if (SymbolizerTool *tool = ChooseSymbolizer(&symbolizer_allocator_)) { - list.push_back(tool); - } + ChooseSymbolizerTools(&list, &symbolizer_allocator_); return new(symbolizer_allocator_) POSIXSymbolizer(list); } Index: test/asan/TestCases/Darwin/atos-symbolizer.cc =================================================================== --- test/asan/TestCases/Darwin/atos-symbolizer.cc +++ test/asan/TestCases/Darwin/atos-symbolizer.cc @@ -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 +#include +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; +} Index: test/asan/TestCases/Darwin/sandbox-symbolizer.cc =================================================================== --- test/asan/TestCases/Darwin/sandbox-symbolizer.cc +++ test/asan/TestCases/Darwin/sandbox-symbolizer.cc @@ -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 +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}} +} Index: test/asan/TestCases/Darwin/suppressions-sandbox.cc =================================================================== --- test/asan/TestCases/Darwin/suppressions-sandbox.cc +++ test/asan/TestCases/Darwin/suppressions-sandbox.cc @@ -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 + +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.