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 currently has the following known 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) when appropriate. 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 stzg, stz2g are not generated at all. Basically none of the store zero and tag the location instructions are implemented yet. 4. vararray-nested-gimple.c shows an outstanding issue. Currently, there is a (what I think) a superfluous .HWASAN_ALLOCA_UNPOISON. This causes incorrect code generation, because the expansion of this .HWASAN_ALLOCA_UNPOISON generates (invalid tagging): mov x1, sp mov x2, 0 .L4: stg sp, [x1], #16 subs x2, x2, 16 bne .L4 IOW, a loop to tag memory block of size 0. TBD: - Any suggestions on any of the above will be helpful. - Are the tests fittingly placed in gcc.target/aarch64 ? Suggestions on other tests, how to add execution tests are also appreciated. gcc/testsuite/ChangeLog: * lib/target-supports.exp: * 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-nested-gimple.c: New test. * gcc.target/aarch64/memtag/vararray.c: New test. --- [Changes from RFC V1] - Adjusted some tests as the code generation got bugfixes - Add vararray-nested-gimple.c to show the current outstanding issue with VLAs. [End of changes from RFC V1] --- .../gcc.target/aarch64/memtag/alloca-1.c | 14 ++++++++ .../gcc.target/aarch64/memtag/alloca-3.c | 27 ++++++++++++++++ .../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 | 18 +++++++++++ .../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 ++++++++++ .../aarch64/memtag/vararray-nested-gimple.c | 21 ++++++++++++ .../gcc.target/aarch64/memtag/vararray.c | 14 ++++++++ gcc/testsuite/lib/target-supports.exp | 12 +++++++ 17 files changed, 279 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-nested-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..370c8ee32b3a --- /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..4b3aaafa9618 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/alloca-3.c @@ -0,0 +1,27 @@ +/* { 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; +} + +/* With HWASAN_ALLOCA_POISON now calling irg of its own, the number of + expected irg is 3 and stg is 4 (3 for tag, 1 for untag each in their + respective loop). */ + +/* { dg-final { scan-assembler-times {\tirg\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..70b790c6c3e7 --- /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..0d86831f409e --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/basic-3.c @@ -0,0 +1,18 @@ +/* { 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); +} + +/* 3 stack vars need 48 bytes: 3 stg to color, 1 st2g + 1 stg to untag. */ + +/* { dg-final { scan-assembler-times {\tirg\t} 1 } } */ +/* { dg-final { scan-assembler-times {\tstg\t} 4 } } */ +/* { 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..d8b448c43a35 --- /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..c960048d6709 --- /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-nested-gimple.c b/gcc/testsuite/gcc.target/aarch64/memtag/vararray-nested-gimple.c new file mode 100644 index 000000000000..1d4447ae0d9f --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/vararray-nested-gimple.c @@ -0,0 +1,21 @@ +/* { 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]; + { + int c[n]; + } + return use (b, n); +} + +/* FIXME this testcase shows an issue that there is a superfluous + .HWASAN_ALLOCA_UNPOISON for the inner scope, which is unnecessary. + The presence of the additional .HWASAN_ALLOCA_UNPOISON here is also + a correctness issue. */ +/* { dg-final { scan-tree-dump-times "HWASAN_ALLOCA_POISON" 1 "asan1" } } */ +/* { dg-final { scan-tree-dump-times "HWASAN_ALLOCA_UNPOISON" 1 "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..0dc3443a7896 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/memtag/vararray.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-O2" } */ + +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 } } */ +/* { dg-final { scan-assembler-times-not {\taddg\t}} } */ diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index a62f459ad7ed..b59afc9bd762 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -4870,6 +4870,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