diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h index 8288d0cb23..d0f3ba9582 100644 --- a/lib/asan/asan_interface_internal.h +++ b/lib/asan/asan_interface_internal.h @@ -25,8 +25,13 @@ extern "C" { // Everytime the asan ABI changes we also change the version number in this // name. Objects build with incompatible asan ABI version // will not link with run-time. - void __asan_init_v2() SANITIZER_INTERFACE_ATTRIBUTE; - #define __asan_init __asan_init_v2 + // Changes between ABI versions: + // v1=>v2: added 'module_name' to __asan_global + // v2=>v3: stack frame description (created by the compiler) + // contains the function PC as the 3-rd field (see + // DescribeAddressIfStack). + void __asan_init_v3() SANITIZER_INTERFACE_ATTRIBUTE; + #define __asan_init __asan_init_v3 // This structure describes an instrumented global variable. struct __asan_global { diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index c2c6d86bcd..ba73055da5 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -235,33 +235,61 @@ bool DescribeAddressIfShadow(uptr addr) { return false; } +// Return " (thread_name) " or an empty string if the name is empty. +const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], + uptr buff_len) { + const char *name = t->name; + if (name[0] == '\0') return ""; + buff[0] = 0; + internal_strncat(buff, " (", 3); + internal_strncat(buff, name, buff_len - 4); + internal_strncat(buff, ")", 2); + return buff; +} + +const char *ThreadNameWithParenthesis(u32 tid, char buff[], + uptr buff_len) { + if (tid == kInvalidTid) return ""; + asanThreadRegistry().CheckLocked(); + AsanThreadContext *t = GetThreadContextByTidLocked(tid); + return ThreadNameWithParenthesis(t, buff, buff_len); +} + bool DescribeAddressIfStack(uptr addr, uptr access_size) { AsanThread *t = FindThreadByStackAddress(addr); if (!t) return false; const sptr kBufSize = 4095; char buf[kBufSize]; uptr offset = 0; - const char *frame_descr = t->GetFrameNameByAddr(addr, &offset); + uptr frame_pc = 0; + char tname[128]; + const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc); // This string is created by the compiler and has the following form: - // "FunctioName n alloc_1 alloc_2 ... alloc_n" + // "n alloc_1 alloc_2 ... alloc_n" // where alloc_i looks like "offset size len ObjectName ". CHECK(frame_descr); - // Report the function name and the offset. - const char *name_end = internal_strchr(frame_descr, ' '); - CHECK(name_end); - buf[0] = 0; - internal_strncat(buf, frame_descr, - Min(kBufSize, - static_cast(name_end - frame_descr))); Decorator d; Printf("%s", d.Location()); - Printf("Address %p is located at offset %zu " - "in frame <%s> of T%d's stack:\n", - (void*)addr, offset, Demangle(buf), t->tid()); + Printf("Address %p is located in stack of thread T%d%s " + "at offset %zu in frame\n", + addr, t->tid(), + ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)), + offset); + // Now we print the frame where the alloca has happened. + // We print this frame as a stack trace with one element. + // The symbolizer may print more than one frame if inlining was involved. + // The frame numbers may be different than those in the stack trace printed + // previously. That's unfortunate, but I have no better solution, + // especially given that the alloca may be from entirely different place + // (e.g. use-after-scope, or different thread's stack). + StackTrace alloca_stack; + alloca_stack.trace[0] = frame_pc + 16; + alloca_stack.size = 1; Printf("%s", d.EndLocation()); + PrintStack(&alloca_stack); // Report the number of stack objects. char *p; - uptr n_objects = internal_simple_strtoll(name_end, &p, 10); + uptr n_objects = internal_simple_strtoll(frame_descr, &p, 10); CHECK(n_objects > 0); Printf(" This frame has %zu object(s):\n", n_objects); // Report all objects in this frame. @@ -313,26 +341,6 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, Printf("%s", d.EndLocation()); } -// Return " (thread_name) " or an empty string if the name is empty. -const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[], - uptr buff_len) { - const char *name = t->name; - if (name[0] == '\0') return ""; - buff[0] = 0; - internal_strncat(buff, " (", 3); - internal_strncat(buff, name, buff_len - 4); - internal_strncat(buff, ")", 2); - return buff; -} - -const char *ThreadNameWithParenthesis(u32 tid, char buff[], - uptr buff_len) { - if (tid == kInvalidTid) return ""; - asanThreadRegistry().CheckLocked(); - AsanThreadContext *t = GetThreadContextByTidLocked(tid); - return ThreadNameWithParenthesis(t, buff, buff_len); -} - void DescribeHeapAddress(uptr addr, uptr access_size) { AsanChunkView chunk = FindHeapChunkByAddress(addr); if (!chunk.IsValid()) return; diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc index 2ef4b2f3c7..8ed8875aea 100644 --- a/lib/asan/asan_thread.cc +++ b/lib/asan/asan_thread.cc @@ -151,7 +151,8 @@ void AsanThread::ClearShadowForThreadStack() { PoisonShadow(stack_bottom_, stack_top_ - stack_bottom_, 0); } -const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { +const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset, + uptr *frame_pc) { uptr bottom = 0; if (AddrIsInStack(addr)) { bottom = stack_bottom(); @@ -159,6 +160,7 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { bottom = fake_stack().AddrIsInFakeStack(addr); CHECK(bottom); *offset = addr - bottom; + *frame_pc = ((uptr*)bottom)[2]; return (const char *)((uptr*)bottom)[1]; } uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr. @@ -183,6 +185,7 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset) { uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1)); CHECK(ptr[0] == kCurrentStackFrameMagic); *offset = addr - (uptr)ptr; + *frame_pc = ptr[2]; return (const char*)ptr[1]; } diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h index b141775fab..084cb30c1f 100644 --- a/lib/asan/asan_thread.h +++ b/lib/asan/asan_thread.h @@ -66,7 +66,7 @@ class AsanThread { AsanThreadContext *context() { return context_; } void set_context(AsanThreadContext *context) { context_ = context; } - const char *GetFrameNameByAddr(uptr addr, uptr *offset); + const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc); bool AddrIsInStack(uptr addr) { return addr >= stack_bottom_ && addr < stack_top_; diff --git a/lib/asan/lit_tests/Linux/zero-base-shadow.cc b/lib/asan/lit_tests/Linux/zero-base-shadow.cc index d6ea1aa02c..eefaf7e22f 100644 --- a/lib/asan/lit_tests/Linux/zero-base-shadow.cc +++ b/lib/asan/lit_tests/Linux/zero-base-shadow.cc @@ -18,7 +18,8 @@ int main(int argc, char **argv) { int res = x[argc * 10]; // BOOOM // CHECK: {{READ of size 1 at 0x.* thread T0}} // CHECK: {{ #0 0x.* in _?main .*zero-base-shadow.cc:}}[[@LINE-2]] - // CHECK: {{Address 0x.* is .* frame
}} + // CHECK: {{Address 0x.* is .* frame}} + // CHECK: main // Check that shadow for stack memory occupies lower part of address space. // CHECK-64: =>0x0f{{.*}} diff --git a/lib/asan/lit_tests/stack-frame-demangle.cc b/lib/asan/lit_tests/stack-frame-demangle.cc index a0de4bbc24..bb8de16b2b 100644 --- a/lib/asan/lit_tests/stack-frame-demangle.cc +++ b/lib/asan/lit_tests/stack-frame-demangle.cc @@ -1,7 +1,4 @@ -// Check that ASan is able to print demangled frame name even w/o -// symbolization. - -// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -m64 -O0 %s -o %t && %t 2>&1 | %symbolize | FileCheck %s #include @@ -11,9 +8,10 @@ struct YYY { char array[10]; memset(array, 0, 10); return array[x]; // BOOOM - // CHECK: {{ERROR: AddressSanitizer: stack-buffer-overflow}} - // CHECK: {{READ of size 1 at 0x.* thread T0}} - // CHECK: {{Address 0x.* is .* frame }} + // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow + // CHECK: READ of size 1 at + // CHECK: is located in stack of thread T0 at offset + // CHECK: XXX::YYY::ZZZ } }; } // namespace XXX diff --git a/lib/asan/lit_tests/stack-oob-frames.cc b/lib/asan/lit_tests/stack-oob-frames.cc new file mode 100644 index 0000000000..0395522252 --- /dev/null +++ b/lib/asan/lit_tests/stack-oob-frames.cc @@ -0,0 +1,59 @@ +// RUN: %clangxx_asan -m64 -O1 %s -o %t +// RUN: %t 0 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK0 +// RUN: %t 1 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK1 +// RUN: %t 2 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK2 +// RUN: %t 3 2>&1 | %symbolize | FileCheck %s --check-prefix=CHECK3 + +#define NOINLINE __attribute__((noinline)) +inline void break_optimization(void *arg) { + __asm__ __volatile__("" : : "r" (arg) : "memory"); +} + +NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { + char s[4] = {0}; + char *d = s; + break_optimization(&d); + switch (frame) { + case 3: a[5]++; break; + case 2: b[5]++; break; + case 1: c[5]++; break; + case 0: d[5]++; break; + } +} +NOINLINE static void Frame1(int frame, char *a, char *b) { + char c[4] = {0}; Frame0(frame, a, b, c); + break_optimization(0); +} +NOINLINE static void Frame2(int frame, char *a) { + char b[4] = {0}; Frame1(frame, a, b); + break_optimization(0); +} +NOINLINE static void Frame3(int frame) { + char a[4] = {0}; Frame2(frame, a); + break_optimization(0); +} + +int main(int argc, char **argv) { + if (argc != 2) return 1; + Frame3(argv[1][0] - '0'); +} + +// CHECK0: AddressSanitizer: stack-buffer-overflow +// CHECK0: #0{{.*}}Frame0 +// CHECK0: #1{{.*}}Frame1 +// CHECK0: #2{{.*}}Frame2 +// CHECK0: #3{{.*}}Frame3 +// CHECK0: is located in stack of thread T0 at offset +// CHECK0-NEXT: #0{{.*}}Frame0 +// +// CHECK1: AddressSanitizer: stack-buffer-overflow +// CHECK1: is located in stack of thread T0 at offset +// CHECK1-NEXT: #0{{.*}}Frame1 +// +// CHECK2: AddressSanitizer: stack-buffer-overflow +// CHECK2: is located in stack of thread T0 at offset +// CHECK2-NEXT: #0{{.*}}Frame2 +// +// CHECK3: AddressSanitizer: stack-buffer-overflow +// CHECK3: is located in stack of thread T0 at offset +// CHECK3-NEXT: #0{{.*}}Frame3 diff --git a/lib/asan/lit_tests/stack-overflow.cc b/lib/asan/lit_tests/stack-overflow.cc index 3deb1e91de..eb55315eaf 100644 --- a/lib/asan/lit_tests/stack-overflow.cc +++ b/lib/asan/lit_tests/stack-overflow.cc @@ -14,6 +14,7 @@ int main(int argc, char **argv) { int res = x[argc * 10]; // BOOOM // CHECK: {{READ of size 1 at 0x.* thread T0}} // CHECK: {{ #0 0x.* in _?main .*stack-overflow.cc:}}[[@LINE-2]] - // CHECK: {{Address 0x.* is .* frame
}} + // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}} + // CHECK: main return res; } diff --git a/lib/asan/lit_tests/use-after-scope-inlined.cc b/lib/asan/lit_tests/use-after-scope-inlined.cc index 3d730de6ab..5c121ea187 100644 --- a/lib/asan/lit_tests/use-after-scope-inlined.cc +++ b/lib/asan/lit_tests/use-after-scope-inlined.cc @@ -23,7 +23,8 @@ int main(int argc, char *argv[]) { // CHECK: READ of size 4 at 0x{{.*}} thread T0 // CHECK: #0 0x{{.*}} in {{_?}}main // CHECK: {{.*}}use-after-scope-inlined.cc:[[@LINE-4]] - // CHECK: Address 0x{{.*}} is located at offset - // CHECK: [[OFFSET:[^ ]*]] in frame
of T0{{.*}}: + // CHECK: Address 0x{{.*}} is located in stack of thread T0 at offset + // CHECK: [[OFFSET:[^ ]*]] in frame + // CHECK: main // CHECK: {{\[}}[[OFFSET]], {{.*}}) 'x.i' } diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc index 9dccd66647..64d70a35ec 100644 --- a/lib/asan/tests/asan_test.cc +++ b/lib/asan/tests/asan_test.cc @@ -465,6 +465,9 @@ TEST(AddressSanitizer, ManyStackObjectsTest) { EXPECT_DEATH(Ident(ZZZ)[-1] = 0, ASAN_PCRE_DOTALL "XXX.*YYY.*ZZZ"); } +#if 0 // This test requires online symbolizer. +// Moved to lit_tests/stack-oob-frames.cc. +// Reenable here once we have online symbolizer by default. NOINLINE static void Frame0(int frame, char *a, char *b, char *c) { char d[4] = {0}; char *D = Ident(d); @@ -500,6 +503,7 @@ TEST(AddressSanitizer, GuiltyStackFrame2Test) { TEST(AddressSanitizer, GuiltyStackFrame3Test) { EXPECT_DEATH(Frame3(3), "located .*in frame <.*Frame3"); } +#endif NOINLINE void LongJmpFunc1(jmp_buf buf) { // create three red zones for these two stack objects.