Add basic tests for MEMTAG sanitizer. MEMTAG sanitizer uses target hooks to emit AArch64 specific MTE instructions.
Add new target-specific tests. The currently generated code has quite a few limitations: 1. For basic-1.c testcase, currently we generate: subg x0, x0, #16, #0 stg x0, [x0, #0] str w1, [x0] The subg can be optimized out. Adding #0 to the tag is non-consequential. The address generation component (x0+16) can be folded into the addr operands of stg. 2. Need to generate stgp (pre-index, post-index) above. Need to look into how aarch64 backend generates the store-pair/load-pair operations currently. We will likely need to use the same framework for generating the store-pair-with-tag (pre-indexed and post-indexed) variants for MTE. 3. Also stzp is not generated at all. TBD: - Any suggestions on any of the above 3 will be helpful. - Are the tests fittingly placed in gcc.target/aarch64 ? Suggestions on other tests are also most welcome. gcc/testsuite/ * gcc.target/aarch64/memtag/alloca-1.c: New test. * gcc.target/aarch64/memtag/alloca-3.c: New test. * gcc.target/aarch64/memtag/arguments-1.c: New test. * gcc.target/aarch64/memtag/arguments-2.c: New test. * gcc.target/aarch64/memtag/arguments-4.c: New test. * gcc.target/aarch64/memtag/arguments.c: New test. * gcc.target/aarch64/memtag/basic-1.c: New test. * gcc.target/aarch64/memtag/basic-3.c: New test. * gcc.target/aarch64/memtag/basic-struct.c: New test. * gcc.target/aarch64/memtag/large-array.c: New test. * gcc.target/aarch64/memtag/local-no-escape.c: New test. * gcc.target/aarch64/memtag/memtag.exp: New test. * gcc.target/aarch64/memtag/no-sanitize-attribute.c: New test. * gcc.target/aarch64/memtag/vararray-gimple.c: New test. * gcc.target/aarch64/memtag/vararray.c: New test. * lib/target-supports.exp: Define new proc to detect whether AArch64 target supports MTE. --- .../gcc.target/aarch64/memtag/alloca-1.c | 14 ++++++++ .../gcc.target/aarch64/memtag/alloca-3.c | 24 ++++++++++++++ .../gcc.target/aarch64/memtag/arguments-1.c | 3 ++ .../gcc.target/aarch64/memtag/arguments-2.c | 3 ++ .../gcc.target/aarch64/memtag/arguments-4.c | 16 ++++++++++ .../gcc.target/aarch64/memtag/arguments.c | 3 ++ .../gcc.target/aarch64/memtag/basic-1.c | 15 +++++++++ .../gcc.target/aarch64/memtag/basic-3.c | 16 ++++++++++ .../gcc.target/aarch64/memtag/basic-struct.c | 23 +++++++++++++ .../gcc.target/aarch64/memtag/large-array.c | 24 ++++++++++++++ .../aarch64/memtag/local-no-escape.c | 20 ++++++++++++ .../gcc.target/aarch64/memtag/memtag.exp | 32 +++++++++++++++++++ .../aarch64/memtag/no-sanitize-attribute.c | 17 ++++++++++ .../aarch64/memtag/vararray-gimple.c | 17 ++++++++++ .../gcc.target/aarch64/memtag/vararray.c | 14 ++++++++ gcc/testsuite/lib/target-supports.exp | 12 +++++++ 16 files changed, 253 insertions(+) create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/alloca-1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/arguments-1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/arguments-2.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/arguments-4.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/arguments.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/basic-1.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/basic-struct.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/large-array.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/local-no-escape.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/memtag.exp create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/no-sanitize-attribute.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/vararray-gimple.c create mode 100644 gcc/testsuite/gcc.target/aarch64/memtag/vararray.c diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/alloca-1.c b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-1.c new file mode 100644 index 000000000000..76cf2fe64669 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-1.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ + +extern int use (int * b); + +int foo (int n) +{ + int * b = __builtin_alloca (n); + int a = use (b); + return a; +} + +/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */ +/* { dg-final { scan-assembler-times {\tstg\t} 2 } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c new file mode 100644 index 000000000000..6a336158732a --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ + +extern int use (int * b); + +extern int n1; +extern int n2; +extern int n3; + +int foo (void) +{ + int * b1 = __builtin_alloca (n1); + int * b2 = __builtin_alloca (n2); + int * b3 = __builtin_alloca (n3); + int a1 = use (b1); + int a2 = use (b2); + int a3 = use (b3); + + return a1+a2+a3; +} + +/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */ +/* { dg-final { scan-assembler-times {\taddg\t} 3 } } */ +/* { dg-final { scan-assembler-times {\tstg\t} 4 } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/arguments-1.c b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-1.c new file mode 100644 index 000000000000..8d1e18a761ac --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-1.c @@ -0,0 +1,3 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fsanitize=kernel-hwaddress" } */ +/* { dg-error ".*'-fsanitize=memtag' is incompatible with '-fsanitize=kernel-hwaddress'.*" "" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/arguments-2.c b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-2.c new file mode 100644 index 000000000000..7650112dff9d --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-2.c @@ -0,0 +1,3 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fsanitize=kernel-address" } */ +/* { dg-error ".*'-fsanitize=memtag' is incompatible with '-fsanitize=kernel-address'.*" "" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/arguments-4.c b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-4.c new file mode 100644 index 000000000000..c5c64bd12454 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/arguments-4.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2 -fno-sanitize=memtag" } */ + +int use (int * x); + +void foo (int n) +{ + int x = 99; + use (&x); +} + +/* { dg-final { scan-assembler-not "irg" } } */ +/* { dg-final { scan-assembler-not "stg" } } */ +/* { dg-final { scan-assembler-not "st2g" } } */ +/* { dg-final { scan-assembler-not "subg" } } */ +/* { dg-final { scan-assembler-not "addg" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/arguments.c b/gcc/testsuite/gcc.target/aarch64/memtag/arguments.c new file mode 100644 index 000000000000..83713496c4a6 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/arguments.c @@ -0,0 +1,3 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fsanitize=address" } */ +/* { dg-error ".*'-fsanitize=memtag' is incompatible with '-fsanitize=address'.*" "" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/basic-1.c b/gcc/testsuite/gcc.target/aarch64/memtag/basic-1.c new file mode 100644 index 000000000000..b76f27d5e553 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/basic-1.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ +/* FIXME - scan-assembler-times-not subg ? */ +/* FIXME - generate stgp instead of stg + str ? */ + +int use (int * x); + +void foo (int n) +{ + int x = 99; + use (&x); +} + +/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */ +/* { dg-final { scan-assembler-times {\tstg\t} 2 } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c b/gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c new file mode 100644 index 000000000000..a54437951154 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ + +int use (int * x); + +void foo (int n) +{ + int a, b, c; + use(&a); + use(&b); + use(&c); +} + +/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */ +/* { dg-final { scan-assembler-times {\tstg\t} 3 } } */ +/* { dg-final { scan-assembler-times {\tst2g\t} 1 } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/basic-struct.c b/gcc/testsuite/gcc.target/aarch64/memtag/basic-struct.c new file mode 100644 index 000000000000..7751d109371e --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/basic-struct.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ +/* FIXME - scan-assembler-times stz2p ? */ + +struct A +{ + long a; + long b; + long c; + long d; +}; + +extern void use (struct A *a); + +long f (void) +{ + struct A a = {0, 0, 64, (long)&a}; + use (&a); + return a.b; +} + +/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */ +/* { dg-final { scan-assembler-times {\tst2g\t} 2 } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/large-array.c b/gcc/testsuite/gcc.target/aarch64/memtag/large-array.c new file mode 100644 index 000000000000..b5b944f4633b --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/large-array.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ +/* FIXME - add other checks later. For now make sure this does not ICE. */ + +#define ARRAY_LEN 12000 + +int create (void); + +void sort (int *data, int n); + +void sort_array() +{ + int data[ARRAY_LEN], i; + + for (i=0; i<ARRAY_LEN; ++i) + { + data[i] = create (); + } + + sort(data, ARRAY_LEN); +} + +/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */ +/* { dg-final { scan-assembler-times {\tst2g\t} 2 } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/local-no-escape.c b/gcc/testsuite/gcc.target/aarch64/memtag/local-no-escape.c new file mode 100644 index 000000000000..173fe1135bf0 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/local-no-escape.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O0" } */ + +/* FIXME - If x doesnt escape the function, why should MTE tagging be done for + x ? */ + +extern int use (int *x); +extern int bar (int *x); +extern int baz (int *x); + +int a[10]; + +int foo (int n) +{ + int x = use (a); + if (x) + return bar (a); + else + return baz (a); +} diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/memtag.exp b/gcc/testsuite/gcc.target/aarch64/memtag/memtag.exp new file mode 100644 index 000000000000..7f45fa99835d --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/memtag.exp @@ -0,0 +1,32 @@ +# Copyright (C) 2024 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/>. + +# GCC testsuite that uses the `dg.exp' driver. + +# Load support procs. +load_lib gcc-dg.exp + +# Initialize `dg'. +dg-init + +# Main loop. +if [check_effective_target_aarch64_mte] { + dg-runtest [lsort [glob -nocomplain $srcdir/$subdir/*.\[cCS\]]] \ + "" "-fsanitize=memtag -march=armv8.5-a+memtag" +} + +# All done. +dg-finish diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/no-sanitize-attribute.c b/gcc/testsuite/gcc.target/aarch64/memtag/no-sanitize-attribute.c new file mode 100644 index 000000000000..39d1d432384e --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/no-sanitize-attribute.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ + +int use (int * x); + +__attribute__((no_sanitize("memtag"))) +void foo (int n) +{ + int x = 99; + use (&x); +} + +/* { dg-final { scan-assembler-not "irg" } } */ +/* { dg-final { scan-assembler-not "stg" } } */ +/* { dg-final { scan-assembler-not "st2g" } } */ +/* { dg-final { scan-assembler-not "subg" } } */ +/* { dg-final { scan-assembler-not "addg" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/vararray-gimple.c b/gcc/testsuite/gcc.target/aarch64/memtag/vararray-gimple.c new file mode 100644 index 000000000000..f270ac619137 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/vararray-gimple.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-asan -O2" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */ + +extern int *use (int *b, int n); + +int* foo (int n) +{ + int b[n]; + return use (b, n); +} + +/* HWASAN_ALLOCA_POISON is used for alloca and VLAs when MEMTAG is in effect. + Although HWASAN_ALLOCA_UNPOISON is (also) used for untagging frame, it + doesnt hurt to check it in context of the current test. */ +/* { dg-final { scan-tree-dump "HWASAN_ALLOCA_POISON" "asan1" } } */ +/* { dg-final { scan-tree-dump "HWASAN_ALLOCA_UNPOISON" "asan1" } } */ diff --git a/gcc/testsuite/gcc.target/aarch64/memtag/vararray.c b/gcc/testsuite/gcc.target/aarch64/memtag/vararray.c new file mode 100644 index 000000000000..ce115dc231b4 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/vararray.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ +/* FIXME - scan-assembler-times-not addg ? */ + +extern int *use (int *b, int n); + +int* foo (int n) +{ + int b[n]; + return use (b, n); +} + +/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */ +/* { dg-final { scan-assembler-times {\tstg\t} 3 } } */ diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 75703ddca608..87c2d50bc553 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -4698,6 +4698,18 @@ proc check_effective_target_aarch64_sve1_only { } { && ![check_effective_target_aarch64_sve2] }] } +# Return 1 if this is an AArch64 target supporting MTE. +# FIXME what is aarch64_mte. more stubs needed ? +proc check_effective_target_aarch64_mte { } { + if { ![istarget aarch64*-*-*] } { + return 0 + } + return [check_no_compiler_messages aarch64_mte assembly { + #if !defined (__ARM_FEATURE_MEMORY_TAGGING) + #error FOO + #endif + } "-march=armv8.5-a+memtag"] +} # Return the size in bits of an SVE vector, or 0 if the size is variable. proc aarch64_sve_bits { } { return [check_cached_effective_target aarch64_sve_bits { -- 2.43.0