Skip to content

Commit 5811f3a

Browse files
committed
[asan_symbolize] Fix bug handling C++ symbols when using Atos.
Summary: The previous code tries to strip out parentheses and anything in between them. I'm guessing the idea here was to try to drop any listed arguments for the function being symbolized. Unfortunately this approach is broken in several ways. * Templated functions may contain parentheses. The existing approach messes up these names. * In C++ argument types are part of a function's signature for the purposes of overloading so removing them could be confusing. Fix this simply by not trying to adjust the function name that comes from `atos`. A test case is included. Without the change the test case produced output like: ``` WRITE of size 4 at 0x6060000001a0 thread T0 #0 0x10b96614d in IntWrapper<void >::operator=> const&) asan-symbolize-templated-cxx.cpp:10 #1 0x10b960b0e in void writeToA<IntWrapper<void > >>) asan-symbolize-templated-cxx.cpp:30 rust-lang#2 0x10b96bf27 in decltype>)>> >)) std::__1::__invoke<void >), IntWrapper<void > >>), IntWrapper<void >&&) type_traits:4425 rust-lang#3 0x10b96bdc1 in void std::__1::__invoke_void_return_wrapper<void>::__call<void >), IntWrapper<void > >>), IntWrapper<void >&&) __functional_base:348 rust-lang#4 0x10b96bd71 in std::__1::__function::__alloc_func<void >), std::__1::allocator<void >)>, void >)>::operator>&&) functional:1533 rust-lang#5 0x10b9684e2 in std::__1::__function::__func<void >), std::__1::allocator<void >)>, void >)>::operator>&&) functional:1707 rust-lang#6 0x10b96cd7b in std::__1::__function::__value_func<void >)>::operator>&&) const functional:1860 rust-lang#7 0x10b96cc17 in std::__1::function<void >)>::operator>) const functional:2419 rust-lang#8 0x10b960ca6 in Foo<void >), IntWrapper<void > >::doCall>) asan-symbolize-templated-cxx.cpp:44 rust-lang#9 0x10b96088b in main asan-symbolize-templated-cxx.cpp:54 rust-lang#10 0x7fff6ffdfcc8 in start (in libdyld.dylib) + 0 ``` Note how the symbol names for the frames are messed up (e.g. rust-lang#8, #1). With the patch the output looks like: ``` WRITE of size 4 at 0x6060000001a0 thread T0 #0 0x10005214d in IntWrapper<void (int)>::operator=(IntWrapper<void (int)> const&) asan-symbolize-templated-cxx.cpp:10 #1 0x10004cb0e in void writeToA<IntWrapper<void (int)> >(IntWrapper<void (int)>) asan-symbolize-templated-cxx.cpp:30 rust-lang#2 0x100057f27 in decltype(std::__1::forward<void (*&)(IntWrapper<void (int)>)>(fp)(std::__1::forward<IntWrapper<void (int)> >(fp0))) std::__1::__invoke<void (*&)(IntWrapper<void (int)>), IntWrapper<void (int)> >(void (*&)(IntWrapper<void (int)>), IntWrapper<void (int)>&&) type_traits:4425 rust-lang#3 0x100057dc1 in void std::__1::__invoke_void_return_wrapper<void>::__call<void (*&)(IntWrapper<void (int)>), IntWrapper<void (int)> >(void (*&)(IntWrapper<void (int)>), IntWrapper<void (int)>&&) __functional_base:348 rust-lang#4 0x100057d71 in std::__1::__function::__alloc_func<void (*)(IntWrapper<void (int)>), std::__1::allocator<void (*)(IntWrapper<void (int)>)>, void (IntWrapper<void (int)>)>::operator()(IntWrapper<void (int)>&&) functional:1533 rust-lang#5 0x1000544e2 in std::__1::__function::__func<void (*)(IntWrapper<void (int)>), std::__1::allocator<void (*)(IntWrapper<void (int)>)>, void (IntWrapper<void (int)>)>::operator()(IntWrapper<void (int)>&&) functional:1707 rust-lang#6 0x100058d7b in std::__1::__function::__value_func<void (IntWrapper<void (int)>)>::operator()(IntWrapper<void (int)>&&) const functional:1860 rust-lang#7 0x100058c17 in std::__1::function<void (IntWrapper<void (int)>)>::operator()(IntWrapper<void (int)>) const functional:2419 rust-lang#8 0x10004cca6 in Foo<void (IntWrapper<void (int)>), IntWrapper<void (int)> >::doCall(IntWrapper<void (int)>) asan-symbolize-templated-cxx.cpp:44 rust-lang#9 0x10004c88b in main asan-symbolize-templated-cxx.cpp:54 rust-lang#10 0x7fff6ffdfcc8 in start (in libdyld.dylib) + 0 ``` rdar://problem/58887175 Reviewers: kubamracek, yln Subscribers: #sanitizers, llvm-commits Tags: #sanitizers Differential Revision: https://reviews.llvm.org/D79597
1 parent f2be30d commit 5811f3a

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

compiler-rt/lib/asan/scripts/asan_symbolize.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,14 @@ def symbolize(self, addr, binary, offset):
275275
atos_line = self.atos.readline()
276276
# A well-formed atos response looks like this:
277277
# foo(type1, type2) (in object.name) (filename.cc:80)
278+
# NOTE:
279+
# * For C functions atos omits parentheses and argument types.
280+
# * For C++ functions the function name (i.e., `foo` above) may contain
281+
# templates which may contain parentheses.
278282
match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line)
279283
logging.debug('atos_line: %s', atos_line)
280284
if match:
281285
function_name = match.group(1)
282-
function_name = re.sub('\(.*?\)', '', function_name)
283286
file_name = fix_filename(match.group(3))
284287
return ['%s in %s %s' % (addr, function_name, file_name)]
285288
else:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// UNSUPPORTED: ios
2+
// RUN: %clangxx_asan -O0 -g %s -o %t.executable
3+
// RUN: %env_asan_opts="symbolize=0" not %run %t.executable > %t_no_module_map.log 2>&1
4+
// RUN: %asan_symbolize --force-system-symbolizer < %t_no_module_map.log 2>&1 | FileCheck %s
5+
#include <cassert>
6+
#include <cstdio>
7+
#include <cstdlib>
8+
#include <functional>
9+
10+
// This test is deliberately convoluted so that there is a function call
11+
// in the stack trace that contains nested parentheses.
12+
13+
template <class CallBackTy>
14+
class IntWrapper {
15+
int value_;
16+
std::function<CallBackTy> callback_;
17+
18+
public:
19+
IntWrapper(int value, std::function<CallBackTy> callback) : value_(value), callback_(callback) {}
20+
int &operator=(const int &new_value) {
21+
value_ = new_value;
22+
callback_(value_);
23+
}
24+
};
25+
26+
using IntW = IntWrapper<void(int)>;
27+
IntW *a;
28+
29+
template <class T>
30+
void writeToA(T new_value) {
31+
// CHECK: heap-use-after-free
32+
// NOTE: atos seems to emit the `void` return type here for some reason.
33+
// CHECK: #{{[0-9]+}} 0x{{.+}} in {{(void +)?}}writeToA<IntWrapper<void{{ *}}(int)>{{ *}}>(IntWrapper<void{{ *}}(int)>) asan-symbolize-templated-cxx.cpp:[[@LINE+1]]
34+
*a = new_value;
35+
}
36+
37+
extern "C" void callback(int new_value) {
38+
printf("new value is %d\n", new_value);
39+
}
40+
41+
template <class T, class V>
42+
struct Foo {
43+
std::function<T> call;
44+
Foo(std::function<T> c) : call(c) {}
45+
void doCall(V new_value) {
46+
// CHECK: #{{[0-9]+}} 0x{{.+}} in Foo<void (IntWrapper<void{{ *}}(int)>),{{ *}}IntWrapper<void{{ *}}(int)>{{ *}}>::doCall(IntWrapper<void{{ *}}(int)>) asan-symbolize-templated-cxx.cpp:[[@LINE+1]]
47+
call(new_value);
48+
}
49+
};
50+
51+
int main() {
52+
a = new IntW(0, callback);
53+
assert(a);
54+
// Foo<void(IntWrapper<void(int)>)>
55+
// This type is deliberately convoluted so that the demangled type contains nested parentheses.
56+
// In particular trying to match parentheses using a least-greedy regex approach will fail.
57+
Foo<void(IntW), IntW> foo(writeToA<IntW>);
58+
delete a;
59+
// CHECK: #{{[0-9]+}} 0x{{.+}} in main asan-symbolize-templated-cxx.cpp:[[@LINE+1]]
60+
foo.doCall(IntW(5, callback)); // BOOM
61+
return 0;
62+
}

0 commit comments

Comments
 (0)