Message ID | 20250608010338.2234530-9-thiago.bauermann@linaro.org |
---|---|
State | New |
Headers | show |
Series | AArch64 Guarded Control Stack support | expand |
On 6/8/25 02:03, Thiago Jung Bauermann wrote: > It exercises GCS with displaced stepping by putting the breakpoint on > the bl instruction to force GDB to copy it to the displaced stepping > buffer. > > In this case GDB needs to manually manage the Guarded Control Stack. > --- > .../gdb.arch/aarch64-gcs-disp-step.c | 140 ++++++++++++++++++ > .../gdb.arch/aarch64-gcs-disp-step.exp | 90 +++++++++++ > 2 files changed, 230 insertions(+) > create mode 100644 gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.c > create mode 100644 gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp > > 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 000000000000..3d895350ae42 > --- /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 000000000000..10b09a4d980e > --- /dev/null > +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp > @@ -0,0 +1,90 @@ > +# 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 "$::gdb_prompt \$" { > + 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 "$::gdb_prompt \$" { > + 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_test "continue" \ > + [multi_line \ > + "Continuing\\." \ > + "\\\[Inferior 1 \\(process $decimal\\) exited normally\\\]"] \ > + "continue until inferior exits" Thanks. This is OK. I saw elsewhere that you're going to merge the tests into their own series entry. I'll wait for that one before OK-ing it.
Luis Machado <luis.machado@arm.com> writes: > On 6/8/25 02:03, Thiago Jung Bauermann wrote: >> It exercises GCS with displaced stepping by putting the breakpoint on >> the bl instruction to force GDB to copy it to the displaced stepping >> buffer. >> >> In this case GDB needs to manually manage the Guarded Control Stack. >> --- >> .../gdb.arch/aarch64-gcs-disp-step.c | 140 ++++++++++++++++++ >> .../gdb.arch/aarch64-gcs-disp-step.exp | 90 +++++++++++ >> 2 files changed, 230 insertions(+) >> create mode 100644 gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.c >> create mode 100644 gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp > > Thanks. This is OK. I saw elsewhere that you're going to merge the tests into > their own series entry. I'll wait for that one before OK-ing it. Thanks! That is correct, I'll change the patches so that each patch adds the tests that verify the changes that it makes.
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 000000000000..3d895350ae42 --- /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 000000000000..10b09a4d980e --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-gcs-disp-step.exp @@ -0,0 +1,90 @@ +# 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 "$::gdb_prompt \$" { + 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 "$::gdb_prompt \$" { + 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_test "continue" \ + [multi_line \ + "Continuing\\." \ + "\\\[Inferior 1 \\(process $decimal\\) exited normally\\\]"] \ + "continue until inferior exits"