diff options
-rw-r--r-- | gdb/testsuite/gdb.arch/aarch64-mte.c | 107 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/aarch64-mte.exp | 338 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/memtag.c | 22 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/memtag.exp | 64 | ||||
-rw-r--r-- | gdb/testsuite/lib/gdb.exp | 16 |
5 files changed, 547 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.arch/aarch64-mte.c b/gdb/testsuite/gdb.arch/aarch64-mte.c new file mode 100644 index 0000000..f6fb4cc --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-mte.c @@ -0,0 +1,107 @@ +/* This test program is part of GDB, the GNU debugger. + + Copyright 2020 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* Exercise AArch64's Memory Tagging Extension with tagged pointers. */ + +/* This test was based on the documentation for the AArch64 Memory Tagging + Extension from the Linux Kernel, found in the sources in + Documentation/arm64/memory-tagging-extension.rst. */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/auxv.h> +#include <sys/mman.h> +#include <sys/prctl.h> + +/* From arch/arm64/include/uapi/asm/hwcap.h */ +#define HWCAP2_MTE (1 << 18) + +/* From arch/arm64/include/uapi/asm/mman.h */ +#define PROT_MTE 0x20 + +/* From include/uapi/linux/prctl.h */ +#define PR_SET_TAGGED_ADDR_CTRL 55 +#define PR_GET_TAGGED_ADDR_CTRL 56 +#define PR_TAGGED_ADDR_ENABLE (1UL << 0) +#define PR_MTE_TCF_SHIFT 1 +#define PR_MTE_TCF_NONE (0UL << PR_MTE_TCF_SHIFT) +#define PR_MTE_TCF_SYNC (1UL << PR_MTE_TCF_SHIFT) +#define PR_MTE_TCF_ASYNC (2UL << PR_MTE_TCF_SHIFT) +#define PR_MTE_TCF_MASK (3UL << PR_MTE_TCF_SHIFT) +#define PR_MTE_TAG_SHIFT 3 +#define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) + +void +access_memory (unsigned char *tagged_ptr, unsigned char *untagged_ptr) +{ + tagged_ptr[0] = 'a'; +} + +int +main (int argc, char **argv) +{ + unsigned char *tagged_ptr; + unsigned char *untagged_ptr; + unsigned long page_sz = sysconf (_SC_PAGESIZE); + unsigned long hwcap2 = getauxval(AT_HWCAP2); + + /* Bail out if MTE is not supported. */ + if (!(hwcap2 & HWCAP2_MTE)) + return 1; + + /* Enable the tagged address ABI, synchronous MTE tag check faults and + allow all non-zero tags in the randomly generated set. */ + if (prctl (PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC + | (0xfffe << PR_MTE_TAG_SHIFT), + 0, 0, 0)) + { + perror ("prctl () failed"); + return 1; + } + + /* Create a mapping that will have PROT_MTE set. */ + tagged_ptr = mmap (0, page_sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (tagged_ptr == MAP_FAILED) + { + perror ("mmap () failed"); + return 1; + } + + /* Create another mapping that won't have PROT_MTE set. */ + untagged_ptr = mmap (0, page_sz, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (untagged_ptr == MAP_FAILED) + { + perror ("mmap () failed"); + return 1; + } + + /* Enable MTE on the above anonymous mmap. */ + if (mprotect (tagged_ptr, page_sz, PROT_READ | PROT_WRITE | PROT_MTE)) + { + perror ("mprotect () failed"); + return 1; + } + + access_memory (tagged_ptr, untagged_ptr); + + return 0; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-mte.exp b/gdb/testsuite/gdb.arch/aarch64-mte.exp new file mode 100644 index 0000000..06d63b5 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-mte.exp @@ -0,0 +1,338 @@ +# Copyright (C) 2020 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# Test a binary with address signing works regardless of whether the target +# supports pauth instructions. On non pauth systems, all pauth instructions +# are treated as nops. + +global hex +global decimal + +# Return TAG in hex format with no leading zeroes. +proc get_hex_tag { tag } { + return [format "%x" $tag] +} + +# Return TAG in the NN format where N is 4 bits of the byte. +proc get_tag_nn { tag } { + return [format "%02x" $tag] +} + +# Return the address of PTR with a tag of TAG. +proc get_tagged_ptr { tag ptr } { + set addr [get_hexadecimal_valueof $ptr -1] + return [get_valueof "/x" \ + "${addr} & (0xf0ffffffffffffff) | ((unsigned long) ${tag} << 56)" \ + "0" "fetch pointer ${ptr} with tag ${tag}"] +} + +# Return the logical TAG from PTR. +proc get_ltag_from_ptr { ptr } { + set addr [get_hexadecimal_valueof $ptr -1] + return [get_valueof "/x" "${addr} >> 56 & 0xf" -1 "fetch tag from pointer ${ptr}"] +} + +if {![is_aarch64_target]} { + verbose "Skipping ${gdb_test_file_name}." + return +} + +standard_testfile +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return -1 +} + +if ![runto_main] { + untested "could not run to main" + return -1 +} + +# Targets that don't support memory tagging should not execute the +# runtime memory tagging tests. +if {![supports_memtag]} { + untested "memory tagging unsupported" + return -1 +} + +gdb_breakpoint "access_memory" + +if [gdb_continue "access_memory"] { + fail "could not run to tagged memory test function" + return -1 +} + +# Fetch a known pointer to an area mapped with PROT_MTE. +set tagged_ptr_symbol "tagged_ptr" +set tagged_ptr_addr [get_hexadecimal_valueof $tagged_ptr_symbol -1] + +if {$tagged_ptr_addr == -1} { + untested "unexpected pointer or tag value" + return -1 +} + +# Fetch a known pointer to an area not mapped with PROT_MTE. +set untagged_ptr_symbol "untagged_ptr" +set untagged_ptr_addr [get_hexadecimal_valueof $untagged_ptr_symbol -1] + +if {$untagged_ptr_addr == -1} { + untested "unexpected pointer or tag value" + return -1 +} + +with_test_prefix "literals" { + # Test inspecting an allocation tag from a pointer to a memory area that + # is not mapped with PROT_MTE. + set msg "Address ${untagged_ptr_addr} not in a region mapped with a memory tagging flag\." + gdb_test "mtag showatag ${untagged_ptr_addr}" $msg \ + "mtag showatag with an untagged address" + + gdb_test "mtag setatag ${untagged_ptr_addr} 0 00" $msg \ + "mtag setatag with an untagged address" + + set addr_tagged 0 + set addr_tagged_valid 0 + + # Test setting and showing the logical tags for a literal address. + for {set i 0} {$i < 32} {incr i} { + with_test_prefix "tag ${i}" { + set addr_tagged [get_tagged_ptr $i ${tagged_ptr_addr}] + } + + set tag_hexnz [get_hex_tag [expr $i % 16]] + gdb_test "mtag showltag ${addr_tagged}" \ + " = 0x${tag_hexnz}" \ + "showltag with tag ${i}" + + set tag_hexnn [get_tag_nn $i] + gdb_test "mtag setltag ${addr_tagged} ${tag_hexnn}" \ + " = \\(void \\*\\) ${addr_tagged}" \ + "setltag with tag ${i}" + } + + set setatag_msg "Allocation tag\\(s\\) updated successfully\." + # Test setting and showing the allocation tags. + for {set i 0} {$i < 32} {incr i} { + + set tag_hexnn [get_tag_nn $i] + gdb_test "mtag setatag ${tagged_ptr_addr} 0 ${tag_hexnn}" \ + $setatag_msg \ + "setatag with tag ${i}" + + set tag_hexnz [get_hex_tag [expr $i % 16]] + gdb_test "mtag showatag ${tagged_ptr_addr}" " = 0x${tag_hexnz}" \ + "showatag with tag ${i}" + } + + # Test tag mismatches. + with_test_prefix "tag mismatches" { + for {set i 0} {$i < 32} {incr i} { + + # Set the allocation tag to a known value. + set tag_hexnn [get_tag_nn $i] + gdb_test "mtag setatag ${tagged_ptr_addr} 0 ${tag_hexnn}" \ + $setatag_msg \ + "setatag with tag ${i}" + + set atag_hexnz [get_hex_tag [expr $i % 16]] + + # Validate that the logical tag matches the allocation tag. + with_test_prefix "tag ${i}" { + set addr_tagged [get_tagged_ptr $i ${tagged_ptr_addr}] + } + + gdb_test "mtag check ${addr_tagged}" \ + "Memory tags for address $hex match \\(0x${atag_hexnz}\\)\." \ + "check match with tag ${i}" + + # Get a pointer with the logical tag that does not match the + # allocation tag. + set ltag [expr $i + 1] + with_test_prefix "fetch mismatch tag ${i}" { + set addr_tagged [get_tagged_ptr $ltag ${tagged_ptr_addr}] + } + + # Validate that the logical tag does not match the allocation + # tag. + set ltag_hexnz [get_hex_tag [expr [expr $i + 1]% 16]] + gdb_test "mtag check ${addr_tagged}" \ + "Logical tag \\(0x${ltag_hexnz}\\) does not match the allocation tag \\(0x${atag_hexnz}\\) for address $hex\." \ + "check mismatch with tag ${i}" + } + } +} + +with_test_prefix "symbolic" { + # Test inspecting an allocation tag from a pointer to a memory area that + # is not mapped with PROT_MTE. + set msg "Address ${untagged_ptr_addr} not in a region mapped with a memory tagging flag\." + gdb_test "mtag showatag ${untagged_ptr_symbol}" $msg \ + "mtag showatag with an untagged address" + + gdb_test "mtag setatag ${untagged_ptr_symbol} 0 00" $msg \ + "mtag setatag with an untagged address" + + # Test setting and showing the logical tags for a literal address. + for {set i 0} {$i < 32} {incr i} { + set addr_tagged 0 + + with_test_prefix "tag ${i}" { + set addr_tagged [get_tagged_ptr $i ${tagged_ptr_addr}] + gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${addr_tagged}" \ + "update value of symbol ${tagged_ptr_symbol}" + } + + set tag_hexnz [get_hex_tag [expr $i % 16]] + gdb_test "mtag showltag ${tagged_ptr_symbol}" \ + " = 0x${tag_hexnz}" \ + "showltag with tag ${i}" + + set tag_hexnn [get_tag_nn $i] + gdb_test "mtag setltag ${tagged_ptr_symbol} ${tag_hexnn}" \ + " = \\(void \\*\\) ${addr_tagged}" \ + "setltag with tag ${i}" + } + + # Reset the tagged ptr to its original value + gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${tagged_ptr_addr}" \ + "reset ${tagged_ptr_symbol} to ${tagged_ptr_addr}" + + set setatag_msg "Allocation tag\\(s\\) updated successfully\." + # Test setting and showing the allocation tags. + for {set i 0} {$i < 32} {incr i} { + + set tag_hexnn [get_tag_nn $i] + gdb_test "mtag setatag ${tagged_ptr_symbol} 0 ${tag_hexnn}" \ + $setatag_msg \ + "setatag with tag ${i}" + + set tag_hexnz [get_hex_tag [expr $i % 16]] + gdb_test "mtag showatag ${tagged_ptr_symbol}" " = 0x${tag_hexnz}" \ + "showatag with tag ${i}" + } + + # Test tag mismatches. + with_test_prefix "tag mismatches" { + for {set i 0} {$i < 32} {incr i} { + + # Set the allocation tag to a known value (0). + set tag_hexnn [get_tag_nn $i] + gdb_test "mtag setatag ${tagged_ptr_symbol} 0 ${tag_hexnn}" \ + $setatag_msg \ + "setatag with tag ${i}" + + set atag_hexnz [get_hex_tag [expr $i % 16]] + + # Validate that the logical tag matches the allocation tag. + with_test_prefix "tag ${i}" { + set addr_tagged [get_tagged_ptr $i ${tagged_ptr_addr}] + } + + with_test_prefix "tag ${i}" { + gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${addr_tagged}" \ + "set ${tagged_ptr_symbol} to a matching logical tag" + } + + gdb_test "mtag check ${tagged_ptr_symbol}" \ + "Memory tags for address $hex match \\(0x${atag_hexnz}\\)\." \ + "check match with tag ${i}" + + # Get a pointer with the logical tag that does not match the + # allocation tag. + set ltag [expr $i + 1] + with_test_prefix "fetch mismatch tag ${i}" { + set addr_tagged [get_tagged_ptr $ltag ${tagged_ptr_addr}] + } + + with_test_prefix "tag ${i}" { + gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${addr_tagged}" \ + "set ${tagged_ptr_symbol} to a mismatching logical tag" + } + + # Validate that the logical tag does not match the allocation + # tag. + set ltag_hexnz [get_hex_tag [expr [expr $i + 1]% 16]] + gdb_test "mtag check ${tagged_ptr_symbol}" \ + "Logical tag \\(0x${ltag_hexnz}\\) does not match the allocation tag \\(0x${atag_hexnz}\\) for address $hex\." \ + "check mismatch with tag ${i}" + } + # Reset the tagged ptr to its original value + gdb_test_no_output "set variable ${tagged_ptr_symbol} = ${tagged_ptr_addr}" \ + "reset ${tagged_ptr_symbol} to ${tagged_ptr_addr}" + } +} + +# Test the memory tagging extensions for the "print" command. +with_test_prefix "print command" { + set untagged_ptr [get_tagged_ptr 0 ${tagged_ptr_addr}] + + with_test_prefix "fetch ltag" { + set ltag [get_ltag_from_ptr ${tagged_ptr_addr}] + } + + if {$ltag == -1} { + untested "unexpected tag value" + return -1 + } + + set atag [expr [expr $ltag + 1] % 16] + set atag_hexnn [get_tag_nn $atag] + + gdb_test "mtag setatag ${tagged_ptr_symbol} 0 ${atag_hexnn}" \ + $setatag_msg \ + "make atag and ltag different" + + set atag_hexnz [get_hex_tag $atag] + gdb_test "p/x ${tagged_ptr_symbol}" \ + [multi_line \ + "Logical tag \\(${ltag}\\) does not match the allocation tag \\(0x${atag_hexnz}\\)\." \ + "\\\$\[0-9\]+ = ${untagged_ptr}"] \ + "show tag mismatch" +} + +# Test the memory tagging extensions for the "x" command. +with_test_prefix "x command" { + + # Check if the allocation tags match what we expect. + gdb_test "x/gxm ${tagged_ptr_symbol}" \ + [multi_line \ + "<Allocation Tag $hex for range \\\[$hex,$hex\\)>" \ + "$hex:\[ \t\]+$hex"] \ + "outputs tag information" + + # Also make sure no tag information is output for memory areas without + # PROT_MTE mappings. + gdb_test "x/gxm ${untagged_ptr_symbol}" \ + "$hex:\[ \t\]+$hex" \ + "does not output tag information" +} + +# Validate the presence of the MTE registers. +foreach reg {"sctlr" "gcr"} { + gdb_test "info registers $reg" \ + "$reg\[ \t\]+$hex\[ \t\]+$decimal" \ + "register $reg available" +} + +# Run until a crash and confirm GDB displays memory tag violation +# information. +gdb_test "continue" \ + [multi_line \ + "Program received signal SIGSEGV, Segmentation fault" \ + "Memory tag violation while accessing address $hex" \ + "Logical tag $hex" \ + "Allocation tag $hex\." \ + "$hex in access_memory \\(.*\\) at .*" \ + ".*tagged_ptr\\\[0\\\] = 'a';"] \ + "display tag violation information" diff --git a/gdb/testsuite/gdb.base/memtag.c b/gdb/testsuite/gdb.base/memtag.c new file mode 100644 index 0000000..63a42ae --- /dev/null +++ b/gdb/testsuite/gdb.base/memtag.c @@ -0,0 +1,22 @@ +/* This test program is part of GDB, the GNU debugger. + + Copyright 2020 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 this program. If not, see <http://www.gnu.org/licenses/>. */ + +int +main (int argc, char **argv) +{ + return 0; +} diff --git a/gdb/testsuite/gdb.base/memtag.exp b/gdb/testsuite/gdb.base/memtag.exp new file mode 100644 index 0000000..6c4b7d7 --- /dev/null +++ b/gdb/testsuite/gdb.base/memtag.exp @@ -0,0 +1,64 @@ +# Copyright 2020 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 this program. If not, see <http://www.gnu.org/licenses/>. + +# Smoke testing for the various memory tagging commands in GDB. + +set u_msg "Memory tagging not supported or disabled by the current architecture\." + +standard_testfile +if {[prepare_for_testing "failed to prepare" ${testfile} ${srcfile}]} { + return -1 +} + +if {[target_info gdb_protocol] == "extended-remote"} { + # Make sure we're disconnected, in case we're testing with an + # extended-remote board, therefore already connected. + gdb_test "disconnect" ".*" +} + +# Test commands without running the program. +with_test_prefix "before program execution" { + # These commands should all fails without a running program. + foreach subcmd {"setltag" "showltag" "setatag" "showatag" "check"} { + gdb_test "mtag $subcmd" $u_msg + } +} + +clean_restart $testfile + +if ![runto_main] { + untested "could not run to main" + return -1 +} + +# Targets that don't support memory tagging should not execute the +# runtime memory tagging tests. +if {![supports_memtag]} { + untested "memory tagging unsupported" + return -1 +} + +# With the program running, try to use the memory tagging commands. +with_test_prefix "during program execution" { + set msg "Argument required \\(address or pointer\\)\." + + # Test the various mtag commands again. + gdb_test "mtag showltag" $msg + gdb_test "mtag showatag" $msg + gdb_test "mtag setltag" "Argument required \\(<address> <tag>\\)\." + gdb_test "mtag setatag" \ + "Argument required \\(<starting address> <length> <tag bytes>\\)\." + gdb_test "mtag check" $msg +} diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 8dbfa7e..98e5e17 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -2613,6 +2613,22 @@ proc supports_get_siginfo_type {} { } } +# Return 1 if memory tagging is supported at runtime, otherwise return 0. + +proc supports_memtag {} { + global gdb_prompt + + gdb_test_multiple "mtag check" "" { + -re "Memory tagging not supported or disabled by the current architecture\..*$gdb_prompt $" { + return 0 + } + -re "Argument required \\(address or pointer\\).*$gdb_prompt $" { + return 1 + } + } + return 0 +} + # Return 1 if the target supports hardware single stepping. proc can_hardware_single_step {} { |