From 17003a1299e8670aaf77a567e1c2b830d398b9fc Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Mon, 24 Sep 2018 23:03:34 +0000 Subject: [hwasan] Record and display stack history in stack-based reports. Summary: Display a list of recent stack frames (not a stack trace!) when tag-mismatch is detected on a stack address. The implementation uses alignment tricks to get both the address of the history buffer, and the base address of the shadow with a single 8-byte load. See the comment in hwasan_thread_list.h for more details. Developed in collaboration with Kostya Serebryany. Reviewers: kcc Subscribers: srhines, kubamracek, mgorny, hiraditya, jfb, llvm-commits Differential Revision: https://reviews.llvm.org/D52249 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@342923 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/hwasan/TestCases/deep-recursion.c | 73 ++++++++++++++++++++++++++++ test/hwasan/TestCases/rich-stack.c | 66 +++++++++++++++++++++++++ test/hwasan/TestCases/stack-history-length.c | 36 ++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 test/hwasan/TestCases/deep-recursion.c create mode 100644 test/hwasan/TestCases/rich-stack.c create mode 100644 test/hwasan/TestCases/stack-history-length.c (limited to 'test/hwasan/TestCases') diff --git a/test/hwasan/TestCases/deep-recursion.c b/test/hwasan/TestCases/deep-recursion.c new file mode 100644 index 000000000..1ac0f5b15 --- /dev/null +++ b/test/hwasan/TestCases/deep-recursion.c @@ -0,0 +1,73 @@ +// RUN: %clang_hwasan -O1 %s -o %t +// RUN: %env_hwasan_opts=stack_history_size=1 not %run %t 2>&1 | FileCheck %s --check-prefix=D1 +// RUN: %env_hwasan_opts=stack_history_size=2 not %run %t 2>&1 | FileCheck %s --check-prefix=D2 +// RUN: %env_hwasan_opts=stack_history_size=3 not %run %t 2>&1 | FileCheck %s --check-prefix=D3 +// RUN: %env_hwasan_opts=stack_history_size=5 not %run %t 2>&1 | FileCheck %s --check-prefix=D5 +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=DEFAULT + +// REQUIRES: stable-runtime + +#include +// At least -O1 is needed for this function to not have a stack frame on +// AArch64. +void USE(void *x) { // pretend_to_do_something(void *x) + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +volatile int four = 4; + +__attribute__((noinline)) void OOB() { int x[4]; x[four] = 0; USE(&x[0]); } +__attribute__((noinline)) void FUNC1() { int x; USE(&x); OOB(); } +__attribute__((noinline)) void FUNC2() { int x; USE(&x); FUNC1(); } +__attribute__((noinline)) void FUNC3() { int x; USE(&x); FUNC2(); } +__attribute__((noinline)) void FUNC4() { int x; USE(&x); FUNC3(); } +__attribute__((noinline)) void FUNC5() { int x; USE(&x); FUNC4(); } +__attribute__((noinline)) void FUNC6() { int x; USE(&x); FUNC5(); } +__attribute__((noinline)) void FUNC7() { int x; USE(&x); FUNC6(); } +__attribute__((noinline)) void FUNC8() { int x; USE(&x); FUNC7(); } +__attribute__((noinline)) void FUNC9() { int x; USE(&x); FUNC8(); } +__attribute__((noinline)) void FUNC10() { int x; USE(&x); FUNC9(); } + +int main() { FUNC10(); } + +// D1: Previosly allocated frames +// D1: in OOB +// D1-NOT: in FUNC +// D1: Memory tags around the buggy address + +// D2: Previosly allocated frames +// D2: in OOB +// D2: in FUNC1 +// D2-NOT: in FUNC +// D2: Memory tags around the buggy address + +// D3: Previosly allocated frames +// D3: in OOB +// D3: in FUNC1 +// D3: in FUNC2 +// D3-NOT: in FUNC +// D3: Memory tags around the buggy address + +// D5: Previosly allocated frames +// D5: in OOB +// D5: in FUNC1 +// D5: in FUNC2 +// D5: in FUNC3 +// D5: in FUNC4 +// D5-NOT: in FUNC +// D5: Memory tags around the buggy address + +// DEFAULT: Previosly allocated frames +// DEFAULT: in OOB +// DEFAULT: in FUNC1 +// DEFAULT: in FUNC2 +// DEFAULT: in FUNC3 +// DEFAULT: in FUNC4 +// DEFAULT: in FUNC5 +// DEFAULT: in FUNC6 +// DEFAULT: in FUNC7 +// DEFAULT: in FUNC8 +// DEFAULT: in FUNC9 +// DEFAULT: in FUNC10 +// DEFAULT-NOT: in FUNC +// DEFAULT: Memory tags around the buggy address diff --git a/test/hwasan/TestCases/rich-stack.c b/test/hwasan/TestCases/rich-stack.c new file mode 100644 index 000000000..6787d5776 --- /dev/null +++ b/test/hwasan/TestCases/rich-stack.c @@ -0,0 +1,66 @@ +// Test how stack frames are reported (not fully implemented yet). +// RUN: %clang_hwasan %s -o %t +// RUN: not %run %t 3 2 -1 2>&1 | FileCheck %s --check-prefix=R321 +// REQUIRES: stable-runtime +#include +#include +void USE(void *x) { // pretend_to_do_something(void *x) + __asm__ __volatile__("" : : "r" (x) : "memory"); +} +void USE2(void *a, void *b) { USE(a); USE(b); } +void USE4(void *a, void *b, void *c, void *d) { USE2(a, b); USE2(c, d); } + +void BAR(int depth, int err_depth, int offset); + +uint64_t *leaked_ptr; + +void FOO(int depth, int err_depth, int offset) { + uint8_t v1; + uint16_t v2; + uint32_t v4; + uint64_t v8; + uint64_t v16[2]; + uint64_t v32[4]; + uint64_t v48[3]; + USE4(&v1, &v2, &v4, &v8); USE4(&v16, &v32, &v48, 0); + leaked_ptr = &v16[0]; + if (depth) + BAR(depth - 1, err_depth, offset); + + if (err_depth == depth) + v16[offset] = 0; // maybe OOB. + if (err_depth == -depth) + leaked_ptr[offset] = 0; // maybe UAR. + USE(&v16); +} + +void BAR(int depth, int err_depth, int offset) { + uint64_t x16[2]; + uint64_t x32[4]; + USE2(&x16, &x32); + leaked_ptr = &x16[0]; + if (depth) + FOO(depth - 1, err_depth, offset); + if (err_depth == depth) + x16[offset] = 0; // maybe OOB + if (err_depth == -depth) + leaked_ptr[offset] = 0; // maybe UAR + USE(&x16); +} + + +int main(int argc, char **argv) { + if (argc != 4) return -1; + int depth = atoi(argv[1]); + int err_depth = atoi(argv[2]); + int offset = atoi(argv[3]); + FOO(depth, err_depth, offset); + return 0; +} + +// R321: HWAddressSanitizer: tag-mismatch +// R321-NEXT: WRITE of size 8 +// R321-NEXT: in BAR +// R321-NEXT: in FOO +// R321-NEXT: in main +// R321: is located in stack of thread T0 diff --git a/test/hwasan/TestCases/stack-history-length.c b/test/hwasan/TestCases/stack-history-length.c new file mode 100644 index 000000000..f4c0b036f --- /dev/null +++ b/test/hwasan/TestCases/stack-history-length.c @@ -0,0 +1,36 @@ +// RUN: %clang_hwasan -O1 -DX=2046 %s -o %t.2046 +// RUN: %clang_hwasan -O1 -DX=2047 %s -o %t.2047 +// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t.2046 2>&1 | FileCheck %s --check-prefix=YES +// RUN: %env_hwasan_opts=stack_history_size=2048 not %run %t.2047 2>&1 | FileCheck %s --check-prefix=NO + +// REQUIRES: stable-runtime + +#include + +void USE(void *x) { // pretend_to_do_something(void *x) + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +volatile int four = 4; +__attribute__((noinline)) void FUNC0() { int x[4]; USE(&x[0]); } +__attribute__((noinline)) void FUNC() { int x[4]; USE(&x[0]); } +__attribute__((noinline)) void OOB() { int x[4]; x[four] = 0; USE(&x[0]); } + +int main() { + // FUNC0 is X+2's element of the ring buffer. + // If runtime buffer size is less than it, FUNC0 record will be lost. + FUNC0(); + for (int i = 0; i < X; ++i) + FUNC(); + OOB(); +} + +// YES: Previosly allocated frames +// YES: OOB +// YES: FUNC +// YES: FUNC0 + +// NO: Previosly allocated frames +// NO: OOB +// NO: FUNC +// NO-NOT: FUNC0 -- cgit v1.2.1