diff --git a/compiler-rt/lib/asan/scripts/asan_symbolize.py b/compiler-rt/lib/asan/scripts/asan_symbolize.py --- a/compiler-rt/lib/asan/scripts/asan_symbolize.py +++ b/compiler-rt/lib/asan/scripts/asan_symbolize.py @@ -275,11 +275,14 @@ atos_line = self.atos.readline() # A well-formed atos response looks like this: # foo(type1, type2) (in object.name) (filename.cc:80) + # NOTE: + # * If `foo` is a C function atos omits parentheses and argument types. + # * If `foo` is a C++ function atos emits parentheses argument types. + # * `foo` may contain templates which may contain parentheses. match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line) logging.debug('atos_line: %s', atos_line) if match: function_name = match.group(1) - function_name = re.sub('\(.*?\)', '', function_name) file_name = fix_filename(match.group(3)) return ['%s in %s %s' % (addr, function_name, file_name)] else: diff --git a/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-templated-cxx.cpp b/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-templated-cxx.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Darwin/asan-symbolize-templated-cxx.cpp @@ -0,0 +1,62 @@ +// UNSUPPORTED: ios +// RUN: %clangxx_asan -O0 -g %s -o %t.executable +// RUN: %env_asan_opts="symbolize=0" not %run %t.executable > %t_no_module_map.log 2>&1 +// RUN: %asan_symbolize --force-system-symbolizer < %t_no_module_map.log 2>&1 | FileCheck %s +#include +#include +#include +#include + +// This test is deliberately convoluted so that there is a function call +// in the stack trace that contains nested parentheses. + +template +class IntWrapper { + int value_; + std::function callback_; + +public: + IntWrapper(int value, std::function callback) : value_(value), callback_(callback) {} + int &operator=(const int &new_value) { + value_ = new_value; + callback_(value_); + } +}; + +using IntW = IntWrapper; +IntW *a; + +template +void writeToA(T new_value) { + // CHECK: heap-use-after-free + // NOTE: atos seems to emit the `void` return type here for some reason. + // CHECK: #{{[0-9]+}} 0x{{.+}} in {{(void +)?}}writeToA{{ *}}>(IntWrapper) asan-symbolize-templated-cxx.cpp:[[@LINE+1]] + *a = new_value; +} + +extern "C" void callback(int new_value) { + printf("new value is %d\n", new_value); +} + +template +struct Foo { + std::function call; + Foo(std::function c) : call(c) {} + void doCall(V new_value) { + // CHECK: #{{[0-9]+}} 0x{{.+}} in Foo),{{ *}}IntWrapper{{ *}}>::doCall(IntWrapper) asan-symbolize-templated-cxx.cpp:[[@LINE+1]] + call(new_value); + } +}; + +int main() { + a = new IntW(0, callback); + assert(a); + // Foo)> + // This type is deliberately convoluted so that the demangled type contains nested parentheses. + // In particular trying to match parentheses using a least-greedy regex approach will fail. + Foo foo(writeToA); + delete a; + // CHECK: #{{[0-9]+}} 0x{{.+}} in main asan-symbolize-templated-cxx.cpp:[[@LINE+1]] + foo.doCall(IntW(5, callback)); // BOOM + return 0; +}