diff options
Diffstat (limited to 'gdb/testsuite/gdb.arch')
25 files changed, 1730 insertions, 68 deletions
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 0000000..7767204 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-core.c @@ -0,0 +1,123 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/auxv.h> +#include <linux/prctl.h> +#include <sys/syscall.h> + +/* 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 0000000..2261ac8 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp @@ -0,0 +1,113 @@ +# 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 <http://www.gnu.org/licenses/>. + +# 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 +} + +# Obtain an OS-generated core file. Save test program output to +# ${binfile}.out. +set core_filename [core_find $binfile {} {} "${binfile}.out"] +set core_generated [expr {$core_filename != ""}] + +# Make sure GDB can read the given core file 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 {!$core_generated} { + untested "unable to create or find corefile" +} + +if {$core_generated} { + clean_restart $binfile + + with_test_prefix "OS corefile" { + # Read GCSPR value from saved output of the test program. + 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 + } +} + +if ![gcore_cmd_available] { + unsupported "target does not support gcore command." + return +} + +clean_restart $binfile + +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"] + +gdb_assert { $gcore_generated } "gcore corefile created" +if {$gcore_generated} { + clean_restart $binfile + + with_test_prefix "gcore corefile" { + check_core_file $gcore_filename $gcspr_in_gcore + } +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.c b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.c new file mode 100644 index 0000000..3d89535 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.c @@ -0,0 +1,140 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/auxv.h> +#include <sys/syscall.h> +#include <linux/prctl.h> + +/* 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; \ + }) + +static int __attribute__ ((noinline)) +function2 (void) +{ + return EXIT_SUCCESS; +} + +/* Put branch and link instructions being tested into their own functions so + that the program returns one level up in the stack after the displaced + stepped instruction. This tests that GDB doesn't leave the GCS out of sync + with the regular stack. */ + +static int __attribute__ ((noinline)) +function_bl (void) +{ + register int x0 __asm__("x0"); + + __asm__ ("bl function2\n" + : "=r"(x0) + : + : "x30"); + + return x0; +} + +static int __attribute__ ((noinline)) +function_blr (void) +{ + register int x0 __asm__("x0"); + + __asm__ ("blr %1\n" + : "=r"(x0) + : "r"(&function2) + : "x30"); + + return x0; +} + +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. */ + 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; + } + } + + int ret1 = function_bl (); + int ret2 = function_blr (); + + /* Avoid returning, in case libc doesn't understand GCS. */ + exit (ret1 + ret2); +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp new file mode 100644 index 0000000..2359d96 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp @@ -0,0 +1,86 @@ +# 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 <http://www.gnu.org/licenses/>. + +# Test displaced stepping in a program that uses a Guarded Control Stack. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return +} + +if ![runto_main] { + return +} + +gdb_test_no_output "set breakpoint auto-hw off" +gdb_test_no_output "set displaced-stepping on" + +# Get address of the branch and link instructions of interest. +set addr_bl 0 +set test "get address of bl instruction" +gdb_test_multiple "disassemble function_bl" $test -lbl { + -re "\r\n\\s+($hex) <\\+${decimal}>:\\s+bl\\s+${hex} <function2>(?=\r\n)" { + set addr_bl $expect_out(1,string) + exp_continue + } + -re -wrap "" { + gdb_assert { $addr_bl != 0 } $test + } +} + +set addr_blr 0 +set test "get address of blr instruction" +gdb_test_multiple "disassemble function_blr" $test -lbl { + -re "\r\n\\s+($hex) <\\+${decimal}>:\\s+blr\\s+x${decimal}(?=\r\n)" { + set addr_blr $expect_out(1,string) + exp_continue + } + -re -wrap "" { + gdb_assert { $addr_blr != 0 } $test + } +} + +if { $addr_bl == 0 || $addr_blr == 0 } { + return +} + +gdb_test "break *$addr_bl" \ + "Breakpoint $decimal at $hex: file .*aarch64-gcs-disp-step.c, line ${decimal}." \ + "set breakpoint at bl instruction" + +gdb_test "break *$addr_blr" \ + "Breakpoint $decimal at $hex: file .*aarch64-gcs-disp-step.c, line ${decimal}." \ + "set breakpoint at blr instruction" + +gdb_test "continue" \ + [multi_line \ + {Continuing\.} \ + "" \ + "Breakpoint $decimal, function_bl \\(\\) at .*aarch64-gcs-disp-step.c:${decimal}(?: \\\[GCS error\\\])?" \ + {[^\r\n]+"bl function2\\n"}] \ + "continue to breakpoint at bl" + +gdb_test "continue" \ + [multi_line \ + {Continuing\.} \ + "" \ + "Breakpoint $decimal, $hex in function_blr \\(\\) at .*aarch64-gcs-disp-step.c:${decimal}(?: \\\[GCS error\\\])?" \ + {[^\r\n]+"blr %1\\n"}] \ + "continue to breakpoint at blr" + +gdb_continue_to_end diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-return.c b/gdb/testsuite/gdb.arch/aarch64-gcs-return.c new file mode 100644 index 0000000..95518b6 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-return.c @@ -0,0 +1,105 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/auxv.h> +#include <sys/syscall.h> +#include <linux/prctl.h> + +/* 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; \ + }) + +static int __attribute__ ((noinline)) +call2 () +{ + return 42; /* Break call2. */ +} + +static int __attribute__ ((noinline)) +call1 () +{ + return call2 (); /* Break call1. */ +} + +int +main () +{ + 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; + } + } + + call1 (); /* Break begin. */ + + /* Avoid returning, in case libc doesn't understand GCS. */ + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-return.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-return.exp new file mode 100644 index 0000000..1d1c237 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-return.exp @@ -0,0 +1,129 @@ +# 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 <http://www.gnu.org/licenses/>. + +# Test the GDB return command in a program that uses a Guarded Control Stack. +# Based on the return tests in gdb.arch/amd64-shadow-stack-cmds.exp. +# Note that potential GCS violations often only occur after resuming normal +# execution. Therefore, it is important to test normal program +# completion after testing the return command. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return +} + +set begin_line [gdb_get_line_number "Break begin"] +set call1_line [gdb_get_line_number "Break call1"] +set call2_line [gdb_get_line_number "Break call2"] + +if ![runto ${begin_line}] { + return +} + +proc restart_and_run_infcall_call2 {} { + global binfile call2_line + clean_restart ${binfile} + if ![runto_main] { + return + } + set inside_infcall_str "The program being debugged stopped while in a function called from GDB" + gdb_breakpoint ${call2_line} + gdb_continue_to_breakpoint "Break call2" ".*Break call2.*" + gdb_test "call (int) call2()" \ + "Breakpoint \[0-9\]*, call2.*$inside_infcall_str.*" +} + +with_test_prefix "test inferior call and continue" { + gdb_breakpoint ${call1_line} + gdb_continue_to_breakpoint "Break call1" ".*Break call1.*" + + gdb_test "call (int) call2()" "= 42" + + gdb_continue_to_end +} + +with_test_prefix "test return inside an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "return" "\#0.*call2.*" \ + "Test GCS return inside an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end +} + +with_test_prefix "test return 'above' an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "frame 2" "call2 ().*" "move to frame 'above' inferior call" + + gdb_test "return" "\#0.*call1.*" \ + "Test GCS return 'above' an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end +} + +clean_restart ${binfile} +if ![runto ${begin_line}] { + return +} + +# Extract GCS pointer inside main, call1 and call2 function. +gdb_breakpoint ${call1_line} +gdb_breakpoint ${call2_line} +set gcspr_main [get_valueof /x "\$gcspr" 0 "get value of gcspr in main"] +gdb_continue_to_breakpoint "Break call1" ".*Break call1.*" +set gcspr_call1 [get_valueof /x "\$gcspr" 0 "get value of gcspr in call1"] +gdb_continue_to_breakpoint "Break call2" ".*Break call2.*" +set gcspr_call2 [get_valueof /x "\$gcspr" 0 "get value of gcspr in call2"] + +with_test_prefix "test frame level update" { + gdb_test "up" "call1.*" "move to frame 1" + gdb_test "print /x \$gcspr" "= $gcspr_call1" "check gcspr of frame 1" + gdb_test "up" "main.*" "move to frame 2" + gdb_test "print /x \$gcspr" "= $gcspr_main" "check gcspr of frame 2" + gdb_test "frame 0" "call2.*" "move to frame 0" + gdb_test "print /x \$gcspr" "= $gcspr_call2" "check gcspr of frame 0" +} + +with_test_prefix "test return from current frame" { + gdb_test "return (int) 1" "#0.*call1.*" \ + "Test GCS return from current frame" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end +} + +clean_restart ${binfile} +if ![runto_main] { + return +} + +with_test_prefix "test return from past frame" { + gdb_breakpoint ${call2_line} + gdb_continue_to_breakpoint "Break call2" ".*Break call2.*" + + gdb_test "frame 1" ".*in call1.*" + + gdb_test "return (int) 1" "#0.*main.*" \ + "Test GCS return from past frame" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-tdesc-without-linux.xml b/gdb/testsuite/gdb.arch/aarch64-gcs-tdesc-without-linux.xml new file mode 100644 index 0000000..056ab58 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-tdesc-without-linux.xml @@ -0,0 +1,65 @@ +<?xml version="1.0"?> +<!DOCTYPE target SYSTEM "gdb-target.dtd"> +<target> + <architecture>aarch64</architecture> + <feature name="org.gnu.gdb.aarch64.core"> + <flags id="cpsr_flags" size="4"> + <field name="SP" start="0" end="0" type="bool"/> + <field name="EL" start="2" end="3" type="uint32"/> + <field name="nRW" start="4" end="4" type="bool"/> + <field name="F" start="6" end="6" type="bool"/> + <field name="I" start="7" end="7" type="bool"/> + <field name="A" start="8" end="8" type="bool"/> + <field name="D" start="9" end="9" type="bool"/> + <field name="BTYPE" start="10" end="11" type="uint32"/> + <field name="SSBS" start="12" end="12" type="bool"/> + <field name="IL" start="20" end="20" type="bool"/> + <field name="SS" start="21" end="21" type="bool"/> + <field name="PAN" start="22" end="22" type="bool"/> + <field name="UAO" start="23" end="23" type="bool"/> + <field name="DIT" start="24" end="24" type="bool"/> + <field name="TCO" start="25" end="25" type="bool"/> + <field name="V" start="28" end="28" type="bool"/> + <field name="C" start="29" end="29" type="bool"/> + <field name="Z" start="30" end="30" type="bool"/> + <field name="N" start="31" end="31" type="bool"/> + </flags> + <reg name="x0" bitsize="64" type="int" regnum="0"/> + <reg name="x1" bitsize="64" type="int" regnum="1"/> + <reg name="x2" bitsize="64" type="int" regnum="2"/> + <reg name="x3" bitsize="64" type="int" regnum="3"/> + <reg name="x4" bitsize="64" type="int" regnum="4"/> + <reg name="x5" bitsize="64" type="int" regnum="5"/> + <reg name="x6" bitsize="64" type="int" regnum="6"/> + <reg name="x7" bitsize="64" type="int" regnum="7"/> + <reg name="x8" bitsize="64" type="int" regnum="8"/> + <reg name="x9" bitsize="64" type="int" regnum="9"/> + <reg name="x10" bitsize="64" type="int" regnum="10"/> + <reg name="x11" bitsize="64" type="int" regnum="11"/> + <reg name="x12" bitsize="64" type="int" regnum="12"/> + <reg name="x13" bitsize="64" type="int" regnum="13"/> + <reg name="x14" bitsize="64" type="int" regnum="14"/> + <reg name="x15" bitsize="64" type="int" regnum="15"/> + <reg name="x16" bitsize="64" type="int" regnum="16"/> + <reg name="x17" bitsize="64" type="int" regnum="17"/> + <reg name="x18" bitsize="64" type="int" regnum="18"/> + <reg name="x19" bitsize="64" type="int" regnum="19"/> + <reg name="x20" bitsize="64" type="int" regnum="20"/> + <reg name="x21" bitsize="64" type="int" regnum="21"/> + <reg name="x22" bitsize="64" type="int" regnum="22"/> + <reg name="x23" bitsize="64" type="int" regnum="23"/> + <reg name="x24" bitsize="64" type="int" regnum="24"/> + <reg name="x25" bitsize="64" type="int" regnum="25"/> + <reg name="x26" bitsize="64" type="int" regnum="26"/> + <reg name="x27" bitsize="64" type="int" regnum="27"/> + <reg name="x28" bitsize="64" type="int" regnum="28"/> + <reg name="x29" bitsize="64" type="int" regnum="29"/> + <reg name="x30" bitsize="64" type="int" regnum="30"/> + <reg name="sp" bitsize="64" type="data_ptr" regnum="31"/> + <reg name="pc" bitsize="64" type="code_ptr" regnum="32"/> + <reg name="cpsr" bitsize="32" type="cpsr_flags" regnum="33"/> + </feature> + <feature name="org.gnu.gdb.aarch64.gcs"> + <reg name="gcspr" bitsize="64" type="data_ptr" regnum="90" group="system"/> + </feature> +</target> diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.c b/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.c new file mode 100644 index 0000000..10cf749 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.c @@ -0,0 +1,26 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> + +int +main (void) +{ + printf ("Hello, world!\n"); + + return 0; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.exp b/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.exp new file mode 100644 index 0000000..f0508cd --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-wrong-tdesc.exp @@ -0,0 +1,48 @@ +# 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 <http://www.gnu.org/licenses/>. + +# Test that GDB complains when given a target description with the GCS feature +# but not the GCS Linux feature. + +require allow_aarch64_gcs_tests + +standard_testfile + +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } { + return +} + +set xml_path "${srcdir}/${subdir}/aarch64-gcs-tdesc-without-linux.xml" + +gdb_test "set tdesc filename ${xml_path}" \ + "warning: Incomplete GCS support in the target: missing Linux part. GCS feature disabled." \ + "warn about incomplete GCS support" + +# We can't test a debugging session on a remote target because with the +# wrong tdesc, GDB expects a g packet reply with the wrong size. +if {[gdb_protocol_is_remote]} { + return +} + +if ![runto_main] { + return +} + +gdb_test "print \$gcspr" " = <unavailable>" "GCSPR is unavailable" + +# Now check that we can continue the debugging session normally. +gdb_test "next" + +gdb_continue_to_end diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs.c b/gdb/testsuite/gdb.arch/aarch64-gcs.c new file mode 100644 index 0000000..39519e4 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs.c @@ -0,0 +1,180 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/auxv.h> +#include <sys/syscall.h> +#include <linux/prctl.h> + +/* 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; \ + }) + +static unsigned long *handler_gcspr = 0; + +static void +handler (int sig) +{ + handler_gcspr = get_gcspr (); +} + +static int __attribute__ ((unused)) +called_from_gdb (int val) +{ + return val + 1; +} + +/* Corrupt the return address to see if GDB will report a SIGSEGV with the + expected $_siginfo.si_code. */ +static void __attribute__ ((noinline)) +normal_function2 (void) +{ + /* x30 holds the return address. */ + register unsigned long x30 __asm__("x30") __attribute__ ((unused)); + + /* Cause a GCS exception. */ + x30 = 0xbadc0ffee; + /* Use explicit ret so that we can verify that a SIGSEGV was generated + exactly on the return instruction. */ + __asm__ volatile ("ret\n"); +} + +static inline void __attribute__ ((__always_inline__)) +inline_function2 (void) +{ + normal_function2 (); +} + +static void __attribute__ ((noinline)) +normal_function1 (void) +{ + inline_function2 (); +} + +/* First in a sequence of inline and normal functions, to test GDB + backtrace. */ +static inline void __attribute__ ((__always_inline__)) +inline_function1 (void) +{ + normal_function1 (); +} + +/* Trivial function, just so that GDB can test return with wrong GCSPR. */ +static void __attribute__ ((noinline)) +normal_function0 (void) +{ + /* Use explicit ret so that we can verify that a SIGSEGV was generated + exactly on the return instruction. */ + __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; + } + } + + /* Regular function call. */ + normal_function0 (); + + /* This is used by GDB. */ + __attribute__((unused)) unsigned long *gcspr = get_gcspr (); + + struct sigaction act = { 0 }; + + act.sa_handler = &handler; /* Break here. */ + if (sigaction (SIGUSR1, &act, NULL) == -1) + { + perror ("sigaction"); + exit (EXIT_FAILURE); + } + + raise (SIGUSR1); + +/* Call sequence of inline and normal functions, to test GDB backtrace. */ + inline_function1 (); + + /* Avoid returning, in case libc doesn't understand GCS. */ + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.arch/aarch64-gcs.exp b/gdb/testsuite/gdb.arch/aarch64-gcs.exp new file mode 100644 index 0000000..ad73b41 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs.exp @@ -0,0 +1,98 @@ +# 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 <http://www.gnu.org/licenses/>. + +# Test 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 +} + +gdb_test "print \$gcs_features_enabled" \ + [string_to_regexp { = [ PR_SHADOW_STACK_ENABLE ]}] \ + "GCS is enabled" + +gdb_test "print \$gcspr" ". = \\(void \\*\\) $hex" "GDB knows about gcspr" +gdb_test "print \$gcspr == gcspr" ". = 1" "GDB has the correct gcspr value" +gdb_test_no_output "set \$gcspr_in_main = \$gcspr" \ + "save gcspr value in main for later" + +# If the inferior function call fails, we don't want the tests following it +# to be affected. +gdb_test_no_output "set unwindonsignal on" +gdb_test "print called_from_gdb (41)" ". = 42" "call inferior function" + +gdb_test "break handler" "Breakpoint \[0-9\]+ .*aarch64-gcs.c, line \[0-9\]+\\." +gdb_test "handle SIGUSR1 nostop" \ + ".*\r\nSIGUSR1\\s+No\\s+Yes\\s+Yes\\s+User defined signal 1" \ + "let the inferior receive SIGUSR1 uninterrupted" +gdb_test "continue" \ + ".*\r\nBreakpoint \[0-9\]+, handler \\(sig=10\\) at .*aarch64-gcs.c.*handler_gcspr = get_gcspr \\(\\);" \ + "continue to signal handler" + +gdb_test_no_output "set \$gcspr_in_handler = \$gcspr" \ + "save gcspr value in handler for later" +# Select the frame above the <signal handler called> frame, which makes GDB +# unwind the gcspr from the signal frame GCS context. +gdb_test "frame 2" "#2 ($hex in )?\\S+ \\(.*\\) (at|from) \\S+.*" \ + "reached frame 2" +gdb_test "print \$gcspr" ". = \\(void \\*\\) $hex" "gcspr in frame level 2" +gdb_test "print \$gcspr == \$gcspr_in_handler + 8" ". = 1" \ + "gcspr unwound from signal context is correct" + +gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Program received signal SIGSEGV, Segmentation fault" \ + "Guarded Control Stack error\\." \ + "normal_function2 \\(\\) at .*aarch64-gcs.c:$decimal" \ + "${decimal}\\s+__asm__ volatile \\(\"ret\\\\n\"\\);"] \ + "continue to SIGSEGV" + +gdb_test "print \$_siginfo.si_code" ". = 10" \ + "test value of si_code when GCS SIGSEGV happens" +# The GCS grows down, and there are two real frames until main. +gdb_test "print \$gcspr == \$gcspr_in_main - 16" ". = 1" \ + "test value of gcspr when GCS SIGSEGV happens" + +# Test writing to GCSPR. +clean_restart ${binfile} +if ![runto normal_function0] { + return +} + +gdb_test_no_output "set \$gcspr = 0xbadc0ffee" "set bogus gcspr value" +# Continue to make sure that the value was actually written to the register. +# The SIGSEGV isn't a GCS error because the problem isn't that the GCS entry +# doesn't match the return address, but rather that that GCSPR is pointing +# to an invalid address. +gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Program received signal SIGSEGV, Segmentation fault\\." \ + "normal_function0 \\(\\) at .*aarch64-gcs.c:$decimal" \ + "${decimal}\\s+__asm__ volatile \\(\"ret\\\\n\"\\);"] \ + "continue after bad gcspr" diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp b/gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp index 08d73d8..b11efa7 100644 --- a/gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp +++ b/gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp @@ -19,6 +19,7 @@ # instructions. require is_x86_64_m64_target have_avx +require support_displaced_stepping standard_testfile .S diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c index 03b868c..0fb2904 100644 --- a/gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c +++ b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call-alarm.c @@ -16,9 +16,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <unistd.h> +#include <stdlib.h> + +extern void test_call (void); + +void +unreachable (void) +{ + abort (); +} void setup_alarm (void) { alarm (300); } + +int +main () +{ + setup_alarm (); + test_call (); + unreachable (); + return 0; +} diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S index 78a6859..20a8eb7 100644 --- a/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S +++ b/gdb/testsuite/gdb.arch/amd64-disp-step-self-call.S @@ -18,33 +18,12 @@ handling. */ .text - - .global main -main: - nop - - callq setup_alarm - - nop - -/***********************************************/ - -/* test call/ret */ - .global test_call test_call: call test_call - nop + call unreachable .global test_ret_end test_ret_end: nop -/***********************************************/ - -/* all done */ - -done: - mov $0,%rdi - call exit - hlt .section .note.GNU-stack,"",@progbits diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp new file mode 100644 index 0000000..c819cbc --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp @@ -0,0 +1,143 @@ +# Copyright 2024-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 <http://www.gnu.org/licenses/>. + +# Test shadow stack enabling for frame level update, the return and the +# call commands. +# As potential CET violations often only occur after resuming normal +# execution, test normal program continuation after each return or call +# commands. + +require allow_ssp_tests + +standard_testfile amd64-shadow-stack.c + +# Restart GDB an run until breakpoint in call2. + +proc restart_and_run_infcall_call2 {} { + global binfile + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + set inside_infcall_str "The program being debugged stopped while in a function called from GDB" + gdb_breakpoint [ gdb_get_line_number "break call2" ] + gdb_continue_to_breakpoint "break call2" ".*break call2.*" + gdb_test "call (int) call2()" \ + "Breakpoint \[0-9\]*, call2.*$inside_infcall_str.*" +} + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + {debug additional_flags="-fcf-protection=return"}] } { + return -1 + } + + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + + with_test_prefix "test inferior call and continue" { + gdb_breakpoint [ gdb_get_line_number "break call1" ] + gdb_continue_to_breakpoint "break call1" ".*break call1.*" + + gdb_test "call (int) call2()" "= 42" + + gdb_continue_to_end + } + + with_test_prefix "test return inside an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "return" "\#0.*call2.*" \ + "Test shadow stack return inside an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end + } + + with_test_prefix "test return 'above' an inferior call" { + restart_and_run_infcall_call2 + + gdb_test "frame 2" "call2 ().*" "move to frame 'above' inferior call" + + gdb_test "return" "\#0.*call1.*" \ + "Test shadow stack return 'above' an inferior call" \ + "Make.*return now\\? \\(y or n\\) " "y" + + gdb_continue_to_end + } + + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + + set call1_line [ gdb_get_line_number "break call1" ] + set call2_line [ gdb_get_line_number "break call2" ] + + # Extract shadow stack pointer inside main, call1 and call2 function. + gdb_breakpoint $call1_line + gdb_breakpoint $call2_line + set ssp_main [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in main"] + gdb_continue_to_breakpoint "break call1" ".*break call1.*" + set ssp_call1 [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in call1"] + gdb_continue_to_breakpoint "break call2" ".*break call2.*" + set ssp_call2 [get_valueof /x "\$pl3_ssp" 0 "get value of ssp in call2"] + + with_test_prefix "test frame level update" { + gdb_test "up" "call1.*" "move to frame 1" + gdb_test "print /x \$pl3_ssp" "= $ssp_call1" "check pl3_ssp of frame 1" + gdb_test "up" "main.*" "move to frame 2" + gdb_test "print /x \$pl3_ssp" "= $ssp_main" "check pl3_ssp of frame 2" + gdb_test "frame 0" "call2.*" "move to frame 0" + gdb_test "print /x \$pl3_ssp" "= $ssp_call2" "check pl3_ssp of frame 0" + } + + with_test_prefix "test return from current frame" { + gdb_test "return (int) 1" "#0.*call1.*" \ + "Test shadow stack return from current frame" \ + "Make.*return now\\? \\(y or n\\) " "y" + + # Potential CET violations often only occur after resuming normal execution. + # Therefore, it is important to test normal program continuation after + # testing the return command. + gdb_continue_to_end + } + + clean_restart ${binfile} + if { ![runto_main] } { + return -1 + } + + with_test_prefix "test return from past frame" { + gdb_breakpoint $call2_line + gdb_continue_to_breakpoint "break call2" ".*break call2.*" + + gdb_test "frame 1" ".*in call1.*" + + gdb_test "return (int) 1" "#0.*main.*" \ + "Test shadow stack return from past frame" \ + "Make.*return now\\? \\(y or n\\) " "y" + + # Potential CET violations often only occur after resuming normal execution. + # Therefore, it is important to test normal program continuation after + # testing the return command. + gdb_continue_to_end + } +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c new file mode 100644 index 0000000..5e84793 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c @@ -0,0 +1,46 @@ +/* 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 <http://www.gnu.org/licenses/>. */ + +#include <stdio.h> + +/* Call the return instruction before function epilogue to trigger a + control-flow exception. */ +void +function () +{ + unsigned long ssp; + #ifndef __ILP32__ + asm volatile ("xor %0, %0; rdsspq %0" : "=r" (ssp)); + #else + asm volatile ("xor %0, %0; rdsspd %0" : "=r" (ssp)); + #endif + + /* Print ssp to stdout so that the testcase can capture it. */ + printf ("%p\n", (void *) ssp); + fflush (stdout); + + /* Manually cause a control-flow exception by executing a return + instruction before function epilogue, so the address atop the stack + is not the return instruction. */ + __asm__ volatile ("ret\n"); +} + +int +main (void) +{ + function (); /* Break here. */ +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp new file mode 100644 index 0000000..a45cd06 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp @@ -0,0 +1,119 @@ +# Copyright 2024-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 <http://www.gnu.org/licenses/>. + +# Test the shadow stack pointer note in core dumps. +# Based on the corefile tests in gdb.arch/aarch64-gcs-core.exp. + +require allow_ssp_tests + +standard_testfile + +# Make sure GDB can read the given core file correctly. + +proc check_core_file {core_filename saved_pl3_ssp} { + global decimal + + # Load the core file. + if [gdb_test "core $core_filename" \ + [multi_line \ + "Core was generated by .*\\." \ + "Program terminated with signal SIGSEGV, Segmentation fault.*" \ + "#0 function \\(\\) at .*amd64-shadow-stack-corefile.c:$decimal" \ + "$decimal.*__asm__ volatile \\(\"ret\\\\n\"\\);"] \ + "load core file"] { + return + } + + # Check the value of ssp in the core file. + gdb_test "print/x \$pl3_ssp" "\\$\[0-9\]+ = $saved_pl3_ssp" \ + "pl3_ssp contents from core file $saved_pl3_ssp" +} + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" $testfile $srcfile \ + {debug additional_flags="-fcf-protection=return"}] } { + return + } + + set linespec ${srcfile}:[gdb_get_line_number "Break here"] + + if ![runto $linespec] { + return + } + + # Obtain an OS-generated core file. Save test program output to + # ${binfile}.out. + set core_filename [core_find $binfile {} {} "${binfile}.out"] + set core_generated [expr {$core_filename != ""}] + + if {!$core_generated} { + untested "unable to create or find corefile" + } + + # Load the core file and check the value of the shadow stack pointer. + if {$core_generated} { + clean_restart $binfile + + with_test_prefix "OS corefile" { + # Read ssp value from saved output of the test program. + set out_id [open ${binfile}.out "r"] + set ssp_in_gcore [gets $out_id] + close $out_id + check_core_file $core_filename $ssp_in_gcore + } + } + + if ![gcore_cmd_available] { + unsupported "target does not support gcore command." + return + } + + clean_restart $binfile + + 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.*" \ + "function \\(\\) at .*amd64-shadow-stack-corefile.c:$decimal" \ + {.*__asm__ volatile \("ret\\n"\);}] \ + "continue to SIGSEGV" + + set ssp_in_gcore [get_valueof "/x" "\$pl3_ssp" "*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"] + + gdb_assert { $gcore_generated } "gcore corefile created" + if { $gcore_generated } { + clean_restart $binfile + + with_test_prefix "gcore corefile" { + check_core_file $gcore_filename $ssp_in_gcore + } + } +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp new file mode 100644 index 0000000..e4efa00 --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp @@ -0,0 +1,84 @@ +# 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 <http://www.gnu.org/licenses/>. + +# Test continue from call instructions with shadow stack and displaced +# stepping being enabled. + +require allow_ssp_tests support_displaced_stepping + +standard_testfile amd64-shadow-stack.c + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + additional_flags="-fcf-protection=return"] } { + return + } + + # Enable displaced stepping. + gdb_test_no_output "set displaced-stepping on" + gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*" + + if { ![runto_main] } { + return + } + + # Get the address of the call to the call1 function. + set call1_addr -1 + gdb_test_multiple "disassemble main" "" { + -re -wrap "($hex) <\\+($decimal)>:\\s*call\\s*0x.*<call1>.*" { + set call1_addr $expect_out(1,string) + pass $gdb_test_name + } + } + + if { $call1_addr == -1 } { + return + } + + # Get the address of the call to the call2 function. + set call2_addr -1 + gdb_test_multiple "disassemble call1" "" { + -re -wrap "($hex) <\\+($decimal)>:\\s*call\\s*0x.*<call2>.*" { + set call2_addr $expect_out(1,string) + pass $gdb_test_name + } + } + + if { $call2_addr == -1 } { + return + } + + gdb_test "break *$call1_addr" \ + "Breakpoint $decimal at $hex.*" \ + "break at the address of the call1 instruction" + + gdb_test "break *$call2_addr" \ + "Breakpoint $decimal at $hex.*" \ + "break at the address of the call2 instruction" + + gdb_test "continue" \ + "Breakpoint $decimal, $call1_addr in main ().*" \ + "continue until call1 instruction" + + # Test continue from breakpoint at call1 and call2 instructions. + gdb_test "continue" \ + "Breakpoint $decimal, $call2_addr in call1 ().*" \ + "continue from call1 instruction" + + gdb_continue_to_end "continue from call2 instruction" +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c new file mode 100644 index 0000000..4a1ca1e --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c @@ -0,0 +1,40 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2024-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 <http://www.gnu.org/licenses/>. */ + +static int +call2 () +{ + return 42; /* break call2. */ +} + +static int +call1 () +{ + return call2 (); /* break call1. */ +} + +int +main () +{ + /* Depending on instruction generation we might end up in the call + instruction of call1 function after "runto_main". Avoid this by + adding a nop instruction, to simplify the testing in + amd64-shadow-stack-disp-step.exp. */ + asm ("nop"); + call1 (); /* break main. */ + return 0; +} diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack.exp new file mode 100644 index 0000000..a72334a --- /dev/null +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack.exp @@ -0,0 +1,71 @@ +# Copyright 2024-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 <http://www.gnu.org/licenses/>. + +# Test accessing the shadow stack pointer register. + +require allow_ssp_tests + +standard_testfile + +# Write PL3_SSP register with invalid shadow stack pointer value. +proc write_invalid_ssp {} { + gdb_test "print /x \$pl3_ssp = 0x12345678" "= 0x12345678" "set pl3_ssp value" + gdb_test "print /x \$pl3_ssp" "= 0x12345678" "read pl3_ssp value after setting" +} + +save_vars { ::env(GLIBC_TUNABLES) } { + + append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK" + + if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} \ + additional_flags="-fcf-protection=return"] } { + return + } + + if {![runto_main]} { + return + } + + with_test_prefix "invalid ssp" { + write_invalid_ssp + + # Continue until SIGSEV to test that the value is written back to HW. + gdb_test "continue" \ + [multi_line \ + "Continuing\\." \ + "" \ + "Program received signal SIGSEGV, Segmentation fault\\." \ + "$hex in main \\(\\)"] \ + "continue to SIGSEGV" + } + + clean_restart ${binfile} + if { ![runto_main] } { + return + } + + with_test_prefix "restore original ssp" { + # Read PL3_SSP register. + set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read pl3_ssp value"] + + write_invalid_ssp + + # Restore original value. + gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main" "restore original value" + + # Now we should not see a SIGSEV, since the original value is restored. + gdb_continue_to_end + } +} diff --git a/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp index dcee040..5663b0d 100644 --- a/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp +++ b/gdb/testsuite/gdb.arch/amd64-watchpoint-downgrade.exp @@ -58,7 +58,7 @@ gdb_test "starti" \ [multi_line \ "warning: watchpoint $num downgraded to software watchpoint" \ "" \ - "Program stopped\\." \ + "(Program|Thread \[^\r\n\]) stopped\\." \ ".*"] # Watchpoint should now have downgraded to a s/w watchpoint. diff --git a/gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c b/gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c index 03b868c..0fb2904 100644 --- a/gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c +++ b/gdb/testsuite/gdb.arch/i386-disp-step-self-call-alarm.c @@ -16,9 +16,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <unistd.h> +#include <stdlib.h> + +extern void test_call (void); + +void +unreachable (void) +{ + abort (); +} void setup_alarm (void) { alarm (300); } + +int +main () +{ + setup_alarm (); + test_call (); + unreachable (); + return 0; +} diff --git a/gdb/testsuite/gdb.arch/i386-disp-step-self-call.S b/gdb/testsuite/gdb.arch/i386-disp-step-self-call.S index 466e50c..20a8eb7 100644 --- a/gdb/testsuite/gdb.arch/i386-disp-step-self-call.S +++ b/gdb/testsuite/gdb.arch/i386-disp-step-self-call.S @@ -18,33 +18,12 @@ handling. */ .text - - .global main -main: - nop - - call setup_alarm - - nop - -/***********************************************/ - -/* test call/ret */ - .global test_call test_call: call test_call - nop + call unreachable .global test_ret_end test_ret_end: nop -/***********************************************/ - -/* all done */ - -done: - pushl $0 - call exit - hlt .section .note.GNU-stack,"",@progbits diff --git a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c new file mode 100644 index 0000000..f55cee5 --- /dev/null +++ b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection-stackalign.c @@ -0,0 +1,27 @@ +/* This testcase 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 <http://www.gnu.org/licenses/>. */ + +#include <alloca.h> + +int +main (int argc, char **argv) +{ + volatile __attribute__ ((__aligned__ (64))) int a; + volatile char *p = (char *) alloca (argc * 12); + p[2] = 'b'; + return 1; +} diff --git a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp index eb93127..06285ce 100644 --- a/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp +++ b/gdb/testsuite/gdb.arch/i386-prologue-skip-cf-protection.exp @@ -19,41 +19,65 @@ # This option places an `endbr32`/`endbr64` instruction at the start of # all functions, which can interfere with prologue analysis. -standard_testfile .c -set binfile ${binfile} +standard_testfile .c -stackalign.c require {is_any_target x86_64-*-* i?86-*-*} - require supports_fcf_protection -set opts {debug additional_flags=-fcf-protection=full} +# Tests if breakpoint set on main is placed past main's entry. +proc test_run {} { + # Get start address of function main. + set main_addr [get_integer_valueof &main -1] + gdb_assert {$main_addr != -1} -if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $opts] != "" } { - untested "failed to compile" - return -} + set bp_addr -1 -clean_restart ${binfile} + # Put breakpoint on main, get the address where the breakpoint was installed. + gdb_test_multiple "break -q main" "break on main, get address" { + -re -wrap "Breakpoint $::decimal at ($::hex).*" { + set bp_addr $expect_out(1,string) -# Get start address of function main. -set main_addr [get_integer_valueof &main -1] -gdb_assert {$main_addr != -1} + # Convert to decimal. + set bp_addr [expr $bp_addr] -set bp_addr -1 + pass $gdb_test_name + } + } -# Put breakpoint on main, get the address where the breakpoint was installed. -gdb_test_multiple "break -q main" "break on main, get address" { - -re -wrap "Breakpoint $decimal at ($hex).*" { - set bp_addr $expect_out(1,string) + # Make sure some prologue was skipped. + gdb_assert {$bp_addr != -1 && $bp_addr > $main_addr} \ + "breakpoint placed past main's entry" +} - # Convert to decimal. - set bp_addr [expr $bp_addr] +with_test_prefix "skip-cf-protection" { + set opts {debug additional_flags=-fcf-protection=full} - pass $gdb_test_name + if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable \ + $opts] != "" } { + untested "failed to compile" + return } + + clean_restart ${binfile} + + test_run } -if { $bp_addr != -1 } { - # Make sure some prologue was skipped. - gdb_assert {$bp_addr > $main_addr} +# Now, make sure that the prologue analysis does not end up at function's entry +# when stack alignment sequence is generated right after 'endbr64'/'endbr32'. +# That could happen if GDB handled those incorrectly - there was a bug that +# checked for those two in incorrect order, which caused such issue. +with_test_prefix "skip-cf-protection-stackalign" { + # gcc is easier to make it produce the sequence of interest. + if { ![is_c_compiler_gcc] } { + unsupported "stackalign test part requires gcc compiler" + return + } + + if { [prepare_for_testing "failed to prepare" "${testfile}-stackalign" \ + $srcfile2 [list optimize=-O0 additional_flags=-fcf-protection=full]] } { + return + } + + test_run } |