summaryrefslogtreecommitdiff
path: root/test/hwasan
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2019-01-19 10:05:08 +0000
committerDimitry Andric <dim@FreeBSD.org>2019-01-19 10:05:08 +0000
commit0646903fc1f75f6e605754621119473ee083f4a4 (patch)
tree57bce79a7423a054cccec23bdf6cd96e2d271b4a /test/hwasan
parent005b7ed8f76756d94ef6266ded755ab7863cb936 (diff)
downloadsrc-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')
-rw-r--r--test/hwasan/CMakeLists.txt3
-rw-r--r--test/hwasan/TestCases/Posix/system-allocator-fallback.cc54
-rw-r--r--test/hwasan/TestCases/abort-message-android.cc28
-rw-r--r--test/hwasan/TestCases/cfi.cc18
-rw-r--r--test/hwasan/TestCases/deep-recursion.c73
-rw-r--r--test/hwasan/TestCases/double-free.c23
-rw-r--r--test/hwasan/TestCases/heap-buffer-overflow.c60
-rw-r--r--test/hwasan/TestCases/hwasan-print-shadow.cc29
-rw-r--r--test/hwasan/TestCases/longjmp.c26
-rw-r--r--test/hwasan/TestCases/malloc-test.c16
-rw-r--r--test/hwasan/TestCases/malloc_fill.cc22
-rw-r--r--test/hwasan/TestCases/many-threads-uaf.c45
-rw-r--r--test/hwasan/TestCases/mem-intrinsics-zero-size.c10
-rw-r--r--test/hwasan/TestCases/mem-intrinsics.c37
-rw-r--r--test/hwasan/TestCases/new-test.cc18
-rw-r--r--test/hwasan/TestCases/print-memory-usage-android.c21
-rw-r--r--test/hwasan/TestCases/print-memory-usage.c72
-rw-r--r--test/hwasan/TestCases/pthread_exit.c5
-rw-r--r--test/hwasan/TestCases/random-align-right.c35
-rw-r--r--test/hwasan/TestCases/realloc-after-free.c28
-rw-r--r--test/hwasan/TestCases/realloc-test.cc37
-rw-r--r--test/hwasan/TestCases/rich-stack.c66
-rw-r--r--test/hwasan/TestCases/sanitizer_malloc.cc29
-rw-r--r--test/hwasan/TestCases/sizes.cpp80
-rw-r--r--test/hwasan/TestCases/stack-history-length.c36
-rw-r--r--test/hwasan/TestCases/stack-oob.c25
-rw-r--r--test/hwasan/TestCases/stack-oob.cc25
-rw-r--r--test/hwasan/TestCases/stack-uar.c41
-rw-r--r--test/hwasan/TestCases/stack-uar.cc23
-rw-r--r--test/hwasan/TestCases/tail-magic.c28
-rw-r--r--test/hwasan/TestCases/thread-uaf.c58
-rw-r--r--test/hwasan/TestCases/uaf_with_rb_distance.c27
-rw-r--r--test/hwasan/TestCases/use-after-free.c43
-rw-r--r--test/hwasan/lit.cfg6
-rw-r--r--test/hwasan/lit.site.cfg.in1
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")