Add AtosSymbolizer and DlAddrSymbolizer as fallbacks for OS X

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
This commit is contained in:
Kuba Brecka 2015-03-22 10:02:50 +00:00
parent 08dcf60295
commit 0be4e0e0c1
4 changed files with 150 additions and 28 deletions

View 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;
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;
if (SymbolizerTool *tool = ChooseExternalSymbolizer(allocator)) {
list->push_back(tool);
} else {
VReport(2, "No internal or external symbolizer found.\n");
}
// 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);
}
}
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);
}

View File

@ -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;
}

View File

@ -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}}
}

View File

@ -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.