Skip to content
This repository was archived by the owner on Apr 23, 2020. It is now read-only.

Commit 3cb1404

Browse files
committed
[asan_symbolize] Fix bug where the frame counter was not incremented.
Summary: This bug occurred when a plug-in requested that a binary not be symbolized while the script is trying to symbolize a stack frame. In this case `self.frame_no` would not be incremented. This would cause subsequent stack frames that are symbolized to be incorrectly numbered. To fix this `get_symbolized_lines()` has been modified to take an argument that indicates whether the stack frame counter should incremented. In `process_line_posix()` `get_symbolized_lines(None, ...)` is now used in in the case where we don't want to symbolize a line so that we can keep the frame counter increment in a single function. A test case is included. The test uses a dummy plugin that always asks `asan_symbolize.py` script to not symbolize the first binary that the script asks about. Prior to the patch this would cause the output to script to look something like ``` #0 0x0 #0 0x0 in do_access #1 0x0 in main ``` This is the second attempt at landing this patch. The first (r368373) failed due to failing some android bots and so was reverted in r368472. The new test is now disabled for Android. It turns out that the patch also fails for iOS too so it is also disabled for that family of platforms too. rdar://problem/49476995 Reviewers: kubamracek, yln, samsonov, dvyukov, vitalybuka Subscribers: #sanitizers, llvm-commits Tags: #llvm, #sanitizers Differential Revision: https://reviews.llvm.org/D65495 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@368603 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent 4e6f406 commit 3cb1404

File tree

3 files changed

+90
-4
lines changed

3 files changed

+90
-4
lines changed

lib/asan/scripts/asan_symbolize.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -431,10 +431,13 @@ def symbolize_address(self, addr, binary, offset, arch):
431431
assert result
432432
return result
433433

434-
def get_symbolized_lines(self, symbolized_lines):
434+
def get_symbolized_lines(self, symbolized_lines, inc_frame_counter=True):
435435
if not symbolized_lines:
436+
if inc_frame_counter:
437+
self.frame_no += 1
436438
return [self.current_line]
437439
else:
440+
assert inc_frame_counter
438441
result = []
439442
for symbolized_frame in symbolized_lines:
440443
result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstrip()))
@@ -464,15 +467,17 @@ def process_line_posix(self, line):
464467
match = re.match(stack_trace_line_format, line)
465468
if not match:
466469
logging.debug('Line "{}" does not match regex'.format(line))
467-
return [self.current_line]
470+
# Not a frame line so don't increment the frame counter.
471+
return self.get_symbolized_lines(None, inc_frame_counter=False)
468472
logging.debug(line)
469473
_, frameno_str, addr, binary, offset = match.groups()
474+
470475
if not self.using_module_map and not os.path.isabs(binary):
471476
# Do not try to symbolicate if the binary is just the module file name
472477
# and a module map is unavailable.
473478
# FIXME(dliew): This is currently necessary for reports on Darwin that are
474479
# partially symbolicated by `atos`.
475-
return [self.current_line]
480+
return self.get_symbolized_lines(None)
476481
arch = ""
477482
# Arch can be embedded in the filename, e.g.: "libabc.dylib:x86_64h"
478483
colon_pos = binary.rfind(":")
@@ -491,7 +496,7 @@ def process_line_posix(self, line):
491496
if binary is None:
492497
# The binary filter has told us this binary can't be symbolized.
493498
logging.debug('Skipping symbolication of binary "%s"', original_binary)
494-
return [self.current_line]
499+
return self.get_symbolized_lines(None)
495500
symbolized_line = self.symbolize_address(addr, binary, offset, arch)
496501
if not symbolized_line:
497502
if original_binary != binary:
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// This test case checks for an old bug when using plug-ins that caused
2+
// the stack numbering to be incorrect.
3+
// UNSUPPORTED: android
4+
// UNSUPPORTED: ios
5+
6+
// RUN: %clangxx_asan -O0 -g %s -o %t
7+
// RUN: %env_asan_opts=symbolize=0 not %run %t DUMMY_ARG > %t.asan_report 2>&1
8+
// RUN: %asan_symbolize --log-level debug --log-dest %t_debug_log_output.txt -l %t.asan_report --plugins %S/plugin_wrong_frame_number_bug.py > %t.asan_report_sym
9+
// RUN: FileCheck --input-file=%t.asan_report_sym %s
10+
11+
#include <stdlib.h>
12+
13+
int* p;
14+
extern "C" {
15+
16+
void bug() {
17+
free(p);
18+
}
19+
20+
void foo(bool call_bug) {
21+
if (call_bug)
22+
bug();
23+
}
24+
25+
// This indirection exists so that the call stack
26+
// is reliably large enough.
27+
void do_access_impl() {
28+
*p = 42;
29+
}
30+
31+
void do_access() {
32+
do_access_impl();
33+
}
34+
35+
int main(int argc, char** argv) {
36+
p = (int*) malloc(sizeof(p));
37+
foo(argc > 1);
38+
do_access();
39+
free(p);
40+
return 0;
41+
}
42+
}
43+
44+
// Check that the numbering of the stackframes is correct.
45+
46+
// CHECK: AddressSanitizer: heap-use-after-free
47+
// CHECK-NEXT: WRITE of size
48+
// CHECK-NEXT: #0 0x{{[0-9a-fA-F]+}}
49+
// CHECK-NEXT: #1 0x{{[0-9a-fA-F]+}} in do_access
50+
// CHECK-NEXT: #2 0x{{[0-9a-fA-F]+}} in main
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import logging
2+
3+
class FailOncePlugin(AsanSymbolizerPlugIn):
4+
"""
5+
This is a simple plug-in that always claims
6+
that a binary can't be symbolized on the first
7+
call but succeeds for all subsequent calls.
8+
9+
This plug-in exists to reproduce an old bug
10+
in the `asan_symbolize.py` script.
11+
12+
By failing the first symbolization request
13+
we used to cause an early exit in `asan_symbolize.py`
14+
that didn't increment the frame counter which
15+
caused subsequent symbolization attempts to
16+
print the wrong frame number.
17+
"""
18+
def __init__(self):
19+
self.should_fail = True
20+
pass
21+
22+
def filter_binary_path(self, path):
23+
logging.info('filter_binary_path called in NoOpPlugin')
24+
if self.should_fail:
25+
logging.info('Doing first fail')
26+
self.should_fail = False
27+
return None
28+
logging.info('Doing succeed')
29+
return path
30+
31+
register_plugin(FailOncePlugin())

0 commit comments

Comments
 (0)