From: Thiago Jung Bauermann Date: Mon, 8 Jan 2024 20:31:05 +0000 (-0300) Subject: GDB: testsuite: Add gdb.arch/aarch64-gcs-core.exp testcase X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fc8413b706b583a513c90a71fa9a9d32b6c88565;p=thirdparty%2Fbinutils-gdb.git GDB: testsuite: Add gdb.arch/aarch64-gcs-core.exp testcase It tests both gcore and OS-generated core files. --- diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-core.c b/gdb/testsuite/gdb.arch/aarch64-gcs-core.c new file mode 100644 index 00000000000..d04bd76e079 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-core.c @@ -0,0 +1,124 @@ +/* This test program is part of GDB, the GNU debugger. + + Copyright 2025 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 . */ + +#include +#include +#include +#include +#include + +/* Feature check for Guarded Control Stack. */ +#ifndef HWCAP_GCS +#define HWCAP_GCS (1UL << 32) +#endif + +#ifndef PR_GET_SHADOW_STACK_STATUS +#define PR_GET_SHADOW_STACK_STATUS 74 +#define PR_SET_SHADOW_STACK_STATUS 75 +#define PR_SHADOW_STACK_ENABLE (1UL << 0) +#endif + +/* We need to use a macro to call prctl because after GCS is enabled, it's not + possible to return from the function which enabled it. This is because the + return address of the calling function isn't on the GCS. */ +#define my_syscall2(num, arg1, arg2) \ + ({ \ + register long _num __asm__("x8") = (num); \ + register long _arg1 __asm__("x0") = (long)(arg1); \ + register long _arg2 __asm__("x1") = (long)(arg2); \ + register long _arg3 __asm__("x2") = 0; \ + register long _arg4 __asm__("x3") = 0; \ + register long _arg5 __asm__("x4") = 0; \ + \ + __asm__ volatile("svc #0\n" \ + : "=r"(_arg1) \ + : "r"(_arg1), "r"(_arg2), "r"(_arg3), "r"(_arg4), \ + "r"(_arg5), "r"(_num) \ + : "memory", "cc"); \ + _arg1; \ + }) + +#define get_gcspr(void) \ + ({ \ + unsigned long *gcspr; \ + \ + /* Get GCSPR_EL0. */ \ + asm volatile("mrs %0, S3_3_C2_C5_1" : "=r"(gcspr) : : "cc"); \ + \ + gcspr; \ + }) + +/* Corrupt the return address to see if GDB will report a SIGSEGV with the + expected + $_siginfo.si_code. */ +static void __attribute__ ((noinline)) +function (unsigned long *gcspr) +{ + /* x30 holds the return address. */ + register long x30 __asm__("x30") __attribute__ ((unused)); + + /* Print GCSPR to stdout so that the testcase can capture it. */ + printf ("%p\n", get_gcspr ()); + fflush (stdout); + + /* Cause a GCS exception. */ + x30 = 0xbadc0ffee; + __asm__ volatile("ret\n"); +} + +int +main (void) +{ + if (!(getauxval (AT_HWCAP) & HWCAP_GCS)) + { + fprintf (stderr, "GCS support not found in AT_HWCAP\n"); + return EXIT_FAILURE; + } + + /* Force shadow stacks on, our tests *should* be fine with or + without libc support and with or without this having ended + up tagged for GCS and enabled by the dynamic linker. We + can't use the libc prctl() function since we can't return + from enabling the stack. Also lock GCS if not already + locked so we can test behaviour when it's locked. */ + unsigned long gcs_mode; + int ret = my_syscall2 (__NR_prctl, PR_GET_SHADOW_STACK_STATUS, &gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to read GCS state: %d\n", ret); + return EXIT_FAILURE; + } + + if (!(gcs_mode & PR_SHADOW_STACK_ENABLE)) + { + gcs_mode = PR_SHADOW_STACK_ENABLE; + ret = my_syscall2 (__NR_prctl, PR_SET_SHADOW_STACK_STATUS, gcs_mode); + if (ret) + { + fprintf (stderr, "Failed to configure GCS: %d\n", ret); + return EXIT_FAILURE; + } + } + + unsigned long *gcspr = get_gcspr (); + + /* Pass gscpr to function just so it's used for something. */ + function (gcspr); /* Break here. */ + + /* Avoid returning, in case libc doesn't understand GCS. */ + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp new file mode 100644 index 00000000000..17bbb5e4ded --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp @@ -0,0 +1,105 @@ +# Copyright 2025 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 reading and writing the core dump of a binary that uses a Guarded +# Control Stack. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" $testfile $srcfile] } { + return +} + +set linespec ${srcfile}:[gdb_get_line_number "Break here"] + +if ![runto $linespec] { + return +} + +# Continue until a crash. The line with the hex number is optional because +# it's printed by the test program, and doesn't appear in the Expect buffer +# when testing a remote target. +gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "($hex\r\n)?" \ + "Program received signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ + "function \\(gcspr=$hex\\) at .*aarch64-gcs-core.c:$decimal" \ + {.*__asm__ volatile\("ret\\n"\);}] \ + "continue to SIGSEGV" + +set gcspr_in_gcore [get_valueof "/x" "\$gcspr" "*unknown*"] + +# Generate the gcore core file. +set gcore_filename [standard_output_file "${testfile}.gcore"] +set gcore_generated [gdb_gcore_cmd "$gcore_filename" "generate gcore file"] + +# Obtain an OS-generated core file. +set core_filename [core_find $binfile {} {} "${binfile}.out"] +set core_generated [expr {$core_filename != ""}] +set os_core_name "${binfile}.core" +remote_exec build "mv $core_filename $os_core_name" +set core_filename $os_core_name + +# At this point we have a couple of core files, the gcore one generated by +# GDB and the one generated by the operating system. Make sure GDB can +# read both correctly. + +proc check_core_file {core_filename saved_gcspr} { + global decimal hex + + # Load the core file. + if [gdb_test "core $core_filename" \ + [multi_line \ + "Core was generated by .*\\." \ + "Program terminated with signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ + "#0 function \\(gcspr=$hex\\) at .*aarch64-gcs-core.c:$decimal" \ + "$decimal.*__asm__ volatile\\(\"ret\\\\n\"\\);"] \ + "load core file"] { + return -1 + } + + # Check the value of GCSPR in the core file. + gdb_test "print/x \$gcspr" "\\$\[0-9\]+ = $saved_gcspr" \ + "gcspr contents from core file" +} + +if {$gcore_generated} { + clean_restart $binfile + + with_test_prefix "gcore corefile" { + check_core_file $gcore_filename $gcspr_in_gcore + } +} else { + fail "gcore corefile not generated" +} + +if {$core_generated} { + clean_restart $binfile + + with_test_prefix "OS corefile" { + set out_id [open ${binfile}.out "r"] + set gcspr_in_core [gets $out_id] + + close $out_id + check_core_file $core_filename $gcspr_in_core + } +} else { + untested "OS corefile not generated" +} diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index 22b49828c8a..65f3132e80b 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -9338,7 +9338,7 @@ proc remove_core {pid {test ""}} { } } -proc core_find {binfile {deletefiles {}} {arg ""}} { +proc core_find {binfile {deletefiles {}} {arg ""} {output_file "/dev/null"}} { global objdir subdir set destcore "$binfile.core" @@ -9360,7 +9360,7 @@ proc core_find {binfile {deletefiles {}} {arg ""}} { set found 0 set coredir [standard_output_file coredir.[getpid]] file mkdir $coredir - catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >/dev/null 2>&1\"" + catch "system \"(cd ${coredir}; ulimit -c unlimited; ${binfile} ${arg}; true) >${output_file} 2>&1\"" # remote_exec host "${binfile}" foreach i "${coredir}/core ${coredir}/core.coremaker.c ${binfile}.core" { if [remote_file build exists $i] {