diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:05:08 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:05:08 +0000 |
commit | 0646903fc1f75f6e605754621119473ee083f4a4 (patch) | |
tree | 57bce79a7423a054cccec23bdf6cd96e2d271b4a /test/hwasan | |
parent | 005b7ed8f76756d94ef6266ded755ab7863cb936 (diff) | |
download | src-test2-vendor/compiler-rt/compiler-rt-release_80-r351543.tar.gz src-test2-vendor/compiler-rt/compiler-rt-release_80-r351543.zip |
Diffstat (limited to 'test/hwasan')
35 files changed, 1078 insertions, 70 deletions
diff --git a/test/hwasan/CMakeLists.txt b/test/hwasan/CMakeLists.txt index 972c73250cf3..3e397ac067f2 100644 --- a/test/hwasan/CMakeLists.txt +++ b/test/hwasan/CMakeLists.txt @@ -11,6 +11,9 @@ foreach(arch ${HWASAN_TEST_ARCH}) string(TOUPPER ${arch} ARCH_UPPER_CASE) set(CONFIG_NAME ${ARCH_UPPER_CASE}) + # FIXME: Set this. + set(HWASAN_ANDROID_FILES_TO_PUSH []) + configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/${CONFIG_NAME}/lit.site.cfg) diff --git a/test/hwasan/TestCases/Posix/system-allocator-fallback.cc b/test/hwasan/TestCases/Posix/system-allocator-fallback.cc new file mode 100644 index 000000000000..8678d906daec --- /dev/null +++ b/test/hwasan/TestCases/Posix/system-allocator-fallback.cc @@ -0,0 +1,54 @@ +// RUN: %clangxx %s -o %t -ldl +// RUN: %clangxx_hwasan -shared %s -o %t.so -DSHARED_LIB -shared-libsan -Wl,-rpath,%compiler_rt_libdir +// RUN: %env_hwasan_opts=disable_allocator_tagging=0 %run %t + +// The dynamic loader on Android O appears to have a bug where it crashes when +// dlopening DF_1_GLOBAL libraries. +// REQUIRES: android-28 + +#include <stddef.h> + +// Test that allocations made by the system allocator can be realloc'd and freed +// by the hwasan allocator. + +typedef void run_test_fn(void *(*system_malloc)(size_t size)); + +#ifdef SHARED_LIB + +// Call the __sanitizer_ versions of these functions so that the test +// doesn't require the Android dynamic loader. +extern "C" void *__sanitizer_realloc(void *ptr, size_t size); +extern "C" void __sanitizer_free(void *ptr); + +extern "C" run_test_fn run_test; +void run_test(void *(*system_malloc)(size_t size)) { + void *mem = system_malloc(64); + mem = __sanitizer_realloc(mem, 128); + __sanitizer_free(mem); +} + +#else + +#include <dlfcn.h> +#include <stdlib.h> +#include <string> + +int main(int argc, char **argv) { + std::string path = argv[0]; + path += ".so"; + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen(): %s\n", dlerror()); + return 1; + } + + auto run_test = reinterpret_cast<run_test_fn *>(dlsym(lib, "run_test")); + if (!run_test) { + printf("failed dlsym\n"); + return 1; + } + + run_test(malloc); +} + +#endif diff --git a/test/hwasan/TestCases/abort-message-android.cc b/test/hwasan/TestCases/abort-message-android.cc new file mode 100644 index 000000000000..f89b929d454d --- /dev/null +++ b/test/hwasan/TestCases/abort-message-android.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_hwasan -DERR=1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_hwasan -DERR=2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// REQUIRES: android + +#include <stdlib.h> +#include <stdio.h> + +#include <sanitizer/hwasan_interface.h> + +__attribute__((no_sanitize("hwaddress"))) +extern "C" void android_set_abort_message(const char *msg) { + fprintf(stderr, "== abort message start\n%s\n== abort message end\n", msg); +} + +int main() { + __hwasan_enable_allocator_tagging(); + char *volatile p = (char *)malloc(16); + if (ERR==1) { + p[16] = 1; + } else { + free(p); + free(p); + } + // CHECK: ERROR: HWAddressSanitizer: + // CHECK: == abort message start + // CHECK: ERROR: HWAddressSanitizer: + // CHECK: == abort message end +} diff --git a/test/hwasan/TestCases/cfi.cc b/test/hwasan/TestCases/cfi.cc new file mode 100644 index 000000000000..457e29659e77 --- /dev/null +++ b/test/hwasan/TestCases/cfi.cc @@ -0,0 +1,18 @@ +// RUN: %clang_hwasan -fsanitize=cfi -fno-sanitize-trap=cfi -flto -fvisibility=hidden -fuse-ld=lld %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +// REQUIRES: android + +// Smoke test for CFI + HWASAN. + +struct A { + virtual void f(); +}; + +void A::f() {} + +int main() { + // CHECK: control flow integrity check for type {{.*}} failed during cast to unrelated type + A *a = reinterpret_cast<A *>(reinterpret_cast<void *>(&main)); + (void)a; +} diff --git a/test/hwasan/TestCases/deep-recursion.c b/test/hwasan/TestCases/deep-recursion.c new file mode 100644 index 000000000000..2fe77a7bdaaf --- /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 <stdlib.h> +// 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: Previously allocated frames +// D1: in OOB +// D1-NOT: in FUNC +// D1: Memory tags around the buggy address + +// D2: Previously allocated frames +// D2: in OOB +// D2: in FUNC1 +// D2-NOT: in FUNC +// D2: Memory tags around the buggy address + +// D3: Previously allocated frames +// D3: in OOB +// D3: in FUNC1 +// D3: in FUNC2 +// D3-NOT: in FUNC +// D3: Memory tags around the buggy address + +// D5: Previously 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: Previously 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/double-free.c b/test/hwasan/TestCases/double-free.c new file mode 100644 index 000000000000..e97aae6edb4c --- /dev/null +++ b/test/hwasan/TestCases/double-free.c @@ -0,0 +1,23 @@ +// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: stable-runtime + +#include <stdlib.h> +#include <stdio.h> +#include <sanitizer/hwasan_interface.h> + +int main() { + __hwasan_enable_allocator_tagging(); + char * volatile x = (char*)malloc(40); + free(x); + free(x); +// CHECK: ERROR: HWAddressSanitizer: invalid-free on address +// CHECK: tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem) +// CHECK: freed by thread {{.*}} here: +// CHECK: previously allocated here: +// CHECK: Memory tags around the buggy address (one tag corresponds to 16 bytes): +// CHECK: =>{{.*}}[[MEM_TAG]] + fprintf(stderr, "DONE\n"); + __hwasan_disable_allocator_tagging(); +// CHECK-NOT: DONE +} diff --git a/test/hwasan/TestCases/heap-buffer-overflow.c b/test/hwasan/TestCases/heap-buffer-overflow.c new file mode 100644 index 000000000000..9f605b3208d6 --- /dev/null +++ b/test/hwasan/TestCases/heap-buffer-overflow.c @@ -0,0 +1,60 @@ +// RUN: %clang_hwasan %s -o %t +// RUN: not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40-LEFT +// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 40 2>&1 | FileCheck %s --check-prefix=CHECK40-RIGHT +// RUN: not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80-LEFT +// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 80 2>&1 | FileCheck %s --check-prefix=CHECK80-RIGHT +// RUN: not %run %t -30 2>&1 | FileCheck %s --check-prefix=CHECKm30 +// RUN: not %run %t -30 1000000 2>&1 | FileCheck %s --check-prefix=CHECKMm30 +// RUN: not %run %t 1000000 1000000 2>&1 | FileCheck %s --check-prefix=CHECKM + +// Test OOB within the granule. +// Misses the bug when malloc is left-aligned, catches it otherwise. +// RUN: %run %t 31 +// RUN: %env_hwasan_opts=malloc_align_right=2 not %run %t 31 2>&1 | FileCheck %s --check-prefix=CHECK31 + +// RUN: %run %t 30 20 +// RUN: %env_hwasan_opts=malloc_align_right=9 not %run %t 30 20 2>&1 | FileCheck %s --check-prefix=CHECK20-RIGHT8 + +// RUN: %env_hwasan_opts=malloc_align_right=42 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-WRONG-FLAG + +// REQUIRES: stable-runtime + +#include <stdlib.h> +#include <stdio.h> +#include <sanitizer/hwasan_interface.h> + +static volatile char sink; + +int main(int argc, char **argv) { + __hwasan_enable_allocator_tagging(); + int offset = argc < 2 ? 40 : atoi(argv[1]); + int size = argc < 3 ? 30 : atoi(argv[2]); + char * volatile x = (char*)malloc(size); + fprintf(stderr, "base: %p access: %p\n", x, &x[offset]); + sink = x[offset]; + +// CHECK40-LEFT: allocated heap chunk; size: 32 offset: 8 +// CHECK40-LEFT: is located 10 bytes to the right of 30-byte region +// CHECK40-RIGHT: allocated heap chunk; size: 32 offset: +// CHECK40-RIGHT: is located 10 bytes to the right of 30-byte region +// +// CHECK80-LEFT: allocated heap chunk; size: 32 offset: 16 +// CHECK80-LEFT: is located 50 bytes to the right of 30-byte region +// CHECK80-RIGHT: allocated heap chunk; size: 32 offset: +// CHECK80-RIGHT: is located 50 bytes to the right of 30-byte region +// +// CHECKm30: is located 30 bytes to the left of 30-byte region +// +// CHECKMm30: is a large allocated heap chunk; size: 1003520 offset: -30 +// CHECKMm30: is located 30 bytes to the left of 1000000-byte region +// +// CHECKM: is a large allocated heap chunk; size: 1003520 offset: 1000000 +// CHECKM: is located 0 bytes to the right of 1000000-byte region +// +// CHECK31: is located 1 bytes to the right of 30-byte region +// +// CHECK20-RIGHT8: is located 10 bytes to the right of 20-byte region [0x{{.*}}8,0x{{.*}}c) +// +// CHECK-WRONG-FLAG: ERROR: unsupported value of malloc_align_right flag: 42 + free(x); +} diff --git a/test/hwasan/TestCases/hwasan-print-shadow.cc b/test/hwasan/TestCases/hwasan-print-shadow.cc new file mode 100644 index 000000000000..fa6330bbcccd --- /dev/null +++ b/test/hwasan/TestCases/hwasan-print-shadow.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_hwasan -DSIZE=16 -O0 %s -o %t && %run %t 2>&1 | FileCheck %s + +// REQUIRES: stable-runtime + +#include <assert.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sanitizer/hwasan_interface.h> + +int main() { + char *p = (char *)mmap(nullptr, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + assert(p); + + __hwasan_tag_memory(p, 1, 32); + __hwasan_tag_memory(p + 32, 3, 16); + __hwasan_tag_memory(p + 48, 0, 32); + __hwasan_tag_memory(p + 80, 4, 16); + + char *q = (char *)__hwasan_tag_pointer(p, 7); + __hwasan_print_shadow(q + 5, 89 - 5); + // CHECK: HWASan shadow map for {{.*}}5 .. {{.*}}9 (pointer tag 7) + // CHECK-NEXT: {{.*}}0: 1 + // CHECK-NEXT: {{.*}}0: 1 + // CHECK-NEXT: {{.*}}0: 3 + // CHECK-NEXT: {{.*}}0: 0 + // CHECK-NEXT: {{.*}}0: 0 + // CHECK-NEXT: {{.*}}0: 4 +} diff --git a/test/hwasan/TestCases/longjmp.c b/test/hwasan/TestCases/longjmp.c new file mode 100644 index 000000000000..8d847b54b275 --- /dev/null +++ b/test/hwasan/TestCases/longjmp.c @@ -0,0 +1,26 @@ +// RUN: %clang_hwasan -O0 -DNEGATIVE %s -o %t && %run %t 2>&1 +// RUN: %clang_hwasan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: stable-runtime + +#include <stdlib.h> +#include <assert.h> +#include <sanitizer/hwasan_interface.h> + +__attribute__((noinline)) +int f(void *caller_frame) { + int z = 0; + int *volatile p = &z; + // Tag of local is never zero. + assert(__hwasan_tag_pointer(p, 0) != p); +#ifndef NEGATIVE + // This will destroy shadow of "z", and the following load will crash. + __hwasan_handle_longjmp(caller_frame); +#endif + return p[0]; +} + +int main() { + return f(__builtin_frame_address(0)); + // CHECK: READ of size 8 at {{.*}} tags: {{.*}}/00 (ptr/mem) +} diff --git a/test/hwasan/TestCases/malloc-test.c b/test/hwasan/TestCases/malloc-test.c new file mode 100644 index 000000000000..199464b9c300 --- /dev/null +++ b/test/hwasan/TestCases/malloc-test.c @@ -0,0 +1,16 @@ +// Test basic malloc functionality. +// RUN: %clang_hwasan %s -o %t +// RUN: %run %t + +#include <stdlib.h> +#include <assert.h> +#include <sanitizer/hwasan_interface.h> +#include <sanitizer/allocator_interface.h> + +int main() { + __hwasan_enable_allocator_tagging(); + char *a1 = (char*)malloc(0); + assert(a1 != 0); + assert(__sanitizer_get_allocated_size(a1) == 0); + free(a1); +} diff --git a/test/hwasan/TestCases/malloc_fill.cc b/test/hwasan/TestCases/malloc_fill.cc new file mode 100644 index 000000000000..b8513b7e2b73 --- /dev/null +++ b/test/hwasan/TestCases/malloc_fill.cc @@ -0,0 +1,22 @@ +// Check that we fill malloc-ed memory correctly. +// RUN: %clangxx_hwasan %s -o %t +// RUN: %run %t | FileCheck %s +// RUN: %env_hwasan_opts=max_malloc_fill_size=10:malloc_fill_byte=8 %run %t | FileCheck %s --check-prefix=CHECK-10-8 +// RUN: %env_hwasan_opts=max_malloc_fill_size=20:malloc_fill_byte=171 %run %t | FileCheck %s --check-prefix=CHECK-20-ab + +#include <stdio.h> +int main(int argc, char **argv) { + // With asan allocator this makes sure we get memory from mmap. + static const int kSize = 1 << 25; + unsigned char *x = new unsigned char[kSize]; + printf("-"); + for (int i = 0; i <= 32; i++) { + printf("%02x", x[i]); + } + printf("-\n"); + delete [] x; +} + +// CHECK: -bebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebe- +// CHECK-10-8: -080808080808080808080000000000000000000000000000000000000000000000- +// CHECK-20-ab: -abababababababababababababababababababab00000000000000000000000000- diff --git a/test/hwasan/TestCases/many-threads-uaf.c b/test/hwasan/TestCases/many-threads-uaf.c new file mode 100644 index 000000000000..3a79cb37b608 --- /dev/null +++ b/test/hwasan/TestCases/many-threads-uaf.c @@ -0,0 +1,45 @@ +// RUN: %clang_hwasan %s -o %t && not %env_hwasan_opts=verbose_threads=1 %run %t 2>&1 | FileCheck %s +// REQUIRES: stable-runtime + +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +#include <sanitizer/hwasan_interface.h> + +void *BoringThread(void *arg) { + char * volatile x = (char*)malloc(10); + x[5] = 0; + free(x); + return NULL; +} + +// CHECK: Creating : T0 +// CHECK: Creating : T1 +// CHECK: Destroying: T1 +// CHECK: Creating : T1100 +// CHECK: Destroying: T1100 +// CHECK: Creating : T1101 + +void *UAFThread(void *arg) { + char * volatile x = (char*)malloc(10); + fprintf(stderr, "ZZZ %p\n", x); + free(x); + x[5] = 42; + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch on address + // CHECK: WRITE of size 1 + // CHECK: many-threads-uaf.c:[[@LINE-3]] + // CHECK: Thread: T1101 + return NULL; +} + +int main() { + __hwasan_enable_allocator_tagging(); + pthread_t t; + for (int i = 0; i < 1100; i++) { + pthread_create(&t, NULL, BoringThread, NULL); + pthread_join(t, NULL); + } + pthread_create(&t, NULL, UAFThread, NULL); + pthread_join(t, NULL); +} diff --git a/test/hwasan/TestCases/mem-intrinsics-zero-size.c b/test/hwasan/TestCases/mem-intrinsics-zero-size.c new file mode 100644 index 000000000000..bcb8e0771f1e --- /dev/null +++ b/test/hwasan/TestCases/mem-intrinsics-zero-size.c @@ -0,0 +1,10 @@ +// RUN: %clang_hwasan %s -o %t && %run %t + +#include <string.h> + +int main() { + char a[1]; + memset(a, 0, 0); + memmove(a, a, 0); + memcpy(a, a, 0); +} diff --git a/test/hwasan/TestCases/mem-intrinsics.c b/test/hwasan/TestCases/mem-intrinsics.c new file mode 100644 index 000000000000..164bc6d93335 --- /dev/null +++ b/test/hwasan/TestCases/mem-intrinsics.c @@ -0,0 +1,37 @@ +// RUN: %clang_hwasan %s -DTEST_NO=1 -mllvm -hwasan-instrument-mem-intrinsics -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=WRITE +// RUN: %clang_hwasan %s -DTEST_NO=2 -mllvm -hwasan-instrument-mem-intrinsics -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=READ +// RUN: %clang_hwasan %s -DTEST_NO=3 -mllvm -hwasan-instrument-mem-intrinsics -o %t && not %run %t 2>&1 | FileCheck %s --check-prefix=WRITE +// RUN: %clang_hwasan %s -DTEST_NO=2 -mllvm -hwasan-instrument-mem-intrinsics -o %t && not %env_hwasan_opts=halt_on_error=0 %run %t 2>&1 | FileCheck %s --check-prefix=RECOVER + +// REQUIRES: stable-runtime + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int main() { + char Q[16]; + char P[16]; +#if TEST_NO == 1 + memset(Q, 0, 32); +#elif TEST_NO == 2 + memmove(Q, Q + 16, 16); +#elif TEST_NO == 3 + memcpy(Q, P, 32); +#endif + write(STDOUT_FILENO, "recovered\n", 10); + // WRITE: ERROR: HWAddressSanitizer: tag-mismatch on address + // WRITE: WRITE {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem) + // WRITE: Memory tags around the buggy address (one tag corresponds to 16 bytes): + // WRITE: =>{{.*}}[[MEM_TAG]] + // WRITE-NOT: recovered + + // READ: ERROR: HWAddressSanitizer: tag-mismatch on address + // READ: READ {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem) + // READ: Memory tags around the buggy address (one tag corresponds to 16 bytes): + // READ: =>{{.*}}[[MEM_TAG]] + // READ-NOT: recovered + + // RECOVER: recovered + return 0; +} diff --git a/test/hwasan/TestCases/new-test.cc b/test/hwasan/TestCases/new-test.cc new file mode 100644 index 000000000000..3b1991e4deaa --- /dev/null +++ b/test/hwasan/TestCases/new-test.cc @@ -0,0 +1,18 @@ +// Test basic new functionality. +// RUN: %clangxx_hwasan %s -o %t +// RUN: %run %t + +#include <stdlib.h> +#include <assert.h> +#include <sanitizer/hwasan_interface.h> +#include <sanitizer/allocator_interface.h> + +int main() { + __hwasan_enable_allocator_tagging(); + + size_t volatile n = 0; + char *a1 = new char[n]; + assert(a1 != nullptr); + assert(__sanitizer_get_allocated_size(a1) == 0); + delete[] a1; +} diff --git a/test/hwasan/TestCases/print-memory-usage-android.c b/test/hwasan/TestCases/print-memory-usage-android.c new file mode 100644 index 000000000000..5a057928ba75 --- /dev/null +++ b/test/hwasan/TestCases/print-memory-usage-android.c @@ -0,0 +1,21 @@ +// Tests __hwasan_print_memory_usage through /proc/$PID/maps. +// RUN: %clang_hwasan %s -o %t && %env_hwasan_opts=export_memory_stats=1 %run %t 2>&1 | FileCheck %s +// REQUIRES: android + +#include <sys/types.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +// The function needs to be unsanitized in order for &cmd to be untagged. This +// address is passed to system() and then to execve() syscall. The tests need to +// run on unpatched linux kernel, which at this time does not accept tagged +// pointers in system call arguments (but there is hope: see +// https://lore.kernel.org/patchwork/cover/979328). +__attribute__((no_sanitize("hwaddress"))) +int main() { + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "cat /proc/%d/maps", getpid()); + system(cmd); + // CHECK: HWASAN pid: [[PID:[0-9]*]] rss: {{.*}} threads: 1 stacks: [[STACKS:[0-9]*]] thr_aux: {{.*}} stack_depot: {{.*}} uniq_stacks: [[UNIQ_STACKS:[0-9]*]] heap: [[HEAP:[0-9]*]] +} diff --git a/test/hwasan/TestCases/print-memory-usage.c b/test/hwasan/TestCases/print-memory-usage.c new file mode 100644 index 000000000000..df9d534f32d4 --- /dev/null +++ b/test/hwasan/TestCases/print-memory-usage.c @@ -0,0 +1,72 @@ +// Tests __hwasan_print_memory_usage. +// RUN: %clang_hwasan %s -o %t +// RUN: ulimit -s 1000 +// RUN: %run %t 2>&1 | FileCheck %s +// REQUIRES: stable-runtime + +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <sanitizer/hwasan_interface.h> + +int state; +__thread volatile char *sink; + +__attribute__((noinline)) +void *malloc_and_use(int size) { + char *x = (char*)malloc(size); + for (int i = 0; i < size; i++) + x[i] = 42; // make this memory used. + return x; +} + +void *T1(void *arg) { + + for (int i = 1; i <= (1 << 20); i *= 2) + sink = malloc_and_use(i); + + __sync_fetch_and_add(&state, 1); + while (__sync_fetch_and_add(&state, 0) != 4) {} + return NULL; +} + +void *T4(void *arg) { return NULL; } + +int main() { + __hwasan_enable_allocator_tagging(); + sink = malloc_and_use(10); + + __hwasan_print_memory_usage(); + // CHECK: HWASAN pid: [[PID:[0-9]*]] rss: {{.*}} threads: 1 stacks: [[STACKS:[0-9]*]] thr_aux: {{.*}} stack_depot: {{.*}} uniq_stacks: [[UNIQ_STACKS:[0-9]*]] heap: [[HEAP:[0-9]*]] + + void *one_meg = malloc_and_use(1 << 20); + + __hwasan_print_memory_usage(); + // CHECK: HWASAN pid: [[PID]] rss: {{.*}} threads: 1 stacks: [[STACKS]] thr_aux: {{.*}} stack_depot: {{.*}} + + free(one_meg); + + __hwasan_print_memory_usage(); + // CHECK: HWASAN pid: [[PID]] rss: {{.*}} threads: 1 stacks: [[STACKS]] thr_aux: {{.*}} stack_depot: {{.*}} uniq_stacks: {{.*}} heap: [[HEAP]] + + pthread_t t1, t2, t3, t4; + + pthread_create(&t1, NULL, T1, NULL); + pthread_create(&t2, NULL, T1, NULL); + pthread_create(&t3, NULL, T1, NULL); + pthread_create(&t4, NULL, T4, NULL); + while (__sync_fetch_and_add(&state, 0) != 3) {} + pthread_join(t4, NULL); + + __hwasan_print_memory_usage(); + // CHECK: HWASAN pid: [[PID]] rss: {{.*}} threads: 4 stacks: + + __sync_fetch_and_add(&state, 1); + pthread_join(t1, NULL); + pthread_join(t2, NULL); + pthread_join(t3, NULL); + __hwasan_print_memory_usage(); + // CHECK: HWASAN pid: [[PID]] rss: {{.*}} threads: 1 stacks: [[STACKS]] +} diff --git a/test/hwasan/TestCases/pthread_exit.c b/test/hwasan/TestCases/pthread_exit.c new file mode 100644 index 000000000000..937e20c6ad19 --- /dev/null +++ b/test/hwasan/TestCases/pthread_exit.c @@ -0,0 +1,5 @@ +// Tests pthread_exit. +// RUN: %clang_hwasan %s -o %t && %run %t +// REQUIRES: stable-runtime +#include <pthread.h> +int main() { pthread_exit(NULL); } diff --git a/test/hwasan/TestCases/random-align-right.c b/test/hwasan/TestCases/random-align-right.c new file mode 100644 index 000000000000..8c524ef4784d --- /dev/null +++ b/test/hwasan/TestCases/random-align-right.c @@ -0,0 +1,35 @@ +// Tests malloc_align_right=1 and 8 (randomly aligning right). +// RUN: %clang_hwasan %s -o %t +// +// RUN: %run %t +// RUN: %env_hwasan_opts=malloc_align_right=1 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 +// RUN: %env_hwasan_opts=malloc_align_right=8 not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK8 + +// REQUIRES: stable-runtime + +#include <stdlib.h> +#include <stdio.h> +#include <sanitizer/hwasan_interface.h> + +static volatile void *sink; + +int main(int argc, char **argv) { + __hwasan_enable_allocator_tagging(); + + // Perform 1000 buffer overflows within the 16-byte granule, + // so that random right-alignment has a very high chance of + // catching at least one of them. + for (int i = 0; i < 1000; i++) { + char *p = (char*)malloc(20); + sink = p; + fprintf(stderr, "[%d] p: %p; accessing p[20]:\n", i, p); + p[20 * argc] = 0; // requires malloc_align_right=1 to catch + fprintf(stderr, "[%d] p: %p; accessing p[30]:\n", i, p); + p[30 * argc] = 0; // requires malloc_align_right={1,8} to catch +// CHECK1: accessing p[20] +// CHECK1-NEXT: HWAddressSanitizer: tag-mismatch +// CHECK8: accessing p[30]: +// CHECK8-NEXT: HWAddressSanitizer: tag-mismatch + } +} + diff --git a/test/hwasan/TestCases/realloc-after-free.c b/test/hwasan/TestCases/realloc-after-free.c new file mode 100644 index 000000000000..c4bc48c9ccda --- /dev/null +++ b/test/hwasan/TestCases/realloc-after-free.c @@ -0,0 +1,28 @@ +// RUN: %clang_hwasan %s -o %t +// RUN: not %run %t 50 2>&1 | FileCheck %s +// RUN: not %run %t 40 2>&1 | FileCheck %s +// RUN: not %run %t 30 2>&1 | FileCheck %s + +// REQUIRES: stable-runtime + +#include <stdlib.h> +#include <stdio.h> +#include <sanitizer/hwasan_interface.h> + +int main(int argc, char **argv) { + __hwasan_enable_allocator_tagging(); + if (argc != 2) return 0; + int realloc_size = atoi(argv[1]); + char * volatile x = (char*)malloc(40); + free(x); + x = realloc(x, realloc_size); +// CHECK: ERROR: HWAddressSanitizer: invalid-free on address +// CHECK: tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem) +// CHECK: freed by thread {{.*}} here: +// CHECK: previously allocated here: +// CHECK: Memory tags around the buggy address (one tag corresponds to 16 bytes): +// CHECK: =>{{.*}}[[MEM_TAG]] + fprintf(stderr, "DONE\n"); + __hwasan_disable_allocator_tagging(); +// CHECK-NOT: DONE +} diff --git a/test/hwasan/TestCases/realloc-test.cc b/test/hwasan/TestCases/realloc-test.cc new file mode 100644 index 000000000000..838790242426 --- /dev/null +++ b/test/hwasan/TestCases/realloc-test.cc @@ -0,0 +1,37 @@ +// Test basic realloc functionality. +// RUN: %clang_hwasan %s -o %t +// RUN: %run %t + +#include <stdlib.h> +#include <assert.h> +#include <sanitizer/hwasan_interface.h> + +int main() { + __hwasan_enable_allocator_tagging(); + char *x = (char*)realloc(nullptr, 4); + x[0] = 10; + x[1] = 20; + x[2] = 30; + x[3] = 40; + char *x1 = (char*)realloc(x, 5); + assert(x1 != x); // not necessary true for C, + // but true today for hwasan. + assert(x1[0] == 10 && x1[1] == 20 && x1[2] == 30 && x1[3] == 40); + x1[4] = 50; + + char *x2 = (char*)realloc(x1, 6); + x2[5] = 60; + assert(x2 != x1); + assert(x2[0] == 10 && x2[1] == 20 && x2[2] == 30 && x2[3] == 40 && + x2[4] == 50 && x2[5] == 60); + + char *x3 = (char*)realloc(x2, 6); + assert(x3 != x2); + assert(x3[0] == 10 && x3[1] == 20 && x3[2] == 30 && x3[3] == 40 && + x3[4] == 50 && x3[5] == 60); + + char *x4 = (char*)realloc(x3, 5); + assert(x4 != x3); + assert(x4[0] == 10 && x4[1] == 20 && x4[2] == 30 && x4[3] == 40 && + x4[4] == 50); +} diff --git a/test/hwasan/TestCases/rich-stack.c b/test/hwasan/TestCases/rich-stack.c new file mode 100644 index 000000000000..6787d57769f4 --- /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 <stdint.h> +#include <stdlib.h> +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/sanitizer_malloc.cc b/test/hwasan/TestCases/sanitizer_malloc.cc new file mode 100644 index 000000000000..66ac9641e109 --- /dev/null +++ b/test/hwasan/TestCases/sanitizer_malloc.cc @@ -0,0 +1,29 @@ +// Test allocator aliases. +// +// RUN: %clangxx_hwasan -O0 %s -o %t && %run %t + +#include <sanitizer/hwasan_interface.h> + +int main() { + void *volatile sink; + sink = (void *)&__sanitizer_posix_memalign; + sink = (void *)&__sanitizer_memalign; + sink = (void *)&__sanitizer_aligned_alloc; + sink = (void *)&__sanitizer___libc_memalign; + sink = (void *)&__sanitizer_valloc; + sink = (void *)&__sanitizer_pvalloc; + sink = (void *)&__sanitizer_free; + sink = (void *)&__sanitizer_cfree; + sink = (void *)&__sanitizer_malloc_usable_size; + sink = (void *)&__sanitizer_mallinfo; + sink = (void *)&__sanitizer_mallopt; + sink = (void *)&__sanitizer_malloc_stats; + sink = (void *)&__sanitizer_calloc; + sink = (void *)&__sanitizer_realloc; + sink = (void *)&__sanitizer_malloc; + + // sanity check + void *p = __sanitizer_malloc(100); + p = __sanitizer_realloc(p, 200); + __sanitizer_free(p); +} diff --git a/test/hwasan/TestCases/sizes.cpp b/test/hwasan/TestCases/sizes.cpp new file mode 100644 index 000000000000..52217de746e3 --- /dev/null +++ b/test/hwasan/TestCases/sizes.cpp @@ -0,0 +1,80 @@ +// RUN: %clangxx_hwasan %s -lstdc++ -o %t +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t malloc max 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t malloc max 2>&1 +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-calloc +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_hwasan_opts=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s --check-prefix=CHECK-oom +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new max 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_hwasan_opts=allocator_may_return_null=1 not %run %t new max 2>&1 | FileCheck %s --check-prefix=CHECK-oom +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 +// RUN: %env_hwasan_opts=allocator_may_return_null=0 not %run %t new-nothrow max 2>&1 | FileCheck %s --check-prefix=CHECK-max +// RUN: %env_hwasan_opts=allocator_may_return_null=1 %run %t new-nothrow max 2>&1 +// RUN: %run %t usable 2>&1 + +// Tests for various edge cases related to sizes, notably the maximum size the +// allocator can allocate. Tests that an integer overflow in the parameters of +// calloc is caught. + +#include <assert.h> +#include <malloc.h> +#include <stdlib.h> +#include <string.h> + +#include <limits> +#include <new> + +#include <sanitizer/allocator_interface.h> + +int main(int argc, char **argv) { + assert(argc <= 3); + bool test_size_max = argc == 3 && !strcmp(argv[2], "max"); + + static const size_t kMaxAllowedMallocSize = 1ULL << 40; + static const size_t kChunkHeaderSize = 16; + + size_t MallocSize = test_size_max ? std::numeric_limits<size_t>::max() + : kMaxAllowedMallocSize; + + if (!strcmp(argv[1], "malloc")) { + void *p = malloc(MallocSize); + assert(!p); + p = malloc(kMaxAllowedMallocSize - kChunkHeaderSize); + assert(!p); + } else if (!strcmp(argv[1], "calloc")) { + // Trigger an overflow in calloc. + size_t size = std::numeric_limits<size_t>::max(); + void *p = calloc((size / 0x1000) + 1, 0x1000); + assert(!p); + } else if (!strcmp(argv[1], "new")) { + void *p = operator new(MallocSize); + assert(!p); + } else if (!strcmp(argv[1], "new-nothrow")) { + void *p = operator new(MallocSize, std::nothrow); + assert(!p); + } else if (!strcmp(argv[1], "usable")) { + // Playing with the actual usable size of a chunk. + void *p = malloc(1007); + assert(p); + size_t size = __sanitizer_get_allocated_size(p); + assert(size >= 1007); + memset(p, 'A', size); + p = realloc(p, 2014); + assert(p); + size = __sanitizer_get_allocated_size(p); + assert(size >= 2014); + memset(p, 'B', size); + free(p); + } else { + assert(0); + } + + return 0; +} + +// CHECK-max: {{ERROR: HWAddressSanitizer: requested allocation size .* exceeds maximum supported size}} +// CHECK-oom: ERROR: HWAddressSanitizer: allocator is out of memory +// CHECK-calloc: ERROR: HWAddressSanitizer: calloc parameters overflow diff --git a/test/hwasan/TestCases/stack-history-length.c b/test/hwasan/TestCases/stack-history-length.c new file mode 100644 index 000000000000..0aefd40bebb1 --- /dev/null +++ b/test/hwasan/TestCases/stack-history-length.c @@ -0,0 +1,36 @@ +// RUN: %clang_hwasan -O1 %s -o %t +// 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 <stdlib.h> + +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(int argc, char **argv) { + int X = argc == 2 ? atoi(argv[1]) : 10; + // 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: Previously allocated frames +// YES: OOB +// YES: FUNC +// YES: FUNC0 + +// NO: Previously allocated frames +// NO: OOB +// NO: FUNC +// NO-NOT: FUNC0 diff --git a/test/hwasan/TestCases/stack-oob.c b/test/hwasan/TestCases/stack-oob.c new file mode 100644 index 000000000000..567af334e4bc --- /dev/null +++ b/test/hwasan/TestCases/stack-oob.c @@ -0,0 +1,25 @@ +// RUN: %clang_hwasan -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_hwasan -DSIZE=64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_hwasan -DSIZE=0x1000 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: stable-runtime + +#include <stdlib.h> +#include <sanitizer/hwasan_interface.h> + +__attribute__((noinline)) +int f() { + char z[SIZE]; + char *volatile p = z; + return p[SIZE]; +} + +int main() { + return f(); + // CHECK: READ of size 1 at + // CHECK: #0 {{.*}} in f{{.*}}stack-oob.c:14 + + // CHECK: is located in stack of threa + + // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in f +} diff --git a/test/hwasan/TestCases/stack-oob.cc b/test/hwasan/TestCases/stack-oob.cc deleted file mode 100644 index 60b9a6295005..000000000000 --- a/test/hwasan/TestCases/stack-oob.cc +++ /dev/null @@ -1,25 +0,0 @@ -// RUN: %clangxx_hwasan -DSIZE=16 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_hwasan -DSIZE=64 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s -// RUN: %clangxx_hwasan -DSIZE=0x1000 -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s - -// REQUIRES: stable-runtime - -#include <stdlib.h> -#include <sanitizer/hwasan_interface.h> - -__attribute__((noinline)) -int f() { - char z[SIZE]; - char *volatile p = z; - return p[SIZE]; -} - -int main() { - return f(); - // CHECK: READ of size 1 at - // CHECK: #0 {{.*}} in f{{.*}}stack-oob.cc:14 - - // CHECK: HWAddressSanitizer can not describe address in more detail. - - // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in f -} diff --git a/test/hwasan/TestCases/stack-uar.c b/test/hwasan/TestCases/stack-uar.c new file mode 100644 index 000000000000..863a84017ee9 --- /dev/null +++ b/test/hwasan/TestCases/stack-uar.c @@ -0,0 +1,41 @@ +// Tests use-after-return detection and reporting. +// RUN: %clang_hwasan -O0 -fno-discard-value-names %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: stable-runtime + +void USE(void *x) { // pretend_to_do_something(void *x) + __asm__ __volatile__("" : : "r" (x) : "memory"); +} + +__attribute__((noinline)) +char *buggy() { + char zzz[0x1000]; + char *volatile p = zzz; + return p; +} + +__attribute__((noinline)) void Unrelated1() { int A[2]; USE(&A[0]); } +__attribute__((noinline)) void Unrelated2() { int BB[3]; USE(&BB[0]); } +__attribute__((noinline)) void Unrelated3() { int CCC[4]; USE(&CCC[0]); } + +int main() { + char *p = buggy(); + Unrelated1(); + Unrelated2(); + Unrelated3(); + return *p; + // CHECK: READ of size 1 at + // CHECK: #0 {{.*}} in main{{.*}}stack-uar.c:[[@LINE-2]] + // CHECK: is located in stack of thread + // CHECK: Previously allocated frames: + // CHECK: Unrelated3 + // CHECK: 16 CCC + // CHECK: Unrelated2 + // CHECK: 12 BB + // CHECK: Unrelated1 + // CHECK: 8 A + // CHECK: buggy + // CHECK: 4096 zzz + + // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in main +} diff --git a/test/hwasan/TestCases/stack-uar.cc b/test/hwasan/TestCases/stack-uar.cc deleted file mode 100644 index e99dcceed533..000000000000 --- a/test/hwasan/TestCases/stack-uar.cc +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %clangxx_hwasan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s - -// REQUIRES: stable-runtime - -#include <stdlib.h> -#include <sanitizer/hwasan_interface.h> - -__attribute__((noinline)) -char *f() { - char z[0x1000]; - char *volatile p = z; - return p; -} - -int main() { - return *f(); - // CHECK: READ of size 1 at - // CHECK: #0 {{.*}} in main{{.*}}stack-uar.cc:16 - - // CHECK: HWAddressSanitizer can not describe address in more detail. - - // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in main -} diff --git a/test/hwasan/TestCases/tail-magic.c b/test/hwasan/TestCases/tail-magic.c new file mode 100644 index 000000000000..95c5ada08a36 --- /dev/null +++ b/test/hwasan/TestCases/tail-magic.c @@ -0,0 +1,28 @@ +// Tests free_checks_tail_magic=1. +// RUN: %clang_hwasan %s -o %t +// RUN: %env_hwasan_opts=free_checks_tail_magic=0 %run %t +// RUN: %env_hwasan_opts=free_checks_tail_magic=1 not %run %t 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s + +// REQUIRES: stable-runtime + +#include <stdlib.h> +#include <stdio.h> +#include <sanitizer/hwasan_interface.h> + +static volatile void *sink; + +int main(int argc, char **argv) { + __hwasan_enable_allocator_tagging(); + + char *p = (char*)malloc(20); + sink = p; + p[20] = 0x42; + p[24] = 0x66; + free(p); +// CHECK: ERROR: HWAddressSanitizer: alocation-tail-overwritten; heap object [{{.*}}) of size 20 +// CHECK: in main {{.*}}tail-magic.c:[[@LINE-2]] +// CHECK: allocated here: +// CHECK: in main {{.*}}tail-magic.c:[[@LINE-8]] +// CHECK: Tail contains: .. .. .. .. 42 {{.. .. ..}} 66 +} diff --git a/test/hwasan/TestCases/thread-uaf.c b/test/hwasan/TestCases/thread-uaf.c new file mode 100644 index 000000000000..f091167e3ced --- /dev/null +++ b/test/hwasan/TestCases/thread-uaf.c @@ -0,0 +1,58 @@ +// Tests UAF detection where Allocate/Deallocate/Use +// happen in separate threads. +// RUN: %clang_hwasan %s -o %t && not %run %t 2>&1 | FileCheck %s +// REQUIRES: stable-runtime + +#include <pthread.h> +#include <stdlib.h> +#include <stdio.h> + +#include <sanitizer/hwasan_interface.h> + +char *volatile x; +int state; + +void *Allocate(void *arg) { + x = (char*)malloc(10); + __sync_fetch_and_add(&state, 1); + while (__sync_fetch_and_add(&state, 0) != 3) {} + return NULL; +} +void *Deallocate(void *arg) { + free(x); + __sync_fetch_and_add(&state, 1); + while (__sync_fetch_and_add(&state, 0) != 3) {} + return NULL; +} + +void *Use(void *arg) { + x[5] = 42; + // CHECK: ERROR: HWAddressSanitizer: tag-mismatch on address + // CHECK: WRITE of size 1 {{.*}} in thread T3 + // CHECK: thread-uaf.c:[[@LINE-3]] + // CHECK: freed by thread T2 here + // CHECK: in Deallocate + // CHECK: previously allocated here: + // CHECK: in Allocate + // CHECK: Thread: T2 0x + // CHECK: Thread: T3 0x + // CHECK-DAG: Thread: T0 0x + // CHECK-DAG: Thread: T1 0x + __sync_fetch_and_add(&state, 1); + return NULL; +} + +int main() { + __hwasan_enable_allocator_tagging(); + pthread_t t1, t2, t3; + + pthread_create(&t1, NULL, Allocate, NULL); + while (__sync_fetch_and_add(&state, 0) != 1) {} + pthread_create(&t2, NULL, Deallocate, NULL); + while (__sync_fetch_and_add(&state, 0) != 2) {} + pthread_create(&t3, NULL, Use, NULL); + + pthread_join(t1, NULL); + pthread_join(t2, NULL); + pthread_join(t3, NULL); +} diff --git a/test/hwasan/TestCases/uaf_with_rb_distance.c b/test/hwasan/TestCases/uaf_with_rb_distance.c new file mode 100644 index 000000000000..25aae5256c41 --- /dev/null +++ b/test/hwasan/TestCases/uaf_with_rb_distance.c @@ -0,0 +1,27 @@ +// Checks how we print the developer note "hwasan_dev_note_heap_rb_distance". +// RUN: %clang_hwasan %s -o %t +// RUN: not %run %t 10 2>&1 | FileCheck %s --check-prefix=D10 +// RUN: not %run %t 42 2>&1 | FileCheck %s --check-prefix=D42 + +// REQUIRES: stable-runtime + +#include <stdlib.h> +#include <stdio.h> +#include <sanitizer/hwasan_interface.h> + + +void *p[100]; + +int main(int argc, char **argv) { + __hwasan_enable_allocator_tagging(); + int distance = argc >= 2 ? atoi(argv[1]) : 1; + for (int i = 0; i < 100; i++) + p[i] = malloc(i); + for (int i = 0; i < 100; i++) + free(p[i]); + + *(int*)p[distance] = 0; +} + +// D10: hwasan_dev_note_heap_rb_distance: 90 1023 +// D42: hwasan_dev_note_heap_rb_distance: 58 1023 diff --git a/test/hwasan/TestCases/use-after-free.c b/test/hwasan/TestCases/use-after-free.c index b9f6060112c1..03a1771c1b8f 100644 --- a/test/hwasan/TestCases/use-after-free.c +++ b/test/hwasan/TestCases/use-after-free.c @@ -1,13 +1,14 @@ -// RUN: %clang_hwasan -O0 -DLOAD %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,LOAD -// RUN: %clang_hwasan -O1 -DLOAD %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,LOAD -// RUN: %clang_hwasan -O2 -DLOAD %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,LOAD -// RUN: %clang_hwasan -O3 -DLOAD %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,LOAD +// RUN: %clang_hwasan -O0 -DISREAD=1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK +// RUN: %clang_hwasan -O1 -DISREAD=1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK +// RUN: %clang_hwasan -O2 -DISREAD=1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK +// RUN: %clang_hwasan -O3 -DISREAD=1 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK -// RUN: %clang_hwasan -O0 -DSTORE %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK,STORE +// RUN: %clang_hwasan -O0 -DISREAD=0 %s -o %t && not %run %t 2>&1 | FileCheck %s --check-prefixes=CHECK // REQUIRES: stable-runtime #include <stdlib.h> +#include <stdio.h> #include <sanitizer/hwasan_interface.h> int main() { @@ -15,25 +16,25 @@ int main() { char * volatile x = (char*)malloc(10); free(x); __hwasan_disable_allocator_tagging(); -#ifdef STORE - x[5] = 42; -#endif -#ifdef LOAD - return x[5]; -#endif - // LOAD: READ of size 1 at - // LOAD: #0 {{.*}} in main {{.*}}use-after-free.c:22 - - // STORE: WRITE of size 1 at - // STORE: #0 {{.*}} in main {{.*}}use-after-free.c:19 - - // CHECK: freed here: + fprintf(stderr, "Going to do a %s\n", ISREAD ? "READ" : "WRITE"); + // CHECK: Going to do a [[TYPE:[A-Z]*]] + int r = 0; + if (ISREAD) r = x[5]; else x[5] = 42; // should be on the same line. + // CHECK: [[TYPE]] of size 1 at {{.*}} tags: [[PTR_TAG:[0-9a-f][0-9a-f]]]/[[MEM_TAG:[0-9a-f][0-9a-f]]] (ptr/mem) + // CHECK: #0 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-2]] + // Offset is 5 or 11 depending on left/right alignment. + // CHECK: is a small unallocated heap chunk; size: 32 offset: {{5|11}} + // CHECK: is located 5 bytes inside of 10-byte region + // + // CHECK: freed by thread {{.*}} here: // CHECK: #0 {{.*}} in {{.*}}free{{.*}} {{.*}}hwasan_interceptors.cc - // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:16 + // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-14]] // CHECK: previously allocated here: // CHECK: #0 {{.*}} in {{.*}}malloc{{.*}} {{.*}}hwasan_interceptors.cc - // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:15 - + // CHECK: #1 {{.*}} in main {{.*}}use-after-free.c:[[@LINE-19]] + // CHECK: Memory tags around the buggy address (one tag corresponds to 16 bytes): + // CHECK: =>{{.*}}[[MEM_TAG]] // CHECK: SUMMARY: HWAddressSanitizer: tag-mismatch {{.*}} in main + return r; } diff --git a/test/hwasan/lit.cfg b/test/hwasan/lit.cfg index 3ebba51d05e4..66008a632bcf 100644 --- a/test/hwasan/lit.cfg +++ b/test/hwasan/lit.cfg @@ -9,14 +9,18 @@ config.name = 'HWAddressSanitizer' + getattr(config, 'name_suffix', 'default') config.test_source_root = os.path.dirname(__file__) # Setup default compiler flags used with -fsanitize=memory option. -clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-generate-tags-with-calls", config.target_cflags] + config.debug_info_flags +clang_cflags = [config.target_cflags] + config.debug_info_flags +clang_cxxflags = config.cxx_mode_flags + clang_cflags +clang_hwasan_cflags = ["-fsanitize=hwaddress", "-mllvm", "-hwasan-generate-tags-with-calls"] + clang_cflags clang_hwasan_cxxflags = config.cxx_mode_flags + clang_hwasan_cflags def build_invocation(compile_flags): return " " + " ".join([config.clang] + compile_flags) + " " +config.substitutions.append( ("%clangxx ", build_invocation(clang_cxxflags)) ) config.substitutions.append( ("%clang_hwasan ", build_invocation(clang_hwasan_cflags)) ) config.substitutions.append( ("%clangxx_hwasan ", build_invocation(clang_hwasan_cxxflags)) ) +config.substitutions.append( ("%compiler_rt_libdir", config.compiler_rt_libdir) ) default_hwasan_opts_str = ':'.join(['disable_allocator_tagging=1', 'random_tags=0'] + config.default_sanitizer_opts) if default_hwasan_opts_str: diff --git a/test/hwasan/lit.site.cfg.in b/test/hwasan/lit.site.cfg.in index 7453d1b74dc0..e95ea92cd2d9 100644 --- a/test/hwasan/lit.site.cfg.in +++ b/test/hwasan/lit.site.cfg.in @@ -4,6 +4,7 @@ config.name_suffix = "@HWASAN_TEST_CONFIG_SUFFIX@" config.target_cflags = "@HWASAN_TEST_TARGET_CFLAGS@" config.target_arch = "@HWASAN_TEST_TARGET_ARCH@" +config.android_files_to_push = @HWASAN_ANDROID_FILES_TO_PUSH@ # Load common config for all compiler-rt lit tests. lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") |