# Copyright (C) 2021-2023 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 . # Test a binary that uses MTE and exercise various MTE-related scenarios. 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] { return -1 } # Targets that don't support memory tagging should not execute the # runtime memory tagging tests. if {![supports_memtag]} { unsupported "memory tagging unsupported" return -1 } gdb_breakpoint "access_memory" if [gdb_continue "access_memory"] { 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} { unresolved "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} { unresolved "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 "memory-tag print-allocation-tag ${untagged_ptr_addr}" $msg \ "memory-tag print-allocation-tag with an untagged address" gdb_test "memory-tag set-allocation-tag ${untagged_ptr_addr} 1 00" $msg \ "memory-tag set-allocation-tag 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 "memory-tag print-logical-tag ${addr_tagged}" \ " = 0x${tag_hexnz}" \ "print-logical-tag with tag ${i}" set tag_hexnn [get_tag_nn $i] gdb_test "memory-tag with-logical-tag ${addr_tagged} ${tag_hexnn}" \ " = \\(void \\*\\) ${addr_tagged}" \ "with-logical-tag with tag ${i}" } set atag_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 "memory-tag set-allocation-tag ${tagged_ptr_addr} 1 ${tag_hexnn}" \ $atag_msg \ "set-allocation-tag with tag ${i}" set tag_hexnz [get_hex_tag [expr $i % 16]] gdb_test "memory-tag print-allocation-tag ${tagged_ptr_addr}" " = 0x${tag_hexnz}" \ "print-allocation-tag 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 "memory-tag set-allocation-tag ${tagged_ptr_addr} 1 ${tag_hexnn}" \ $atag_msg \ "set-allocation-tag 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 "memory-tag 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 "memory-tag 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 "memory-tag print-allocation-tag ${untagged_ptr_symbol}" $msg \ "memory-tag print-allocation-tag with an untagged address" gdb_test "memory-tag set-allocation-tag ${untagged_ptr_symbol} 1 00" $msg \ "memory-tag set-allocation-tag 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 "memory-tag print-logical-tag ${tagged_ptr_symbol}" \ " = 0x${tag_hexnz}" \ "print-logical-tag with tag ${i}" set tag_hexnn [get_tag_nn $i] gdb_test "memory-tag with-logical-tag ${tagged_ptr_symbol} ${tag_hexnn}" \ " = \\(void \\*\\) ${addr_tagged}" \ "with-logical-tag 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 atag_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 "memory-tag set-allocation-tag ${tagged_ptr_symbol} 1 ${tag_hexnn}" \ $atag_msg \ "set-allocation-tag with tag ${i}" set tag_hexnz [get_hex_tag [expr $i % 16]] gdb_test "memory-tag print-allocation-tag ${tagged_ptr_symbol}" \ " = 0x${tag_hexnz}" \ "print-allocation-tag 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 "memory-tag set-allocation-tag ${tagged_ptr_symbol} 1 ${tag_hexnn}" \ $atag_msg \ "set-allocation-tag 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 "memory-tag 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 "memory-tag 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} { unresolved "unexpected tag value" return -1 } set atag [expr [expr $ltag + 1] % 16] set atag_hexnn [get_tag_nn $atag] gdb_test "memory-tag set-allocation-tag ${tagged_ptr_symbol} 1 ${atag_hexnn}" \ $atag_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 \ "" \ "$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 {"tag_ctl" } { 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" \ "Allocation tag $hex" \ "Logical tag $hex\." \ "$hex in access_memory \\(.*\\) at .*" \ ".*tagged_ptr\\\[0\\\] = 'a';"] \ "display tag violation information" # Restart to execute the async tag fault test. with_test_prefix "async" { if ![runto_main] { return -1 } gdb_breakpoint "access_memory" if [gdb_continue "access_memory"] { fail "could not run to tagged memory test function" return -1 } # Force a tag fault. gdb_test "memory-tag set-allocation-tag tagged_ptr 1 05" \ $atag_msg \ "make atag and ltag different" # Force the tag fault to be async. gdb_test_no_output "set \$tag_ctl=0x7fff5" "set tag_ctl to async" # Run until a crash and confirm GDB displays memory tag violation # information for async mode gdb_test "continue" \ [multi_line \ "Program received signal SIGSEGV, Segmentation fault" \ "Memory tag violation" \ "Fault address unavailable\." \ "$hex in .* \\(.*\\) .*"] \ "display tag violation information" }