Adding hwasan tests. Frankly, these could be tidied up a little. I will be tidying them up while getting feedback on the hwasan introduction.
gcc/testsuite/ChangeLog: 2019-11-05 Matthew Malcomson <matthew.malcom...@arm.com> * c-c++-common/hwasan/arguments.c: New test. * c-c++-common/hwasan/halt_on_error-1.c: New test. * g++.dg/hwasan/rvo-handled.c: New test. * g++.dg/hwasan/try-catch-0.cpp: New test. * g++.dg/hwasan/try-catch-1.cpp: New test. * gcc.dg/hwasan/aligned-alloc.c: New test. * gcc.dg/hwasan/alloca-array-accessible.c: New test. * gcc.dg/hwasan/alloca-gets-different-tag.c: New test. * gcc.dg/hwasan/alloca-outside-caught.c: New test. * gcc.dg/hwasan/bitfield-1.c: New test. * gcc.dg/hwasan/bitfield-2.c: New test. * gcc.dg/hwasan/builtin-special-handling.c: New test. * gcc.dg/hwasan/check-interface.c: New test. * gcc.dg/hwasan/hwasan-poison-optimisation.c: New test. * gcc.dg/hwasan/hwasan-thread-access-parent.c: New test. * gcc.dg/hwasan/hwasan-thread-basic-failure.c: New test. * gcc.dg/hwasan/hwasan-thread-clears-stack.c: New test. * gcc.dg/hwasan/hwasan-thread-success.c: New test. * gcc.dg/hwasan/hwasan.exp: New file. * gcc.dg/hwasan/kernel-defaults.c: New test. * gcc.dg/hwasan/large-aligned-0.c: New test. * gcc.dg/hwasan/large-aligned-1.c: New test. * gcc.dg/hwasan/macro-definition.c: New test. * gcc.dg/hwasan/nested-functions-0.c: New test. * gcc.dg/hwasan/nested-functions-1.c: New test. * gcc.dg/hwasan/nested-functions-2.c: New test. * gcc.dg/hwasan/no-sanitize-attribute.c: New test. * gcc.dg/hwasan/random-frame-tag.c: New test. * gcc.dg/hwasan/setjmp-longjmp-0.c: New test. * gcc.dg/hwasan/setjmp-longjmp-1.c: New test. * gcc.dg/hwasan/stack-tagging-basic-0.c: New test. * gcc.dg/hwasan/stack-tagging-basic-1.c: New test. * gcc.dg/hwasan/stack-tagging-disable.c: New test. * gcc.dg/hwasan/vararray-outside-caught.c: New test. * gcc.dg/hwasan/very-large-objects.c: New test. * lib/hwasan-dg.exp: New file. ############### Attachment also inlined for ease of reply ############### diff --git a/gcc/testsuite/c-c++-common/hwasan/arguments.c b/gcc/testsuite/c-c++-common/hwasan/arguments.c new file mode 100644 index 0000000000000000000000000000000000000000..2d563eb8541694d501b021babd9452fd7fd502a3 --- /dev/null +++ b/gcc/testsuite/c-c++-common/hwasan/arguments.c @@ -0,0 +1,7 @@ +/* + TODO + Somehow test the conflict of arguments + -fsanitize=hwaddress -fsanitize=kernel-address + -fsanitize=hwaddress -fsanitize=address + -fsanitize=hwaddress -fsanitize=thread + */ diff --git a/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c new file mode 100644 index 0000000000000000000000000000000000000000..118191e2e00bd07bd4839888d2fb29baec926c60 --- /dev/null +++ b/gcc/testsuite/c-c++-common/hwasan/halt_on_error-1.c @@ -0,0 +1,25 @@ +/* Test recovery mode. */ +/* { dg-do run } */ +/* { dg-options "-fsanitize-recover=hwaddress" } */ +/* { dg-set-target-env-var HWASAN_OPTIONS "halt_on_error=false" } */ +/* { dg-shouldfail "hwasan" } */ + +#include <string.h> + +volatile int ten = 16; + +int main() { + char x[10]; + __builtin_memset(x, 0, ten + 1); + asm volatile ("" : : : "memory"); + volatile int res = x[ten]; + x[ten] = res + 3; + res = x[ten]; + return 0; +} + +/* { dg-output "WRITE of size 17 at 0x\[0-9a-f\]+.*" } */ +/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */ +/* { dg-output "WRITE of size 1 at 0x\[0-9a-f\]+.*" } */ +/* { dg-output "READ of size 1 at 0x\[0-9a-f\]+.*" } */ + diff --git a/gcc/testsuite/g++.dg/hwasan/rvo-handled.c b/gcc/testsuite/g++.dg/hwasan/rvo-handled.c new file mode 100644 index 0000000000000000000000000000000000000000..6e6934a0be1b0ce14c459555168f6a2590a8ec7f --- /dev/null +++ b/gcc/testsuite/g++.dg/hwasan/rvo-handled.c @@ -0,0 +1,56 @@ +/* { dg-do run } */ +/* TODO Ensure this test has enough optimisation to get RVO. */ + +#define assert(x) if (!(x)) __builtin_abort () + +struct big_struct { + int left; + int right; + void *ptr; + int big_array[100]; +}; + +/* + Tests for RVO (basically, checking -fsanitize=hwaddress has not broken RVO + in any way). + + 0) The value is accessible in both functions without a hwasan complaint. + 1) RVO does happen. + */ + +struct big_struct __attribute__ ((noinline)) +return_on_stack() +{ + struct big_struct x; + x.left = 100; + x.right = 20; + x.big_array[10] = 30; + x.ptr = &x; + return x; +} + +struct big_struct __attribute__ ((noinline)) +unnamed_return_on_stack() +{ + return (struct big_struct){ + .left = 100, + .right = 20, + .ptr = __builtin_frame_address (0), + .big_array = {0} + }; +} + +int main() +{ + struct big_struct x; + x = return_on_stack(); + /* Check that RVO happens by checking the address that the callee saw. */ + assert (x.ptr == &x); + struct big_struct y; + y = unnamed_return_on_stack(); + /* Know only running this on AArch64, which means stack grows downwards, + We're checking that the frame of the callee function is below the address + of this variable, which means that the callee function used RVO. */ + assert (y.ptr < (void *)&y); + return 0; +} diff --git a/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp b/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d5bbc93564c7f32a029be1a1e071f44188c91da5 --- /dev/null +++ b/gcc/testsuite/g++.dg/hwasan/try-catch-0.cpp @@ -0,0 +1,65 @@ +/* { dg-do run } */ + +/* This version should work just fine. */ +#include <stdexcept> + +char *intermediate_pointer = NULL; +static void optimization_barrier(void* arg) { + asm volatile("" : : "r"(arg) : "memory"); +} + +__attribute__((noinline)) +void h() { + char x[1000]; + intermediate_pointer = (void *)&x; + optimization_barrier(x); + throw std::runtime_error("hello"); +} + +__attribute__((noinline)) +void g() { + char x[1000]; + optimization_barrier(x); + h(); + optimization_barrier(x); +} + +__attribute__((noinline)) +void hwasan_read(char *p, int size) { + char volatile sink; + for (int i = 0; i < size; ++i) + sink = p[i]; +} + +__attribute__((noinline, no_sanitize("hwaddress"))) void after_catch() { + char x[10000]; + hwasan_read(&x[0], sizeof(x)); +} + + +__attribute__((noinline)) +void f() { + char x[1000]; + try { + // Put two tagged frames on the stack, throw an exception from the deepest one. + g(); + } catch (const std::runtime_error &e) { + // Put an untagged frame on stack, check that it is indeed untagged. + // This relies on exception support zeroing out stack tags. + // BAD: tag-mismatch + after_catch(); + // Check that an in-scope stack allocation is still tagged. + // This relies on exception support not zeroing too much. + hwasan_read(&x[0], sizeof(x)); +#ifdef CLEARED_ACCESS_CATCH + return (int)(intermediate_pointer[1]); +#else + return 0; +#endif + } + __builtin_abort (); +} + +int main() { + f(); +} diff --git a/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp b/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b124d29d33613794f569646b932b42c958b52348 --- /dev/null +++ b/gcc/testsuite/g++.dg/hwasan/try-catch-1.cpp @@ -0,0 +1,6 @@ +/* { dg-do run } */ + +/* This version should catch the invalid access. */ +#define CLEARED_ACCESS_CATCH +#include "try-catch-0.cpp" +#undef CLEARED_ACCESS_CATCH diff --git a/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c b/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c new file mode 100644 index 0000000000000000000000000000000000000000..49b317cce7516845944e4b74ece94af0a8570076 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/aligned-alloc.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* + TODO + This alignment isn't handled by the sanitizer interceptor alloc. + At the moment this program fails at runtime in the libhwasan library. + + LLVM catches this problem at compile-time. + */ + +int +main () +{ + void *p = __builtin_aligned_alloc (17, 100); + if ((unsigned long long)p & 0x10 == 0) + return 0; + __builtin_abort (); +} + diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c b/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c new file mode 100644 index 0000000000000000000000000000000000000000..c1d7c3bb0d1de7b8e6aa2ba7cf114604583e3ba3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/alloca-array-accessible.c @@ -0,0 +1,32 @@ +/* { dg-do run } */ + +#define alloca __builtin_alloca + +int __attribute__ ((noinline)) +using_alloca (int num) +{ + int retval = 0; + int *big_array = alloca (num * sizeof (int)); + for (int i = 0; i < num; ++i) { + retval += big_array[i]; + } + return retval; +} + +int __attribute__ ((noinline)) +using_vararray (int num) +{ + int retval = 0; + int big_array[num]; + for (int i = 0; i < num; ++i) { + retval += big_array[i]; + } + return retval; +} + +int main() +{ + using_alloca (16); + using_vararray (12); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c b/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c new file mode 100644 index 0000000000000000000000000000000000000000..d6dcb6fdf4ec535fef475d51de964bcc13337b67 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/alloca-gets-different-tag.c @@ -0,0 +1,64 @@ +/* { dg-do run } */ + +/* Alloca is given a different tag to other variables. + vararray should behave in the same way. */ + +#define alloca __builtin_alloca +#define assert(x) if (!(x)) __builtin_abort () + +struct two_values { + int left; + int right; +}; + +/* Require default hwasan tag ABI. + Know we're using AArch64 since that's the only architecture we run hwasan + tests on. */ +char tag_of (void * x) { return ((unsigned long long)x) >> 56; } + +int __attribute__ ((noinline)) +alloca_different_tag (int num) +{ + struct two_values tmp_object = { + .left = 100, + .right = num, + }; + int *big_array = alloca (num * sizeof (int)); + int other_array[100]; + + char first_tag = tag_of (&tmp_object); + char second_tag = tag_of (big_array); + char other_tag = tag_of (other_array); + assert (first_tag != second_tag); + assert (second_tag != other_tag); + assert (first_tag != other_tag); + return 0; +} + +int __attribute__ ((noinline)) +vararray_different_tag (int num) +{ + struct two_values tmp_object = { + .left = 100, + .right = num, + }; + int big_array[num]; + int other_array[100]; + + char first_tag = tag_of (&tmp_object); + char second_tag = tag_of (big_array); + char other_tag = tag_of (other_array); + assert (first_tag != second_tag); + assert (second_tag != other_tag); + assert (first_tag != other_tag); + return 0; +} + +int __attribute__ ((noinline)) +main () +{ + alloca_different_tag (10); + vararray_different_tag (8); + return 0; +} + diff --git a/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c b/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c new file mode 100644 index 0000000000000000000000000000000000000000..c794d7900a80ffd42d46ee492b9d53795ba914b5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/alloca-outside-caught.c @@ -0,0 +1,24 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ + +#define alloca __builtin_alloca + +int __attribute__ ((noinline)) +check_alloca (int num) +{ + volatile int *allocd_array = alloca (num * sizeof(int)); + int other_array[10]; + return allocd_array[12]; +} + +int __attribute__ ((noinline)) +main () +{ + check_alloca (3); + return 1; +} + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c b/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c new file mode 100644 index 0000000000000000000000000000000000000000..4cabb1c8a1578bcfb230be40fb54551e010c85b7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/bitfield-1.c @@ -0,0 +1,30 @@ +/* { dg-do run } */ + +struct bitmapped_struct { + unsigned one : 1; + unsigned two : 1; + unsigned three : 1; + unsigned four : 1; + unsigned five : 1; + unsigned six : 1; + unsigned seven : 1; + unsigned eight : 1; +}; + +/* Check that hwasan allows valid bitfield accesses. */ +int __attribute__ ((noinline)) +handle_unaligned_access (struct bitmapped_struct *foo) +{ + if (foo->three) + return foo->four; + + foo->five = 1; + return 1; +} + +int main() +{ + struct bitmapped_struct myvar = {0}; + handle_unaligned_access (&myvar); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c b/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c new file mode 100644 index 0000000000000000000000000000000000000000..4bd53bf8c3e4337d4dccd7f966908f50a9ace08b --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/bitfield-2.c @@ -0,0 +1,28 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ + +struct A +{ + /* Ensure the offset from the start of this struct to the bitfield we access + is large enough to be in a different tag. */ + char base[16]; + int : 4; + long x : 7; +}; + +int __attribute__ ((noinline, noclone)) +f (void *p) { + return ((struct A *)p)->x; +} + +int +main () +{ + char a = 0; + return f (&a); +} + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 2 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c b/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c new file mode 100644 index 0000000000000000000000000000000000000000..9458e338f437b64298143f3c81459c1221bf6537 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/builtin-special-handling.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-hwasan_O0 -fdump-tree-hwasan" } */ + +typedef __SIZE_TYPE__ size_t; +/* Functions to observe that HWASAN instruments memory builtins in the expected + manner. */ +void * __attribute__((noinline)) +memset_builtin (void *dest, int value, size_t len) +{ + return __builtin_memset (dest, value, len); +} + +/* HWASAN avoids strlen because it doesn't know the size of the memory access + until *after* the function call. */ +size_t __attribute__ ((noinline)) +strlen_builtin (char *element) +{ + return __builtin_strlen (element); +} + +/* First test ensures that the HWASAN_CHECK was emitted before the + builtin_memset. Second test ensures there was only HWASAN_CHECK (which + demonstrates that builtin_strlen was not instrumented). */ +/* { dg-final { scan-tree-dump-times "HWASAN_CHECK.*builtin_memset" 1 "hwasan1" } } */ +/* TODO It may be that the DejaGNU framework checks the *remaining* output + instead of the entire output in the second option. + Basically, check this test passes and it may work nicely. */ +/* { dg-final { scan-tree-dump-times "HWASAN_CHECK" 1 "hwasan1" } } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/check-interface.c b/gcc/testsuite/gcc.dg/hwasan/check-interface.c new file mode 100644 index 0000000000000000000000000000000000000000..dedb9174726ba031f1727c2617e7c9fc6dc4487d --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/check-interface.c @@ -0,0 +1,22 @@ +/* { dg-do run } */ +/* + Test taken from LLVM + compiler-rt/test/hwasan/TestCases/check-interface.cpp + */ +// Utilizes all flavors of __hwasan_load/store interface functions to verify +// that the instrumentation and the interface provided by HWASan do match. +// In case of a discrepancy, this test fails to link. + +#include <sanitizer/hwasan_interface.h> + +#define F(T) void f_##T(T *a, T *b) { *a = *b; } + +F(uint8_t) +F(uint16_t) +F(uint32_t) +F(uint64_t) + +typedef unsigned V32 __attribute__((__vector_size__(32))); +F(V32) + +int main() {} diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c new file mode 100644 index 0000000000000000000000000000000000000000..6ca716b78f430b726d6211aa4fb271f3a4116588 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-poison-optimisation.c @@ -0,0 +1,35 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */ +/* { dg-additional-options "-fdump-tree-hwasan1 -save-temps" } */ + +/* Here to check that the ASAN_POISON stuff works just fine. + This mechanism isn't very often used, but I should at least go through the + code-path once in my testfile. */ +int +main () +{ + int *ptr = 0; + + { + int a; + ptr = &a; + *ptr = 12345; + } + + return *ptr; +} + +/* TODO This is a pain around LTO. + I want to have a test that works for everything (including -flto functions), + but can't use any directive that works for both with and without -flto. */ +/* { dg-final { scan-tree-dump-times "ASAN_POISON" 1 "hwasan1" } } */ +/* TODO At the moment I simply abort, but in the future we should call some sort of + reporting function. + There is hence no output to look for right now, but will be in the future. +*/ +/* { dg-final { scan-assembler-times "bl\\s*__hwasan_tag_mismatch4" 1 } } */ +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c new file mode 100644 index 0000000000000000000000000000000000000000..107430a90245758637e14cca98be4a1cfe4faa13 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-access-parent.c @@ -0,0 +1,44 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ +/* { dg-additional-options "-lpthread" } */ + +#include <pthread.h> + +extern int printf (const char *, ...); +typedef __UINTPTR_TYPE__ uintptr_t; +typedef __UINT64_TYPE__ uint64_t; + +/* Test that tags are checked across different threads. + i.e. if this thread tries to access a different threads memory with the + incorrect tag, then this thread fails. */ +void * +failing_thread_function (void *argument) +{ + void * other = (void *)((uint64_t)argument & 0xffffffffffffffULL); + int *num = argument; + printf ("(should succeed): first number = %d\n", num[0]); + printf ("(now should fail):\n"); + + int *othernum = other; + printf (" second number = %d\n", othernum[0]); + return (void *)1; +} + +int +main (int argc, char **argv) +{ + int argument[100] = {0}; + argument[1] = 10; + pthread_t thread_index; + pthread_create (&thread_index, NULL, failing_thread_function, (void*)argument); + + void *retval; + pthread_join (thread_index, &retval); + + return (uintptr_t)retval; +} + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: 00/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T1.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c new file mode 100644 index 0000000000000000000000000000000000000000..baf4e57884ded6261052c41c94ea5c829372db28 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-basic-failure.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ +/* { dg-additional-options "-lpthread" } */ + +/* Ensure the failure mode for hwasan under pthreads looks sane. + (Looks sane means that the same error message is printed out rather than an + opaque message due to mishandling). */ + +#include <pthread.h> + +extern int printf (const char *, ...); +typedef __UINTPTR_TYPE__ uintptr_t; +typedef __UINT64_TYPE__ uint64_t; + +void * +failing_from_stack (void * argument) +{ + int internal_array[16] = {0}; + printf ("(now should fail):"); + printf (" problem number is %d\n", internal_array[17]); + return (void *)1; +} + +int +main (int argc, char **argv) +{ + int argument[100] = {0}; + argument[1] = 10; + pthread_t thread_index; + pthread_create (&thread_index, NULL, failing_from_stack, (void*)argument); + + void *retval; + pthread_join (thread_index, &retval); + + return (uintptr_t)retval; +} + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T1.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T1.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c new file mode 100644 index 0000000000000000000000000000000000000000..9d07447fae1866bfdf271a6fa2e61a1e3185a48f --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-clears-stack.c @@ -0,0 +1,47 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ +/* { dg-additional-options "-lpthread" } */ + +/* This checks the interceptor ABI pthread hooks. */ + +#include <pthread.h> + +extern int printf (const char *, ...); +typedef __UINTPTR_TYPE__ uintptr_t; +typedef __UINT64_TYPE__ uint64_t; + +__attribute__ ((noinline)) +void * Ident (void * argument) +{ + return argument; +} + +void * +pthread_stack_is_cleared (void *argument) +{ + (void)argument; + int internal_array[16] = {0}; + return Ident((void*)internal_array); +} + +int +main (int argc, char **argv) +{ + int argument[100] = {0}; + argument[1] = 10; + pthread_t thread_index; + pthread_create (&thread_index, NULL, pthread_stack_is_cleared, (void*)argument); + + void *retval; + pthread_join (thread_index, &retval); + + printf ("(should fail): "); + printf ("value left in stack is: %d\n", ((int *)retval)[0]); + + return (uintptr_t)retval; +} + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "HWAddressSanitizer can not describe address in more detail\..*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c new file mode 100644 index 0000000000000000000000000000000000000000..cf8f73495d0da3e734521a488da064f3fe287fcb --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/hwasan-thread-success.c @@ -0,0 +1,34 @@ +/* { dg-do run } */ +/* { dg-additional-options "-lpthread" } */ + +/* Just ensure that a basic threaded program works while running with hwasan. + */ + +#include <pthread.h> + +extern int printf (const char *, ...); +typedef __UINTPTR_TYPE__ uintptr_t; +typedef __UINT64_TYPE__ uint64_t; + +void * +successful_thread_function (void * argument) +{ + int *deref = (int *)argument; + if (deref[0] == 100) + deref[1] = 10; + return (void *)0; +} + +int +main (int argc, char **argv) +{ + int argument[100] = {0}; + argument[1] = 10; + pthread_t thread_index; + pthread_create (&thread_index, NULL, successful_thread_function, (void*)argument); + + void *retval; + pthread_join (thread_index, &retval); + + return (uintptr_t)retval; +} diff --git a/gcc/testsuite/gcc.dg/hwasan/hwasan.exp b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp new file mode 100644 index 0000000000000000000000000000000000000000..f90fd4a8e3e900a36935f6869f011051c1f40882 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/hwasan.exp @@ -0,0 +1,42 @@ +# Copyright (C) 2012-2019 Free Software Foundation, Inc. +# +# This file is part of GCC. +# +# GCC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GCC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# GCC testsuite that uses the `dg.exp' driver. + +# TODO If target is not AArch64, then exit. +# TODO If target does not have special kernel then exit. +# TODO Probably best if I just check that compiling some basic file with +# ~-fsanitize=hwaddress~ works and doesn't crash when using a syscall with a +# tagged pointer. + +# Load support procs. +load_lib gcc-dg.exp +load_lib hwasan-dg.exp + +# Initialize `dg'. +dg-init +hwasan_init + +# Main loop. +if [check_effective_target_fsanitize_hwaddress] { + gcc-dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.c $srcdir/c-c++-common/hwasan/*.c]] "" "" +} + +# All done. +hwasan_finish +dg-finish diff --git a/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c b/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c new file mode 100644 index 0000000000000000000000000000000000000000..be021464b8cea548467ac7ea53c67a30b6b79943 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/kernel-defaults.c @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-options "-fno-sanitize=hwaddress -fsanitize=kernel-hwaddress" } */ + + +/* Defaults to check for kernel-hwaddress. + 1) No stack tagging => no calls to __hwasan_tag_memory. + 2) No block scope tagging (same again). + 3) Use sanitize-recover by default (everything ends in noabort). */ +int __attribute__ ((noinline)) +accessing_pointers (int *left, int *right) +{ + int x = right[2]; + left[3] = right[1]; + return right[1] + left[2]; +} + +int __attribute__ ((noinline)) +using_stack (int num) +{ + int big_array[10]; + int other_array[20]; + accessing_pointers(other_array, big_array); + return big_array[num]; +} + +#ifndef ARG +#define ARG 0 +#endif +int __attribute__ ((noinline)) +main () +{ + using_stack (ARG); + return 0; +} + +/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */ +/* { dg-final { scan-assembler-not "__hwasan_(load|store)\\d(?!_noabort)" } } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c b/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c new file mode 100644 index 0000000000000000000000000000000000000000..aba79e0acf36dd9fa20755d3cfcab29f3cb5625e --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/large-aligned-0.c @@ -0,0 +1,31 @@ +/* { dg-do run } */ + +/* Handling large aligned variables. + Large aligned variables take a different code-path through expand_stack_vars + in cfgexpand.c. This testcase is just to exercise that code-path. + + The alternate code-path produces a second base-pointer through some + instructions emitted in the prologue. + + Test cases are: + 0) Valid access works without complaint. + 1) Invalid access is caught. */ +int __attribute__ ((noinline)) +handle_large_alignment (int num) +{ + int other_array[10]; + int big_array[100] __attribute__ ((aligned (32))); + return big_array[num] + other_array[num]; +} + +#ifndef ARG +#define ARG 1 +#endif +int __attribute__ ((noinline)) +main () +{ + handle_large_alignment (ARG); + return 0; +} + + diff --git a/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c b/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c new file mode 100644 index 0000000000000000000000000000000000000000..abea810bee9d0418493cca72c80d61dfd61a03d7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/large-aligned-1.c @@ -0,0 +1,13 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ + +#define ARG 12 +#include "large-aligned-0.c" +#undef ARG + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* NOTE: This assumes the current tagging mechanism (one at a time from the + base and large aligned variables being handled first). */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/macro-definition.c b/gcc/testsuite/gcc.dg/hwasan/macro-definition.c new file mode 100644 index 0000000000000000000000000000000000000000..5f654f557821f2dbe060e9976fbca7e5770f274c --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/macro-definition.c @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +extern void testfunc(int); +int foo() +{ +#ifndef __SANITIZE_HWADDRESS__ + testfunc(1); +#endif + return 1; +} + +/* { dg-final { scan-assembler-not "testfunc" } } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c new file mode 100644 index 0000000000000000000000000000000000000000..1fce3ec5fd3b092969660f2ee1fa9ac3633747fc --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-0.c @@ -0,0 +1,52 @@ +/* { dg-do run } */ + +/* + Tests of nested funtions are: + 0) Accessing closed over variables works. + 1) Accesses outside of variables is caught. + 2) Accessing variable out of scope is caught. + + Here we test that accessing closed over variables works. + */ + +/* We need a second layer of indirection so that GCC doesn't notice we're + returning the address of a local variable and put 0 in it's place. */ +__attribute__((noinline)) +int *Ident(void *x) { + return x; +} + +int __attribute__ ((noinline)) +intermediate (void (*f) (int, char), + char num) +{ + if (num == 1) + /* NOTE: We need to overrun by an amount greater than the "extra data" in a + nonlocal goto structure. The entire structure is allocated on the stack + with a single colour, which means we can't tell if a closed-over buffer + was overrun by an amount small enough that the access was still to some + data in that nonlocal goto structure. */ + f (100, 100); + else + f (3, 100); + /* Just return something ... */ + return num % 3; +} + +int* __attribute__ ((noinline)) +nested_function (char num) +{ + int big_array[16]; + int other_array[16]; + void store (int index, char value) + { big_array[index] = value; } + return Ident(&other_array[intermediate (store, num)]); +} + +#ifndef MAIN +int main () +{ + nested_function (0); + return 0; +} +#endif diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c new file mode 100644 index 0000000000000000000000000000000000000000..7d5e80e8a9cb6a1e5a2fb87f78f140c37cd88841 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-1.c @@ -0,0 +1,26 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ + +/* + Tests of nested funtions are: + 0) Accessing closed over variables works. + 1) Accesses outside of variables is caught. + 2) Accessing variable out of scope is caught. + + Here we test option 1. + */ + +#define MAIN 0 +#include "nested-functions-0.c" +#undef MAIN + +int main () +{ + nested_function (1); + return 0; +} + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c new file mode 100644 index 0000000000000000000000000000000000000000..be7942d4fb1bf0dec657396e0bd3ca2f43b78b36 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/nested-functions-2.c @@ -0,0 +1,27 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ + +/* + Tests of nested funtions are: + 0) Accessing closed over variables works. + 1) Accesses outside of variables is caught. + 2) Accessing variable out of scope is caught. + + Here we test option 2. + */ + +#define MAIN 0 +#include "nested-functions-0.c" +#undef MAIN + +int main () +{ + int *retval = nested_function (2); + *retval = 100; + return 0; +} + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "WRITE of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c b/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c new file mode 100644 index 0000000000000000000000000000000000000000..d5ff4aec06c8bc6c80fe512061ccc8908e321153 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/no-sanitize-attribute.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ + +__attribute__((no_sanitize_hwaddress)) int +f1 (int *p, int *q) +{ + *p = 42; + return *q; +} + +__attribute__((no_sanitize("hwaddress"))) int +f2 (int *p, int *q) +{ + *p = 42; + return *q; +} + +/* Only have one instance of __hwasan, it is __hwasan_init (the module + * constructor) there is no instrumentation in the functions. */ +/* { dg-final { scan-assembler-times "__hwasan" 1 } } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c b/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c new file mode 100644 index 0000000000000000000000000000000000000000..6dba71bf39558a945d10007b5437651fb7871626 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/random-frame-tag.c @@ -0,0 +1,7 @@ +/* { dg-do compile } */ +/* { dg-options "--param hwasan-random-frame-tag=1" } */ + +#include "stack-tagging-basic-0.c" + +/* Random frame tag => call to __hwasan_generate_tag. */ +/* { dg-final { scan-assembler "__hwasan_generate_tag" } } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c new file mode 100644 index 0000000000000000000000000000000000000000..3c7e9c5b3fec8677440b81a2e029427957b359ba --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-0.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ + +#include <setjmp.h> +#include <stdio.h> + +/* + Testing longjmp/setjmp should test. + + 0) Nothing special happens with the jmp_buf. + 1) Accesses to scopes jmp'd over are caught. + */ +int __attribute__ ((noinline)) +uses_longjmp (int **other_array, int num, jmp_buf env) +{ + int internal_array[100] = {0}; + *other_array = &internal_array[0]; + if (num % 2) + longjmp (env, num); + else + return num % 8; +} + +int __attribute__ ((noinline)) +uses_setjmp (int num) +{ + int big_array[100]; + int *other_array = NULL; + sigjmp_buf cur_env; + int temp = 0; + if ((temp = sigsetjmp (cur_env, 1)) != 0) + { + if (other_array != NULL) + printf ("Value pointed to in other_array[0]: %d\n", + other_array[0]); + + printf ("Longjmp returned %d.\n", temp); + return 10; + } + else + { + return uses_longjmp (&other_array, num, cur_env); + } +} + +#ifndef ARG +#define ARG 0 +#endif +int __attribute__ ((noinline)) +main () +{ + uses_setjmp (ARG); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c new file mode 100644 index 0000000000000000000000000000000000000000..a32b5063e556f0e45e83d657aa312fb4de860442 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/setjmp-longjmp-1.c @@ -0,0 +1,18 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ + +/* + Testing longjmp/setjmp should test. + + 0) Nothing special happens with the jmp_buf. + 1) Accesses to scopes jmp'd over are caught. + */ + +#define ARG 1 +#include "setjmp-longjmp-0.c" +#undef ARG + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c new file mode 100644 index 0000000000000000000000000000000000000000..050a3eda7aa421b6b955aedd900f6fbb24a74a61 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-0.c @@ -0,0 +1,33 @@ +/* { dg-do run } */ + +/* Basic tests for stack tagging. + + 0) Valid accesses work. + 1) Accesses outside of a variable crash. +*/ +int __attribute__ ((noinline)) +accessing_pointers (int *left, int *right) +{ + int x = right[2]; + left[3] = right[1]; + return right[1] + left[2]; +} + +int __attribute__ ((noinline)) +using_stack (int num) +{ + int big_array[10]; + int other_array[20]; + accessing_pointers(other_array, big_array); + return big_array[num]; +} + +#ifndef ARG +#define ARG 0 +#endif +int __attribute__ ((noinline)) +main () +{ + using_stack (ARG); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c new file mode 100644 index 0000000000000000000000000000000000000000..0db6bc4bd220e99bcd695ee93fcc9f4118d09457 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-basic-1.c @@ -0,0 +1,17 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ + +/* Basic tests for stack tagging. + + 0) Accesses outside of a variable crash. + 1) Valid accesses work. +*/ + +#define ARG 17 +#include "stack-tagging-basic-0.c" +#undef ARG + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/\[\[:xdigit:\]\]\[\[:xdigit:\]\] \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c new file mode 100644 index 0000000000000000000000000000000000000000..30f45062d070c482be34f9c824c57f4bd9cc3e10 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/stack-tagging-disable.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-options "--param hwasan-stack=0" } */ + + +/* No stack tagging => no calls to __hwasan_tag_memory. */ +int __attribute__ ((noinline)) +accessing_pointers (int *left, int *right) +{ + int x = right[2]; + left[3] = right[1]; + return right[1] + left[2]; +} + +int __attribute__ ((noinline)) +using_stack (int num) +{ + int big_array[10]; + int other_array[20]; + accessing_pointers(other_array, big_array); + return big_array[num]; +} + +#ifndef ARG +#define ARG 0 +#endif +int __attribute__ ((noinline)) +main () +{ + using_stack (ARG); + return 0; +} + +/* { dg-final { scan-assembler-not "__hwasan_tag_memory" } } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c b/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c new file mode 100644 index 0000000000000000000000000000000000000000..858bfcd7e596a90c6bd1d68667259a735bb4dce7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/vararray-outside-caught.c @@ -0,0 +1,21 @@ +/* { dg-do run } */ +/* { dg-shouldfail "hwasan" } */ + +int __attribute__ ((noinline)) +check_vararray (int num) +{ + int var_array[num]; + int other_array[10]; + return var_array[12]; +} + +int __attribute__ ((noinline)) +main () +{ + return check_vararray (3); +} + +/* { dg-output "HWAddressSanitizer: tag-mismatch on address 0x\[0-9a-f\]*.*" } */ +/* { dg-output "READ of size 4 at 0x\[0-9a-f\]* tags: \[\[:xdigit:\]\]\[\[:xdigit:\]\]/00 \\(ptr/mem\\) in thread T0.*" } */ +/* { dg-output "Address 0x\[0-9a-f\]* is located in stack of thread T0.*" } */ +/* { dg-output "SUMMARY: HWAddressSanitizer: tag-mismatch \[^\n\]*.*" } */ diff --git a/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c b/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c new file mode 100644 index 0000000000000000000000000000000000000000..55265353369540872e8fba4da99d9be92a7ad99b --- /dev/null +++ b/gcc/testsuite/gcc.dg/hwasan/very-large-objects.c @@ -0,0 +1,68 @@ +/* { dg-do compile } */ + +/* Ensure the sanitizer can handle very large offsets (i.e. that the hooks + handle offsets too large for the relevant instructions). + Just want to make sure this compiles without an ICE. */ +#ifndef ASIZE +# define ASIZE 0x10000000000UL +#endif + +typedef __UINT64_TYPE__ uint64_t; + +#if __LONG_MAX__ < 8 * ASIZE +# undef ASIZE +# define ASIZE 4096 +#endif + +extern void abort (void); + +int __attribute__((noinline)) +foo (const char *s) +{ + if (!s) + return 1; + if (s[0] != 'a') + abort (); + s += ASIZE - 1; + if (s[0] != 'b') + abort (); + return 0; +} + +int (*fn) (const char *) = foo; + +int __attribute__((noinline)) +bar (void) +{ + char s[ASIZE]; + s[0] = 'a'; + s[ASIZE - 1] = 'b'; + foo (s); + foo (s); + return 0; +} + +int __attribute__((noinline)) +baz (long i) +{ + if (i) + return fn (0); + else + { + char s[ASIZE]; + s[0] = 'a'; + s[ASIZE - 1] = 'b'; + foo (s); + foo (s); + return fn (0); + } +} + +int __attribute__((noinline)) +very_large_offset (int *p) +{ + char init_array[(uint64_t)0xfefefef]; + char other_array[(uint64_t)0xfefefef]; + return (int)init_array[p[1]] + (int)other_array[p[0]]; +} + diff --git a/gcc/testsuite/lib/hwasan-dg.exp b/gcc/testsuite/lib/hwasan-dg.exp new file mode 100644 index 0000000000000000000000000000000000000000..13cb94b02943f3186cd18f5913bfbd24089af75a --- /dev/null +++ b/gcc/testsuite/lib/hwasan-dg.exp @@ -0,0 +1,352 @@ +# Copyright (C) 2012-2019 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GCC; see the file COPYING3. If not see +# <http://www.gnu.org/licenses/>. + +# Return 1 if compilation with -fsanitize=hwaddress is error-free for trivial +# code, 0 otherwise. + +proc check_effective_target_fsanitize_hwaddress {} { + if ![check_no_compiler_messages fsanitize_hwaddress executable { + int main (void) { return 0; } + }] { + return 0; + } + + # hwasan doesn't work if there's a ulimit on virtual memory. + if ![is_remote target] { + if [catch {exec sh -c "ulimit -v"} ulimit_v] { + # failed to get ulimit + } elseif [regexp {^[0-9]+$} $ulimit_v] { + # ulimit -v gave a numeric limit + warning "skipping hwasan tests due to ulimit -v" + return 0; + } + } + + return 1; +} + +proc hwasan_include_flags {} { + global srcdir + global TESTING_IN_BUILD_TREE + + set flags "" + + if { [is_remote host] || ! [info exists TESTING_IN_BUILD_TREE] } { + return "${flags}" + } + + set flags "-I$srcdir/../../libsanitizer/include" + + return "$flags" +} + +# +# hwasan_link_flags -- compute library path and flags to find libhwasan. +# (originally from g++.exp) +# + +proc hwasan_link_flags { paths } { + global srcdir + global ld_library_path + global shlib_ext + global hwasan_saved_library_path + + set gccpath ${paths} + set flags "" + + set shlib_ext [get_shlib_extension] + set hwasan_saved_library_path $ld_library_path + + if { $gccpath != "" } { + if { [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.a"] + || [file exists "${gccpath}/libsanitizer/hwasan/.libs/libhwasan.${shlib_ext}"] } { + append flags " -B${gccpath}/libsanitizer/ " + append flags " -B${gccpath}/libsanitizer/hwasan/ " + append flags " -L${gccpath}/libsanitizer/hwasan/.libs " + append ld_library_path ":${gccpath}/libsanitizer/hwasan/.libs" + } + } else { + global tool_root_dir + + set libhwasan [lookfor_file ${tool_root_dir} libhwasan] + if { $libhwasan != "" } { + append flags "-L${libhwasan} " + append ld_library_path ":${libhwasan}" + } + } + + set_ld_library_path_env_vars + + return "$flags" +} + +# +# hwasan_init -- called at the start of each subdir of tests +# + +proc hwasan_init { args } { + global TEST_ALWAYS_FLAGS + global ALWAYS_CXXFLAGS + global TOOL_OPTIONS + global hwasan_saved_TEST_ALWAYS_FLAGS + global hwasan_saved_ALWAYS_CXXFLAGS + + set link_flags "" + if ![is_remote host] { + if [info exists TOOL_OPTIONS] { + set link_flags "[hwasan_link_flags [get_multilibs ${TOOL_OPTIONS}]]" + } else { + set link_flags "[hwasan_link_flags [get_multilibs]]" + } + } + + set include_flags "[hwasan_include_flags]" + + if [info exists TEST_ALWAYS_FLAGS] { + set hwasan_saved_TEST_ALWAYS_FLAGS $TEST_ALWAYS_FLAGS + } + if [info exists ALWAYS_CXXFLAGS] { + set hwasan_saved_ALWAYS_CXXFLAGS $ALWAYS_CXXFLAGS + set ALWAYS_CXXFLAGS [concat "{ldflags=$link_flags}" $ALWAYS_CXXFLAGS] + set ALWAYS_CXXFLAGS [concat "{additional_flags=-fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags}" $ALWAYS_CXXFLAGS] + } else { + if [info exists TEST_ALWAYS_FLAGS] { + set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags $TEST_ALWAYS_FLAGS" + } else { + set TEST_ALWAYS_FLAGS "$link_flags -fsanitize=hwaddress --param hwasan-random-frame-tag=0 -g $include_flags" + } + } +} + +# +# hwasan_finish -- called at the start of each subdir of tests +# + +proc hwasan_finish { args } { + global TEST_ALWAYS_FLAGS + global hwasan_saved_TEST_ALWAYS_FLAGS + global hwasan_saved_ALWAYS_CXXFLAGS + global hwasan_saved_library_path + global ld_library_path + + if [info exists hwasan_saved_ALWAYS_CXXFLAGS ] { + set ALWAYS_CXXFLAGS $hwasan_saved_ALWAYS_CXXFLAGS + } else { + if [info exists hwasan_saved_TEST_ALWAYS_FLAGS] { + set TEST_ALWAYS_FLAGS $hwasan_saved_TEST_ALWAYS_FLAGS + } else { + unset TEST_ALWAYS_FLAGS + } + } + set ld_library_path $hwasan_saved_library_path + set_ld_library_path_env_vars + clear_effective_target_cache +} + +# Symbolize lines like +# #2 0xdeadbeef (/some/path/libsanitizer.so.0.0.0+0xbeef) +# in $output using addr2line to +# #2 0xdeadbeef in foobar file:123 +proc hwasan_symbolize { output } { + set addresses [regexp -inline -all -line "^ *#\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+)\[+\](0x\[0-9a-f\]+)\[)\]$" "$output"] + if { [llength $addresses] > 0 } { + set addr2line_name [find_binutils_prog addr2line] + set idx 1 + while { $idx < [llength $addresses] } { + set key [regsub -all "\[\]\[\]" [lindex $addresses $idx] "\\\\&"] + set val [lindex $addresses [expr $idx + 1]] + lappend arr($key) $val + set idx [expr $idx + 3] + } + foreach key [array names arr] { + set args "-f -e $key $arr($key)" + set status [remote_exec host "$addr2line_name" "$args"] + if { [lindex $status 0] > 0 } continue + regsub -all "\r\n" [lindex $status 1] "\n" addr2line_output + regsub -all "\[\n\r\]BFD: \[^\n\r\]*" $addr2line_output "" addr2line_output + regsub -all "^BFD: \[^\n\r\]*\[\n\r\]" $addr2line_output "" addr2line_output + set addr2line_output [regexp -inline -all -line "^\[^\n\r]*" $addr2line_output] + set idx 0 + foreach val $arr($key) { + if { [expr $idx + 1] < [llength $addr2line_output] } { + set fnname [lindex $addr2line_output $idx] + set fileline [lindex $addr2line_output [expr $idx + 1]] + if { "$fnname" != "??" } { + set newkey "$key+$val" + set repl($newkey) "$fnname $fileline" + } + set idx [expr $idx + 2] + } + } + } + set idx 0 + set new_output "" + while {[regexp -start $idx -indices " #\[0-9\]+ 0x\[0-9a-f\]+ \[(\](\[^)\]+\[+\]0x\[0-9a-f\]+)\[)\]" "$output" -> addr] > 0} { + set low [lindex $addr 0] + set high [lindex $addr 1] + set val [string range "$output" $low $high] + append new_output [string range "$output" $idx [expr $low - 2]] + if [info exists repl($val)] { + append new_output "in $repl($val)" + } else { + append new_output "($val)" + } + set idx [expr $high + 2] + } + append new_output [string range "$output" $idx [string length "$output"]] + return "$new_output" + } + return "$output" +} + +# Return a list of gtest tests, printed in the form +# DEJAGNU_GTEST_TEST AddressSanitizer_SimpleDeathTest +# DEJAGNU_GTEST_TEST AddressSanitizer_VariousMallocsTest +proc hwasan_get_gtest_test_list { output } { + set idx 0 + set ret "" + while {[regexp -start $idx -indices "DEJAGNU_GTEST_TEST (\[^\n\r\]*)(\r\n|\n|\r)" "$output" -> testname] > 0} { + set low [lindex $testname 0] + set high [lindex $testname 1] + set val [string range "$output" $low $high] + lappend ret $val + set idx [expr $high + 1] + } + return $ret +} + +# Return a list of gtest EXPECT_DEATH tests, printed in the form +# DEJAGNU_GTEST_EXPECT_DEATH1 statement DEJAGNU_GTEST_EXPECT_DEATH1 regexp DEJAGNU_GTEST_EXPECT_DEATH1 +# DEJAGNU_GTEST_EXPECT_DEATH2 other statement DEJAGNU_GTEST_EXPECT_DEATH2 other regexp DEJAGNU_GTEST_EXPECT_DEATH2 +proc hwasan_get_gtest_expect_death_list { output } { + set idx 0 + set ret "" + while {[regexp -start $idx -indices "DEJAGNU_GTEST_EXPECT_DEATH(\[0-9\]*)" "$output" -> id ] > 0} { + set low [lindex $id 0] + set high [lindex $id 1] + set val_id [string range "$output" $low $high] + if {[regexp -start $low -indices "$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id (.*) DEJAGNU_GTEST_EXPECT_DEATH$val_id\[\n\r\]" "$output" whole statement regexpr ] == 0} { break } + set low [lindex $statement 0] + set high [lindex $statement 1] + set val_statement [string range "$output" $low $high] + set low [lindex $regexpr 0] + set high [lindex $regexpr 1] + set val_regexpr [string range "$output" $low $high] + lappend ret [list "$val_id" "$val_statement" "$val_regexpr"] + set idx [lindex $whole 1] + } + return $ret +} + +# Replace ${tool}_load with a wrapper so that we can symbolize the output. +if { [info procs ${tool}_load] != [list] \ + && [info procs saved_hwasan_${tool}_load] == [list] } { + rename ${tool}_load saved_hwasan_${tool}_load + + proc ${tool}_load { program args } { + global tool + global hwasan_last_gtest_test_list + global hwasan_last_gtest_expect_death_list + set result [eval [list saved_hwasan_${tool}_load $program] $args] + set output [lindex $result 1] + set symbolized_output [hwasan_symbolize "$output"] + set hwasan_last_gtest_test_list [hwasan_get_gtest_test_list "$output"] + set hwasan_last_gtest_expect_death_list [hwasan_get_gtest_expect_death_list "$output"] + set result [list [lindex $result 0] $symbolized_output] + return $result + } +} + +# Utility for running gtest hwasan emulation under dejagnu, invoked via dg-final. +# Call pass if variable has the desired value, otherwise fail. +# +# Argument 0 handles expected failures and the like +proc hwasan-gtest { args } { + global tool + global hwasan_last_gtest_test_list + global hwasan_last_gtest_expect_death_list + + if { ![info exists hwasan_last_gtest_test_list] } { return } + if { [llength $hwasan_last_gtest_test_list] == 0 } { return } + if { ![isnative] || [is_remote target] } { return } + + set gtest_test_list $hwasan_last_gtest_test_list + unset hwasan_last_gtest_test_list + + if { [llength $args] >= 1 } { + switch [dg-process-target [lindex $args 0]] { + "S" { } + "N" { return } + "F" { setup_xfail "*-*-*" } + "P" { } + } + } + + # This assumes that we are three frames down from dg-test, and that + # it still stores the filename of the testcase in a local variable "name". + # A cleaner solution would require a new DejaGnu release. + upvar 2 name testcase + upvar 2 prog prog + + set output_file "[file rootname [file tail $prog]].exe" + + foreach gtest $gtest_test_list { + set testname "$testcase $gtest" + set status -1 + + setenv DEJAGNU_GTEST_ARG "$gtest" + set result [${tool}_load ./$output_file $gtest] + unsetenv DEJAGNU_GTEST_ARG + set status [lindex $result 0] + set output [lindex $result 1] + if { "$status" == "pass" } { + pass "$testname execution test" + if { [info exists hwasan_last_gtest_expect_death_list] } { + set gtest_expect_death_list $hwasan_last_gtest_expect_death_list + foreach gtest_death $gtest_expect_death_list { + set id [lindex $gtest_death 0] + set testname "$testcase $gtest [lindex $gtest_death 1]" + set regexpr [lindex $gtest_death 2] + set status -1 + + setenv DEJAGNU_GTEST_ARG "$gtest:$id" + set result [${tool}_load ./$output_file "$gtest:$id"] + unsetenv DEJAGNU_GTEST_ARG + set status [lindex $result 0] + set output [lindex $result 1] + if { "$status" == "fail" } { + pass "$testname execution test" + if { ![regexp $regexpr ${output}] } { + fail "$testname output pattern test" + send_log "Output should match: $regexpr\n" + } else { + pass "$testname output pattern test" + } + } elseif { "$status" == "pass" } { + fail "$testname execution test" + } else { + $status "$testname execution test" + } + } + } + } else { + $status "$testname execution test" + } + unset hwasan_last_gtest_expect_death_list + } + + return +}
hwasan-patch16.patch.gz
Description: hwasan-patch16.patch.gz