summaryrefslogtreecommitdiff
path: root/lib/asan
diff options
context:
space:
mode:
authorKostya Serebryany <kcc@google.com>2013-03-22 10:36:24 +0000
committerKostya Serebryany <kcc@google.com>2013-03-22 10:36:24 +0000
commit50f3daa00d3da0a80c8798a3e977705e96ec106f (patch)
tree2b24c9e4a1844b12a8b5a8e689e8a16b08333469 /lib/asan
parent6e5ff89e8980b7c2ca0a39811433fb7ac2c74372 (diff)
[asan] Change the way we report the alloca frame on stack-buff-overflow.
Before: the function name was stored by the compiler as a constant string and the run-time was printing it. Now: the PC is stored instead and the run-time prints the full symbolized frame. This adds a couple of instructions into every function with non-empty stack frame, but also reduces the binary size because we store less strings (I saw 2% size reduction). This change bumps the asan ABI version to v3. compiler-rt part, llvm part will follow. Example of report (now): ==31711==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffa77cf1c5 at pc 0x41feb0 bp 0x7fffa77cefb0 sp 0x7fffa77cefa8 READ of size 1 at 0x7fffa77cf1c5 thread T0 #0 0x41feaf in Frame0(int, char*, char*, char*) stack-oob-frames.cc:20 #1 0x41f7ff in Frame1(int, char*, char*) stack-oob-frames.cc:24 #2 0x41f477 in Frame2(int, char*) stack-oob-frames.cc:28 #3 0x41f194 in Frame3(int) stack-oob-frames.cc:32 #4 0x41eee0 in main stack-oob-frames.cc:38 #5 0x7f0c5566f76c (/lib/x86_64-linux-gnu/libc.so.6+0x2176c) #6 0x41eb1c (/usr/local/google/kcc/llvm_cmake/a.out+0x41eb1c) Address 0x7fffa77cf1c5 is located in stack of thread T0 at offset 293 in frame #0 0x41f87f in Frame0(int, char*, char*, char*) stack-oob-frames.cc:12 <<<<<<<<<<<<<< this is new This frame has 6 object(s): [32, 36) 'frame.addr' [96, 104) 'a.addr' [160, 168) 'b.addr' [224, 232) 'c.addr' [288, 292) 's' [352, 360) 'd' git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@177723 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/asan')
-rw-r--r--lib/asan/asan_interface_internal.h9
-rw-r--r--lib/asan/asan_report.cc74
-rw-r--r--lib/asan/asan_thread.cc5
-rw-r--r--lib/asan/asan_thread.h2
-rw-r--r--lib/asan/lit_tests/Linux/zero-base-shadow.cc3
-rw-r--r--lib/asan/lit_tests/stack-frame-demangle.cc12
-rw-r--r--lib/asan/lit_tests/stack-oob-frames.cc59
-rw-r--r--lib/asan/lit_tests/stack-overflow.cc3
-rw-r--r--lib/asan/lit_tests/use-after-scope-inlined.cc5
-rw-r--r--lib/asan/tests/asan_test.cc4
10 files changed, 128 insertions, 48 deletions
diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h
index 8288d0cb2..d0f3ba958 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 c2c6d86bc..ba73055da 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<sptr>(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 2ef4b2f3c..8ed8875ae 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 b141775fa..084cb30c1 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 d6ea1aa02..eefaf7e22 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 <main>}}
+ // 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 a0de4bbc2..bb8de16b2 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 <string.h>
@@ -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 <XXX::YYY::ZZZ(.*)>}}
+ // 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 000000000..039552225
--- /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 3deb1e91d..eb55315ea 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 <main>}}
+ // 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 3d730de6a..5c121ea18 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 <main> 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 9dccd6664..64d70a35e 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.