Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v5 00/12] Add CET shadow stack support
@ 2025-06-28  8:27 Christina Schimpe
  2025-06-28  8:27 ` [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save program output Christina Schimpe
                   ` (14 more replies)
  0 siblings, 15 replies; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:27 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi all,

this is my v5 of the series to add amd64 shadow stack support to GDB on linux.
It addresses the feedback of Luis.

v4 can be found here:
https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html

Changes since v4:
- Improve some comments.
- Change the test in "gdb: amd64 linux coredump support with shadow
  stack." to also test core file generated by the linux kernel.  This
  requires changes for the core_find procedure to save program output,
  that have been implemented by Thiago already, so we include this part
  of the patch in this series: "gdb, testsuite: Extend core_find procedure
  to save program output.".  The test is now very similar to the test
  implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
  providing the input here!

I am looking forward to your feedback!

Regards,

Christina

Christina Schimpe (12):
  gdb, testsuite: Extend core_find procedure to save program output.
  gdbserver: Add optional runtime register set type.
  gdbserver: Add assert in x86_linux_read_description.
  gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
  gdb, gdbserver: Use xstate_bv for target description creation on x86.
  gdb, gdbserver: Add support of Intel shadow stack pointer register.
  gdb: amd64 linux coredump support with shadow stack.
  gdb: Handle shadow stack pointer register unwinding for amd64 linux.
  gdb, gdbarch: Enable inferior calls for shadow stack support.
  gdb: Implement amd64 linux shadow stack support for inferior calls.
  gdb, gdbarch: Introduce gdbarch method to get the shadow stack
    pointer.
  gdb: Enable displaced stepping with shadow stack on amd64 linux.

 gdb/NEWS                                      |   6 +
 gdb/amd64-linux-nat.c                         |  17 ++
 gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
 gdb/amd64-tdep.c                              |  35 ++-
 gdb/amd64-tdep.h                              |   9 +-
 gdb/arch-utils.c                              |  10 +
 gdb/arch-utils.h                              |   5 +
 gdb/arch/amd64-linux-tdesc.c                  |  33 +--
 gdb/arch/amd64-linux-tdesc.h                  |   7 +-
 gdb/arch/amd64.c                              |  25 +-
 gdb/arch/amd64.h                              |  10 +-
 gdb/arch/i386-linux-tdesc.c                   |  29 +--
 gdb/arch/i386-linux-tdesc.h                   |   5 +-
 gdb/arch/i386.c                               |  19 +-
 gdb/arch/i386.h                               |   8 +-
 gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
 gdb/arch/x86-linux-tdesc-features.h           |  25 +-
 gdb/doc/gdb.texinfo                           |  42 ++++
 gdb/features/Makefile                         |   2 +
 gdb/features/i386/32bit-ssp.c                 |  14 ++
 gdb/features/i386/32bit-ssp.xml               |  11 +
 gdb/features/i386/64bit-ssp.c                 |  14 ++
 gdb/features/i386/64bit-ssp.xml               |  11 +
 gdb/gdbarch-gen.c                             |  54 +++++
 gdb/gdbarch-gen.h                             |  24 ++
 gdb/gdbarch_components.py                     |  31 +++
 gdb/i386-tdep.c                               |  51 +++-
 gdb/i386-tdep.h                               |  11 +-
 gdb/infcall.c                                 |  14 +-
 gdb/linux-tdep.c                              |  47 ++++
 gdb/linux-tdep.h                              |   7 +
 gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
 gdb/nat/x86-linux-tdesc.c                     |  20 +-
 gdb/nat/x86-linux-tdesc.h                     |   7 +-
 gdb/nat/x86-linux.c                           |  57 +++++
 gdb/nat/x86-linux.h                           |   4 +
 .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
 .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
 .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
 .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
 gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
 gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
 .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
 gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
 gdb/x86-linux-nat.c                           |  50 +++-
 gdb/x86-linux-nat.h                           |  11 +
 gdb/x86-tdep.c                                |  21 ++
 gdb/x86-tdep.h                                |   9 +
 gdbserver/i387-fp.cc                          |  40 ++--
 gdbserver/linux-amd64-ipa.cc                  |  10 +-
 gdbserver/linux-i386-ipa.cc                   |   6 +-
 gdbserver/linux-low.cc                        |  50 ++--
 gdbserver/linux-low.h                         |   7 +-
 gdbserver/linux-x86-low.cc                    |  44 +++-
 gdbsupport/x86-xstate.h                       |   7 +-
 55 files changed, 1687 insertions(+), 217 deletions(-)
 create mode 100644 gdb/features/i386/32bit-ssp.c
 create mode 100644 gdb/features/i386/32bit-ssp.xml
 create mode 100644 gdb/features/i386/64bit-ssp.c
 create mode 100644 gdb/features/i386/64bit-ssp.xml
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp

-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save program output.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
@ 2025-06-28  8:27 ` Christina Schimpe
  2025-07-14 12:21   ` Andrew Burgess
  2025-06-28  8:28 ` [PATCH v5 02/12] gdbserver: Add optional runtime register set type Christina Schimpe
                   ` (13 subsequent siblings)
  14 siblings, 1 reply; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:27 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>

The change comes from ARM's GCS series:

[PATCH v3 5/9] GDB, gdbserver: aarch64-linux: Initial Guarded Control Stack support.

We need it for testing coredmp files, too.  So include it in this patch series.

Abridged-by: Christina Schimpe <christina.schimpe@intel.com>
---
This is the patch mentioned above:

https://sourceware.org/pipermail/gdb-patches/2025-June/218892.html

Minus everything except for the change in gdb.exp's corefind procedure.
---
 gdb/testsuite/lib/gdb.exp | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 3f1cd55d727..bb17c4e91a5 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -9352,7 +9352,13 @@ proc remove_core {pid {test ""}} {
     }
 }
 
-proc core_find {binfile {deletefiles {}} {arg ""}} {
+# Runs ${binfile} expecting it to crash and generate a core file.
+# If DELETEFILES is provided, remove these files after running the program.
+# If ARG is provided, pass it as a command line argument to the program.
+# If OUTPUT_FILE is provided, save the program output to it.
+# Returns the name of the core dump, or empty string if not found.
+
+proc core_find {binfile {deletefiles {}} {arg ""} {output_file "/dev/null"}} {
     global objdir subdir
 
     set destcore "$binfile.core"
@@ -9374,7 +9380,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}"
     set binfile_basename [file tail $binfile]
     foreach i [list \
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 02/12] gdbserver: Add optional runtime register set type.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
  2025-06-28  8:27 ` [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save program output Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-06-28  8:28 ` [PATCH v5 03/12] gdbserver: Add assert in x86_linux_read_description Christina Schimpe
                   ` (12 subsequent siblings)
  14 siblings, 0 replies; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

Some register sets can be activated and deactivated by the OS during the runtime of
a process.  One example register is the Intel CET shadow stack pointer.  This patch
adds a new type of register set to handle such cases.  We shouldn't deactivate these
regsets and should not show a warning if the register set is not active but supported
by the kernel.  However, it is safe to deactivate them, if they are unsupported by the
kernel.  To differentiate those scenarios we can use the errno returned by the ptrace
call.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
 gdbserver/linux-low.cc | 50 ++++++++++++++++++++++++++++++------------
 gdbserver/linux-low.h  |  7 +++++-
 2 files changed, 42 insertions(+), 15 deletions(-)

diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 39642705b0d..e8c4eb8a78d 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -5006,23 +5006,31 @@ regsets_fetch_inferior_registers (struct regsets_info *regsets_info,
       if (res < 0)
 	{
 	  if (errno == EIO
-	      || (errno == EINVAL && regset->type == OPTIONAL_REGS))
+	      || (errno == EINVAL
+		  && (regset->type == OPTIONAL_REGS
+		      || regset->type == OPTIONAL_RUNTIME_REGS)))
 	    {
 	      /* If we get EIO on a regset, or an EINVAL and the regset is
-		 optional, do not try it again for this process mode.  */
+		 optional, do not try it again for this process mode.
+		 Even if the regset can be enabled at runtime it is safe
+		 to deactivate the regset in case of EINVAL, as we know
+		 the regset itself was the invalid argument of the ptrace
+		 call which means that it's unsupported by the kernel.  */
 	      disable_regset (regsets_info, regset);
 	    }
-	  else if (errno == ENODATA)
+	  else if (errno == ENODATA
+		   || (errno == ENODEV
+		       && regset->type == OPTIONAL_RUNTIME_REGS)
+		   || errno == ESRCH)
 	    {
-	      /* ENODATA may be returned if the regset is currently
-		 not "active".  This can happen in normal operation,
-		 so suppress the warning in this case.  */
-	    }
-	  else if (errno == ESRCH)
-	    {
-	      /* At this point, ESRCH should mean the process is
-		 already gone, in which case we simply ignore attempts
-		 to read its registers.  */
+	      /* ENODATA or ENODEV may be returned if the regset is
+		 currently not "active".  For ENODEV we additionally check
+		 if the register set is of type OPTIONAL_RUNTIME_REGS.
+		 This can happen in normal operation, so suppress the
+		 warning in this case.
+		 ESRCH should mean the process is already gone at this
+		 point, in which case we simply ignore attempts to read
+		 its registers.  */
 	    }
 	  else
 	    {
@@ -5104,12 +5112,26 @@ regsets_store_inferior_registers (struct regsets_info *regsets_info,
       if (res < 0)
 	{
 	  if (errno == EIO
-	      || (errno == EINVAL && regset->type == OPTIONAL_REGS))
+	      || (errno == EINVAL
+		   && (regset->type == OPTIONAL_REGS
+		       || regset->type == OPTIONAL_RUNTIME_REGS)))
 	    {
 	      /* If we get EIO on a regset, or an EINVAL and the regset is
-		 optional, do not try it again for this process mode.  */
+		 optional, do not try it again for this process mode.
+		 Even if the regset can be enabled at runtime it is safe
+		 to deactivate the regset in case of EINVAL, as we know
+		 the regset itself was the invalid argument of the ptrace
+		 call which means that it's unsupported by the kernel.  */
 	      disable_regset (regsets_info, regset);
 	    }
+	  else if (errno == ENODEV
+		   && regset->type == OPTIONAL_RUNTIME_REGS)
+	    {
+	      /* If we get ENODEV on a regset and the regset can be
+		 enabled at runtime try it again for this process mode.
+		 This can happen in normal operation, so suppress the
+		 warning in this case.  */
+	    }
 	  else if (errno == ESRCH)
 	    {
 	      /* At this point, ESRCH should mean the process is
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index e1c88ee0bb2..5710e49d950 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -42,7 +42,12 @@ enum regset_type {
   GENERAL_REGS,
   FP_REGS,
   EXTENDED_REGS,
-  OPTIONAL_REGS, /* Do not error if the regset cannot be accessed.  */
+  OPTIONAL_REGS, /* Do not error if the regset cannot be accessed.
+		    Disable the regset instead.  */
+  OPTIONAL_RUNTIME_REGS, /* Some optional regsets can only be accessed
+			    dependent on the execution flow.  For such
+			    access errors don't show a warning and don't
+			    disable the regset.  */
 };
 
 /* The arch's regsets array initializer must be terminated with a NULL
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 03/12] gdbserver: Add assert in x86_linux_read_description.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
  2025-06-28  8:27 ` [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save program output Christina Schimpe
  2025-06-28  8:28 ` [PATCH v5 02/12] gdbserver: Add optional runtime register set type Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-06-28  8:28 ` [PATCH v5 04/12] gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch Christina Schimpe
                   ` (11 subsequent siblings)
  14 siblings, 0 replies; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

On x86 the PTRACE_GETREGSET request is currently only used for the xstate regset.
The size of the xstate regset is initialized to 0 such that it can be reset to
the appropriate size once we know it is supported for the current target
in x86_linux_read_description.

However, this configuration would not just affect the xstate regset but any regset
with PTRACE_GETREGSET request that is added in the future.  The new regset  would be
misconfigured with the xstate regset size.  To avoid this we add an assert for
unsupported regsets and check explicitly for the note type of the register set.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
 gdbserver/linux-x86-low.cc | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 918630d4b61..24920e71a53 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -894,7 +894,12 @@ x86_linux_read_description ()
 	   regset++)
 	{
 	  if (regset->get_request == PTRACE_GETREGSET)
-	    regset->size = xsave_len;
+	    {
+	      if (regset->nt_type == NT_X86_XSTATE)
+		regset->size = xsave_len;
+	      else
+		gdb_assert_not_reached ("invalid regset type.");
+	    }
 	  else if (regset->type != GENERAL_REGS)
 	    regset->size = 0;
 	}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 04/12] gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (2 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 03/12] gdbserver: Add assert in x86_linux_read_description Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-06-28  8:28 ` [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86 Christina Schimpe
                   ` (10 subsequent siblings)
  14 siblings, 0 replies; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

This is required for a later commit which requires "bit_SHSTK".

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Approved-By: Tom Tromey <tom@tromey.com>
---
 gdb/nat/x86-gcc-cpuid.h | 153 ++++++++++++++++++++++++++++++++--------
 1 file changed, 122 insertions(+), 31 deletions(-)

diff --git a/gdb/nat/x86-gcc-cpuid.h b/gdb/nat/x86-gcc-cpuid.h
index 14981005489..9e27fd88127 100644
--- a/gdb/nat/x86-gcc-cpuid.h
+++ b/gdb/nat/x86-gcc-cpuid.h
@@ -1,5 +1,5 @@
 /*
- * Helper cpuid.h file copied from gcc-6.0.0.  Code in gdb should not
+ * Helper cpuid.h file copied from gcc-14.2.0.  Code in gdb should not
  * include this directly, but pull in x86-cpuid.h and use that func.
  */
 
@@ -55,7 +55,7 @@
 #define bit_SSE		(1 << 25)
 #define bit_SSE2	(1 << 26)
 
-/* Extended Features */
+/* Extended Features (%eax == 0x80000001) */
 /* %ecx */
 #define bit_LAHF_LM	(1 << 0)
 #define bit_ABM		(1 << 5)
@@ -68,29 +68,28 @@
 #define bit_MWAITX      (1 << 29)
 
 /* %edx */
-#define bit_AVX5124VNNIW (1 << 2)
-#define bit_AVX5124FMAPS (1 << 3)
 #define bit_MMXEXT	(1 << 22)
 #define bit_LM		(1 << 29)
 #define bit_3DNOWP	(1 << 30)
-#define bit_3DNOW	(1 << 31)
+#define bit_3DNOW	(1u << 31)
 
-/* %ebx.  */
+/* %ebx  */
 #define bit_CLZERO	(1 << 0)
+#define bit_WBNOINVD	(1 << 9)
 
-/* Extended Features (%eax == 7) */
+/* Extended Features Leaf (%eax == 7, %ecx == 0) */
 /* %ebx */
 #define bit_FSGSBASE	(1 << 0)
-#define bit_BMI	(1 << 3)
-#define bit_HLE	(1 << 4)
+#define bit_SGX		(1 << 2)
+#define bit_BMI		(1 << 3)
+#define bit_HLE		(1 << 4)
 #define bit_AVX2	(1 << 5)
 #define bit_BMI2	(1 << 8)
-#define bit_RTM	(1 << 11)
-#define bit_MPX	(1 << 14)
+#define bit_RTM		(1 << 11)
 #define bit_AVX512F	(1 << 16)
 #define bit_AVX512DQ	(1 << 17)
 #define bit_RDSEED	(1 << 18)
-#define bit_ADX	(1 << 19)
+#define bit_ADX		(1 << 19)
 #define bit_AVX512IFMA	(1 << 21)
 #define bit_CLFLUSHOPT	(1 << 23)
 #define bit_CLWB	(1 << 24)
@@ -99,23 +98,85 @@
 #define bit_AVX512CD	(1 << 28)
 #define bit_SHA		(1 << 29)
 #define bit_AVX512BW	(1 << 30)
-#define bit_AVX512VL	(1 << 31)
+#define bit_AVX512VL	(1u << 31)
 
 /* %ecx */
-#define bit_PREFETCHWT1	  (1 << 0)
+#define bit_PREFETCHWT1	(1 << 0)
 #define bit_AVX512VBMI	(1 << 1)
-#define bit_PKU	(1 << 3)
+#define bit_PKU		(1 << 3)
 #define bit_OSPKE	(1 << 4)
+#define bit_WAITPKG	(1 << 5)
+#define bit_AVX512VBMI2	(1 << 6)
+#define bit_SHSTK	(1 << 7)
+#define bit_GFNI	(1 << 8)
+#define bit_VAES	(1 << 9)
+#define bit_VPCLMULQDQ	(1 << 10)
+#define bit_AVX512VNNI	(1 << 11)
+#define bit_AVX512BITALG	(1 << 12)
+#define bit_AVX512VPOPCNTDQ	(1 << 14)
+#define bit_RDPID	(1 << 22)
+#define bit_KL		(1 << 23)
+#define bit_CLDEMOTE	(1 << 25)
+#define bit_MOVDIRI	(1 << 27)
+#define bit_MOVDIR64B	(1 << 28)
+#define bit_ENQCMD	(1 << 29)
 
-/* XFEATURE_ENABLED_MASK register bits (%eax == 13, %ecx == 0) */
-#define bit_BNDREGS     (1 << 3)
-#define bit_BNDCSR      (1 << 4)
+/* %edx */
+#define bit_AVX5124VNNIW	(1 << 2)
+#define bit_AVX5124FMAPS	(1 << 3)
+#define bit_UINTR	(1 << 5)
+#define bit_AVX512VP2INTERSECT	(1 << 8)
+#define bit_SERIALIZE	(1 << 14)
+#define bit_TSXLDTRK    (1 << 16)
+#define bit_PCONFIG	(1 << 18)
+#define bit_IBT         (1 << 20)
+#define bit_AMX_BF16    (1 << 22)
+#define bit_AVX512FP16	(1 << 23)
+#define bit_AMX_TILE    (1 << 24)
+#define bit_AMX_INT8    (1 << 25)
+
+/* Extended Features Sub-leaf (%eax == 7, %ecx == 1) */
+/* %eax */
+#define bit_SHA512	(1 << 0)
+#define bit_SM3		(1 << 1)
+#define bit_SM4		(1 << 2)
+#define bit_RAOINT      (1 << 3)
+#define bit_AVXVNNI     (1 << 4)
+#define bit_AVX512BF16  (1 << 5)
+#define bit_CMPCCXADD   (1 << 7)
+#define bit_AMX_COMPLEX (1 << 8)
+#define bit_AMX_FP16    (1 << 21)
+#define bit_HRESET      (1 << 22)
+#define bit_AVXIFMA     (1 << 23)
 
-/* Extended State Enumeration Sub-leaf (%eax == 13, %ecx == 1) */
+/* %edx */
+#define bit_AVXVNNIINT8 (1 << 4)
+#define bit_AVXNECONVERT	(1 << 5)
+#define bit_AVXVNNIINT16	(1 << 10)
+#define bit_PREFETCHI	(1 << 14)
+#define bit_USER_MSR	(1 << 15)
+#define bit_AVX10	(1 << 19)
+#define bit_APX_F	(1 << 21)
+
+/* Extended State Enumeration Sub-leaf (%eax == 0xd, %ecx == 1) */
 #define bit_XSAVEOPT	(1 << 0)
 #define bit_XSAVEC	(1 << 1)
 #define bit_XSAVES	(1 << 3)
 
+/* PT sub leaf (%eax == 0x14, %ecx == 0) */
+/* %ebx */
+#define bit_PTWRITE	(1 << 4)
+
+/* Keylocker leaf (%eax == 0x19) */
+/* %ebx */
+#define bit_AESKLE	( 1<<0 )
+#define bit_WIDEKL	( 1<<2 )
+
+/* AVX10 sub leaf (%eax == 0x24) */
+/* %ebx */
+#define bit_AVX10_256	(1 << 17)
+#define bit_AVX10_512	(1 << 18)
+
 /* Signatures for different CPU implementations as returned in uses
    of cpuid with level 0.  */
 #define signature_AMD_ebx	0x68747541
@@ -170,19 +231,40 @@
 #define signature_VORTEX_ecx	0x436f5320
 #define signature_VORTEX_edx	0x36387865
 
-#define __cpuid(level, a, b, c, d)			\
-  __asm__ ("cpuid\n\t"					\
-	   : "=a" (a), "=b" (b), "=c" (c), "=d" (d)	\
-	   : "0" (level))
+#define signature_SHANGHAI_ebx	0x68532020
+#define signature_SHANGHAI_ecx	0x20206961
+#define signature_SHANGHAI_edx	0x68676e61
 
-#define __cpuid_count(level, count, a, b, c, d)		\
-  __asm__ ("cpuid\n\t"					\
-	   : "=a" (a), "=b" (b), "=c" (c), "=d" (d)	\
-	   : "0" (level), "2" (count))
+#ifndef __x86_64__
+/* At least one cpu (Winchip 2) does not set %ebx and %ecx
+   for cpuid leaf 1. Forcibly zero the two registers before
+   calling cpuid as a precaution.  */
+#define __cpuid(level, a, b, c, d)					\
+  do {									\
+    if (__builtin_constant_p (level) && (level) != 1)			\
+      __asm__ __volatile__ ("cpuid\n\t"					\
+			    : "=a" (a), "=b" (b), "=c" (c), "=d" (d)	\
+			    : "0" (level));				\
+    else								\
+      __asm__ __volatile__ ("cpuid\n\t"					\
+			    : "=a" (a), "=b" (b), "=c" (c), "=d" (d)	\
+			    : "0" (level), "1" (0), "2" (0));		\
+  } while (0)
+#else
+#define __cpuid(level, a, b, c, d)					\
+  __asm__ __volatile__ ("cpuid\n\t"					\
+			: "=a" (a), "=b" (b), "=c" (c), "=d" (d)	\
+			: "0" (level))
+#endif
+
+#define __cpuid_count(level, count, a, b, c, d)				\
+  __asm__ __volatile__ ("cpuid\n\t"					\
+			: "=a" (a), "=b" (b), "=c" (c), "=d" (d)	\
+			: "0" (level), "2" (count))
 
 
 /* Return highest supported input value for cpuid instruction.  ext can
-   be either 0x0 or 0x8000000 to return highest supported value for
+   be either 0x0 or 0x80000000 to return highest supported value for
    basic or extended cpuid information.  Function returns 0 if cpuid
    is not supported or whatever cpuid returns in eax register.  If sig
    pointer is non-null, then first four bytes of the signature
@@ -225,7 +307,7 @@ __get_cpuid_max (unsigned int __ext, unsigned int *__sig)
 	   : "i" (0x00200000));
 #endif
 
-  if (!((__eax ^ __ebx) & 0x00200000))
+  if (__builtin_expect (!((__eax ^ __ebx) & 0x00200000), 0))
     return 0;
 #endif
 
@@ -249,8 +331,9 @@ __get_cpuid (unsigned int __leaf,
 	     unsigned int *__ecx, unsigned int *__edx)
 {
   unsigned int __ext = __leaf & 0x80000000;
+  unsigned int __maxlevel = __get_cpuid_max (__ext, 0);
 
-  if (__get_cpuid_max (__ext, 0) < __leaf)
+  if (__maxlevel == 0 || __maxlevel < __leaf)
     return 0;
 
   __cpuid (__leaf, *__eax, *__ebx, *__ecx, *__edx);
@@ -265,12 +348,20 @@ __get_cpuid_count (unsigned int __leaf, unsigned int __subleaf,
 		   unsigned int *__ecx, unsigned int *__edx)
 {
   unsigned int __ext = __leaf & 0x80000000;
+  unsigned int __maxlevel = __get_cpuid_max (__ext, 0);
 
-  if (__get_cpuid_max (__ext, 0) < __leaf)
+  if (__builtin_expect (__maxlevel == 0, 0) || __maxlevel < __leaf)
     return 0;
 
   __cpuid_count (__leaf, __subleaf, *__eax, *__ebx, *__ecx, *__edx);
   return 1;
 }
 
+static __inline void
+__cpuidex (int __cpuid_info[4], int __leaf, int __subleaf)
+{
+  __cpuid_count (__leaf, __subleaf, __cpuid_info[0], __cpuid_info[1],
+		 __cpuid_info[2], __cpuid_info[3]);
+}
+
 #endif /* GDB_NAT_X86_GCC_CPUID_H */
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (3 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 04/12] gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-07-14 13:52   ` Andrew Burgess
  2025-06-28  8:28 ` [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register Christina Schimpe
                   ` (9 subsequent siblings)
  14 siblings, 1 reply; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

The XSAVE features set is organized in state components, which are a set
of or parts of registers.  So-called XSAVE-supported features are
organized using state-component bitmaps, each bit corresponding to a
single state component.

The SDM uses the term xstate_bv for a state-component bitmap, which is
defined as XCR0 | IA32_XSS.  The control register XCR0 only contains a
state-component bitmap that specifies user state components, while IA32_XSS
contains a state-component bitmap that specifies supervisor state components.

Until now, XCR0 is used as input for target description creation in GDB.
However, a following patch will add userspace support for the CET shadow
stack feature by Intel.  The CET state is configured in IA32_XSS and consists
of 2 state components:
- State component 11 used for the 2 MSRs controlling user-mode
  functionality for CET (CET_U state)
- State component 12 used for the 3 MSRs containing shadow-stack pointers
  for privilege levels 0-2 (CET_S state).

Reading the CET shadow stack pointer register on linux requires a separate
ptrace call using NT_X86_SHSTK.  To pass the CET shadow stack enablement
state we would like to pass the xstate_bv value instead of xcr0 for target
description creation.  To prepare for that, we rename the xcr0 mask
values for target description creation to xstate_bv.  However, this
patch doesn't add any functional changes in GDB.

Future states specified in IA32_XSS such as CET will create a combined
xstate_bv_mask including xcr0 register value and its corresponding bit in
the state component bitmap.  This combined mask will then be used to create
the target descriptions.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
 gdb/amd64-tdep.c                    | 14 +++----
 gdb/amd64-tdep.h                    |  8 +++-
 gdb/arch/amd64-linux-tdesc.c        | 33 ++++++++--------
 gdb/arch/amd64-linux-tdesc.h        |  7 ++--
 gdb/arch/amd64.c                    | 15 +++-----
 gdb/arch/amd64.h                    | 10 ++++-
 gdb/arch/i386-linux-tdesc.c         | 29 +++++++-------
 gdb/arch/i386-linux-tdesc.h         |  5 ++-
 gdb/arch/i386.c                     | 15 ++++----
 gdb/arch/i386.h                     |  8 +++-
 gdb/arch/x86-linux-tdesc-features.c | 59 +++++++++++++++--------------
 gdb/arch/x86-linux-tdesc-features.h | 25 +++++++-----
 gdb/i386-tdep.c                     | 14 +++----
 gdb/i386-tdep.h                     |  7 +++-
 gdb/nat/x86-linux-tdesc.c           | 18 +++++----
 gdb/nat/x86-linux-tdesc.h           |  7 ++--
 gdb/x86-linux-nat.c                 | 11 ++++--
 gdbserver/i387-fp.cc                | 40 +++++++++----------
 gdbserver/linux-amd64-ipa.cc        | 10 +++--
 gdbserver/linux-i386-ipa.cc         |  6 +--
 gdbserver/linux-x86-low.cc          |  9 ++---
 gdbsupport/x86-xstate.h             |  4 +-
 22 files changed, 198 insertions(+), 156 deletions(-)

diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 82dd1e07cf3..04539dd288a 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -3551,23 +3551,23 @@ amd64_x32_none_init_abi (gdbarch_info info, gdbarch *arch)
 		      amd64_target_description (X86_XSTATE_SSE_MASK, true));
 }
 
-/* Return the target description for a specified XSAVE feature mask.  */
+/* See amd64-tdep.h.  */
 
 const struct target_desc *
-amd64_target_description (uint64_t xcr0, bool segments)
+amd64_target_description (uint64_t xstate_bv_mask, bool segments)
 {
   static target_desc *amd64_tdescs \
     [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
   target_desc **tdesc;
 
-  tdesc = &amd64_tdescs[(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0]
+  tdesc = &amd64_tdescs[(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
+    [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
+    [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
     [segments ? 1 : 0];
 
   if (*tdesc == NULL)
-    *tdesc = amd64_create_target_description (xcr0, false, false,
-					      segments);
+    *tdesc = amd64_create_target_description (xstate_bv_mask, false,
+					      false, segments);
 
   return *tdesc;
 }
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index cbf8a97fa3c..82f781bf404 100644
--- a/gdb/amd64-tdep.h
+++ b/gdb/amd64-tdep.h
@@ -108,8 +108,12 @@ extern void amd64_init_abi (struct gdbarch_info info,
 extern void amd64_x32_init_abi (struct gdbarch_info info,
 				struct gdbarch *gdbarch,
 				const target_desc *default_tdesc);
-extern const struct target_desc *amd64_target_description (uint64_t xcr0,
-							   bool segments);
+
+/* Return the target description for the specified xsave features as
+   defined in XSTATE_BV_MASK and SEGMENTS.  */
+
+extern const struct target_desc *amd64_target_description
+  (uint64_t xstate_bv_mask, bool segments);
 
 /* Fill register REGNUM in REGCACHE with the appropriate
    floating-point or SSE register value from *FXSAVE.  If REGNUM is
diff --git a/gdb/arch/amd64-linux-tdesc.c b/gdb/arch/amd64-linux-tdesc.c
index 91de75873a1..771badc0ca7 100644
--- a/gdb/arch/amd64-linux-tdesc.c
+++ b/gdb/arch/amd64-linux-tdesc.c
@@ -26,41 +26,42 @@
 /* See arch/amd64-linux-tdesc.h.  */
 
 const struct target_desc *
-amd64_linux_read_description (uint64_t xcr0, bool is_x32)
+amd64_linux_read_description (uint64_t xstate_bv_mask, bool is_x32)
 {
   /* The type used for the amd64 and x32 target description caches.  */
   using tdesc_cache_type = std::unordered_map<uint64_t, const target_desc_up>;
 
   /* Caches for the previously seen amd64 and x32 target descriptions,
-     indexed by the xcr0 value that created the target description.  These
-     need to be static within this function to ensure they are initialised
-     before first use.  */
+     indexed by the xstate_bv_mask value that created the target
+     description.  These need to be static within this function to ensure
+     they are initialised before first use.  */
   static tdesc_cache_type amd64_tdesc_cache, x32_tdesc_cache;
 
   tdesc_cache_type &tdesc_cache = is_x32 ? x32_tdesc_cache : amd64_tdesc_cache;
 
-  /* Only some bits are checked when creating a tdesc, but the XCR0 value
-     contains other feature bits that are not relevant for tdesc creation.
-     When indexing into the TDESC_CACHE we need to use a consistent xcr0
-     value otherwise we might fail to find an existing tdesc which has the
-     same set of relevant bits set.  */
-  xcr0 &= is_x32
-    ? x86_linux_x32_xcr0_feature_mask ()
-    : x86_linux_amd64_xcr0_feature_mask ();
+  /* Only some bits are checked when creating a tdesc, but the
+     xstate_bv_mask value contains other feature bits that are not
+     relevant for tdesc creation.
+     When indexing into the TDESC_CACHE we need to use a consistent
+     xstate_bv_mask value otherwise we might fail to find an existing
+     tdesc which has the same set of relevant bits set.  */
+  xstate_bv_mask &= is_x32
+    ? x86_linux_x32_xstate_bv_feature_mask ()
+    : x86_linux_amd64_xstate_bv_feature_mask ();
 
-  const auto it = tdesc_cache.find (xcr0);
+  const auto it = tdesc_cache.find (xstate_bv_mask);
   if (it != tdesc_cache.end ())
     return it->second.get ();
 
   /* Create the previously unseen target description.  */
-  target_desc_up tdesc (amd64_create_target_description (xcr0, is_x32,
-							 true, true));
+  target_desc_up tdesc (amd64_create_target_description (xstate_bv_mask,
+							 is_x32, true, true));
   x86_linux_post_init_tdesc (tdesc.get (), true);
 
   /* Add to the cache, and return a pointer borrowed from the
      target_desc_up.  This is safe as the cache (and the pointers contained
      within it) are not deleted until GDB exits.  */
   target_desc *ptr = tdesc.get ();
-  tdesc_cache.emplace (xcr0, std::move (tdesc));
+  tdesc_cache.emplace (xstate_bv_mask, std::move (tdesc));
   return ptr;
 }
diff --git a/gdb/arch/amd64-linux-tdesc.h b/gdb/arch/amd64-linux-tdesc.h
index 8806a132562..0d0e1bbff72 100644
--- a/gdb/arch/amd64-linux-tdesc.h
+++ b/gdb/arch/amd64-linux-tdesc.h
@@ -22,9 +22,10 @@
 
 struct target_desc;
 
-/* Return the AMD64 target descriptions corresponding to XCR0 and IS_X32.  */
+/* Return the AMD64 target descriptions corresponding to XSTATE_BV_MASK
+   and IS_X32.  */
 
-extern const target_desc *amd64_linux_read_description (uint64_t xcr0,
-							bool is_x32);
+extern const target_desc *amd64_linux_read_description
+  (uint64_t xstate_bv_mask, bool is_x32);
 
 #endif /* GDB_ARCH_AMD64_LINUX_TDESC_H */
diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
index 252650b6390..3181f827356 100644
--- a/gdb/arch/amd64.c
+++ b/gdb/arch/amd64.c
@@ -30,14 +30,11 @@
 
 #include "../features/i386/x32-core.c"
 
-/* Create amd64 target descriptions according to XCR0.  If IS_X32 is
-   true, create the x32 ones.  If IS_LINUX is true, create target
-   descriptions for Linux.  If SEGMENTS is true, then include
-   the "org.gnu.gdb.i386.segments" feature registers.  */
+/* See arch/amd64.h.  */
 
 target_desc *
-amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
-				 bool segments)
+amd64_create_target_description (uint64_t xstate_bv_mask, bool is_x32,
+				 bool is_linux, bool segments)
 {
   target_desc_up tdesc = allocate_target_description ();
 
@@ -62,13 +59,13 @@ amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
   if (segments)
     regnum = create_feature_i386_64bit_segments (tdesc.get (), regnum);
 
-  if (xcr0 & X86_XSTATE_AVX)
+  if (xstate_bv_mask & X86_XSTATE_AVX)
     regnum = create_feature_i386_64bit_avx (tdesc.get (), regnum);
 
-  if (xcr0 & X86_XSTATE_AVX512)
+  if (xstate_bv_mask & X86_XSTATE_AVX512)
     regnum = create_feature_i386_64bit_avx512 (tdesc.get (), regnum);
 
-  if (xcr0 & X86_XSTATE_PKRU)
+  if (xstate_bv_mask & X86_XSTATE_PKRU)
     regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
 
   return tdesc.release ();
diff --git a/gdb/arch/amd64.h b/gdb/arch/amd64.h
index 695660c55d6..54080ee10a9 100644
--- a/gdb/arch/amd64.h
+++ b/gdb/arch/amd64.h
@@ -21,7 +21,13 @@
 #include "gdbsupport/tdesc.h"
 #include <stdint.h>
 
-target_desc *amd64_create_target_description (uint64_t xcr0, bool is_x32,
-					      bool is_linux, bool segments);
+/* Create amd64 target descriptions according to XSTATE_BV_MASK.  If
+   IS_X32 is true, create the x32 ones.  If IS_LINUX is true, create
+   target descriptions for Linux.  If SEGMENTS is true, then include
+   the "org.gnu.gdb.i386.segments" feature registers.  */
+
+target_desc *amd64_create_target_description (uint64_t xstate_bv_mask,
+					      bool is_x32, bool is_linux,
+					      bool segments);
 
 #endif /* GDB_ARCH_AMD64_H */
diff --git a/gdb/arch/i386-linux-tdesc.c b/gdb/arch/i386-linux-tdesc.c
index 51513176464..57cdbbfa7e5 100644
--- a/gdb/arch/i386-linux-tdesc.c
+++ b/gdb/arch/i386-linux-tdesc.c
@@ -25,32 +25,35 @@
 /* See arch/i386-linux-tdesc.h.  */
 
 const target_desc *
-i386_linux_read_description (uint64_t xcr0)
+i386_linux_read_description (uint64_t xstate_bv_mask)
 {
-  /* Cache of previously seen i386 target descriptions, indexed by the xcr0
-     value that created the target description.  This needs to be static
-     within this function to ensure it is initialised before first use.  */
+  /* Cache of previously seen i386 target descriptions, indexed by the
+     xstate_bv_mask value that created the target description.  This
+     needs to be static within this function to ensure it is initialised
+     before first use.  */
   static std::unordered_map<uint64_t, const target_desc_up> i386_tdesc_cache;
 
-  /* Only some bits are checked when creating a tdesc, but the XCR0 value
-     contains other feature bits that are not relevant for tdesc creation.
-     When indexing into the I386_TDESC_CACHE we need to use a consistent
-     xcr0 value otherwise we might fail to find an existing tdesc which has
-     the same set of relevant bits set.  */
-  xcr0 &= x86_linux_i386_xcr0_feature_mask ();
+  /* Only some bits are checked when creating a tdesc, but the
+     XSTATE_BV_MASK value contains other feature bits that are not
+     relevant for tdesc creation.  When indexing into the I386_TDESC_CACHE
+     we need to use a consistent XSTATE_BV_MASK value otherwise we might
+     fail to find an existing tdesc which has the same set of relevant
+     bits set.  */
+  xstate_bv_mask &= x86_linux_i386_xstate_bv_feature_mask ();
 
-  const auto it = i386_tdesc_cache.find (xcr0);
+  const auto it = i386_tdesc_cache.find (xstate_bv_mask);
   if (it != i386_tdesc_cache.end ())
     return it->second.get ();
 
   /* Create the previously unseen target description.  */
-  target_desc_up tdesc (i386_create_target_description (xcr0, true, false));
+  target_desc_up tdesc
+    (i386_create_target_description (xstate_bv_mask, true, false));
   x86_linux_post_init_tdesc (tdesc.get (), false);
 
   /* Add to the cache, and return a pointer borrowed from the
      target_desc_up.  This is safe as the cache (and the pointers contained
      within it) are not deleted until GDB exits.  */
   target_desc *ptr = tdesc.get ();
-  i386_tdesc_cache.emplace (xcr0, std::move (tdesc));
+  i386_tdesc_cache.emplace (xstate_bv_mask, std::move (tdesc));
   return ptr;
 }
diff --git a/gdb/arch/i386-linux-tdesc.h b/gdb/arch/i386-linux-tdesc.h
index 2c3c1740d81..3392b3fa06e 100644
--- a/gdb/arch/i386-linux-tdesc.h
+++ b/gdb/arch/i386-linux-tdesc.h
@@ -22,8 +22,9 @@
 
 struct target_desc;
 
-/* Return the i386 target description corresponding to XCR0.  */
+/* Return the i386 target description corresponding to XSTATE_BV_MASK.  */
 
-extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
+extern const struct target_desc *i386_linux_read_description
+  (uint64_t xstate_bv_mask);
 
 #endif /* GDB_ARCH_I386_LINUX_TDESC_H */
diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c
index 835df53c75d..e04d8c5dd94 100644
--- a/gdb/arch/i386.c
+++ b/gdb/arch/i386.c
@@ -29,10 +29,11 @@
 #include "../features/i386/32bit-segments.c"
 #include "../features/i386/pkeys.c"
 
-/* Create i386 target descriptions according to XCR0.  */
+/* See arch/i386.h.  */
 
 target_desc *
-i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
+i386_create_target_description (uint64_t xstate_bv_mask, bool is_linux,
+				bool segments)
 {
   target_desc_up tdesc = allocate_target_description ();
 
@@ -44,10 +45,10 @@ i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
 
   long regnum = 0;
 
-  if (xcr0 & X86_XSTATE_X87)
+  if (xstate_bv_mask & X86_XSTATE_X87)
     regnum = create_feature_i386_32bit_core (tdesc.get (), regnum);
 
-  if (xcr0 & X86_XSTATE_SSE)
+  if (xstate_bv_mask & X86_XSTATE_SSE)
     regnum = create_feature_i386_32bit_sse (tdesc.get (), regnum);
 
   if (is_linux)
@@ -56,13 +57,13 @@ i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
   if (segments)
     regnum = create_feature_i386_32bit_segments (tdesc.get (), regnum);
 
-  if (xcr0 & X86_XSTATE_AVX)
+  if (xstate_bv_mask & X86_XSTATE_AVX)
     regnum = create_feature_i386_32bit_avx (tdesc.get (), regnum);
 
-  if (xcr0 & X86_XSTATE_AVX512)
+  if (xstate_bv_mask & X86_XSTATE_AVX512)
     regnum = create_feature_i386_32bit_avx512 (tdesc.get (), regnum);
 
-  if (xcr0 & X86_XSTATE_PKRU)
+  if (xstate_bv_mask & X86_XSTATE_PKRU)
     regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
 
   return tdesc.release ();
diff --git a/gdb/arch/i386.h b/gdb/arch/i386.h
index 1fc41014624..a23deebc366 100644
--- a/gdb/arch/i386.h
+++ b/gdb/arch/i386.h
@@ -21,7 +21,13 @@
 #include "gdbsupport/tdesc.h"
 #include <stdint.h>
 
-target_desc *i386_create_target_description (uint64_t xcr0, bool is_linux,
+/* Create i386 target descriptions according to XSTATE_BV_MASK.  If
+   IS_LINUX is true, create target descriptions for Linux.  If SEGMENTS
+   is true, then include the "org.gnu.gdb.i386.segments" feature
+   registers.  */
+
+target_desc *i386_create_target_description (uint64_t xstate_bv_mask,
+					     bool is_linux,
 					     bool segments);
 
 #endif /* GDB_ARCH_I386_H */
diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
index f65920cb95b..68f37fccaef 100644
--- a/gdb/arch/x86-linux-tdesc-features.c
+++ b/gdb/arch/x86-linux-tdesc-features.c
@@ -28,18 +28,21 @@
 
    We want to cache target descriptions, and this is currently done in
    three separate caches, one each for i386, amd64, and x32.  Additionally,
-   the caching we're discussing here is Linux only, and for Linux, the only
-   thing that has an impact on target description creation is the xcr0
-   value.
-
-   In order to ensure the cache functions correctly we need to filter out
-   only those xcr0 feature bits that are relevant, we can then cache target
-   descriptions based on the relevant feature bits.  Two xcr0 values might
-   be different, but have the same relevant feature bits.  In this case we
-   would expect the two xcr0 values to map to the same cache entry.  */
+   the caching we're discussing here is Linux only.  Currently for Linux,
+   the only thing that has an impact on target description creation are
+   the supported features in xsave which are modelled by a xstate_bv_mask
+   value, which has the same format than the state component bitmap.
+
+   In order to ensure the cache functions correctly we need to filter only
+   those xstate_bv_mask feature bits that are relevant, we can then cache
+   target descriptions based on the relevant feature bits.  Two
+   xstate_bv_mask values might be different, but have the same relevant
+   feature bits.  In this case we would expect the two xstate_bv_mask
+   values to map to the same cache entry.  */
 
 struct x86_xstate_feature {
-  /* The xstate feature mask.  This is a mask against an xcr0 value.  */
+  /* The xstate feature mask.  This is a mask against the state component
+     bitmap.  */
   uint64_t feature;
 
   /* Is this feature checked when creating an i386 target description.  */
@@ -56,9 +59,9 @@ struct x86_xstate_feature {
    checked when building a target description for i386, amd64, or x32.
 
    If in the future, due to simplifications or refactoring, this table ever
-   ends up with 'true' for every xcr0 feature on every target type, then this
-   is an indication that this table should probably be removed, and that the
-   rest of the code in this file can be simplified.  */
+   ends up with 'true' for every xsave feature on every target type, then
+   this is an indication that this table should probably be removed, and
+   that the rest of the code in this file can be simplified.  */
 
 static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
   /* Feature,           i386,	amd64,	x32.  */
@@ -73,7 +76,7 @@ static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
    that are checked for when building an i386 target description.  */
 
 static constexpr uint64_t
-x86_linux_i386_xcr0_feature_mask_1 ()
+x86_linux_i386_xstate_bv_feature_mask_1 ()
 {
   uint64_t mask = 0;
 
@@ -88,7 +91,7 @@ x86_linux_i386_xcr0_feature_mask_1 ()
    that are checked for when building an amd64 target description.  */
 
 static constexpr uint64_t
-x86_linux_amd64_xcr0_feature_mask_1 ()
+x86_linux_amd64_xstate_bv_feature_mask_1 ()
 {
   uint64_t mask = 0;
 
@@ -103,7 +106,7 @@ x86_linux_amd64_xcr0_feature_mask_1 ()
    that are checked for when building an x32 target description.  */
 
 static constexpr uint64_t
-x86_linux_x32_xcr0_feature_mask_1 ()
+x86_linux_x32_xstate_bv_feature_mask_1 ()
 {
   uint64_t mask = 0;
 
@@ -117,25 +120,25 @@ x86_linux_x32_xcr0_feature_mask_1 ()
 /* See arch/x86-linux-tdesc-features.h.  */
 
 uint64_t
-x86_linux_i386_xcr0_feature_mask ()
+x86_linux_i386_xstate_bv_feature_mask ()
 {
-  return x86_linux_i386_xcr0_feature_mask_1 ();
+  return x86_linux_i386_xstate_bv_feature_mask_1 ();
 }
 
 /* See arch/x86-linux-tdesc-features.h.  */
 
 uint64_t
-x86_linux_amd64_xcr0_feature_mask ()
+x86_linux_amd64_xstate_bv_feature_mask ()
 {
-  return x86_linux_amd64_xcr0_feature_mask_1 ();
+  return x86_linux_amd64_xstate_bv_feature_mask_1 ();
 }
 
 /* See arch/x86-linux-tdesc-features.h.  */
 
 uint64_t
-x86_linux_x32_xcr0_feature_mask ()
+x86_linux_x32_xstate_bv_feature_mask ()
 {
-  return x86_linux_x32_xcr0_feature_mask_1 ();
+  return x86_linux_x32_xstate_bv_feature_mask_1 ();
 }
 
 #ifdef GDBSERVER
@@ -143,7 +146,7 @@ x86_linux_x32_xcr0_feature_mask ()
 /* See arch/x86-linux-tdesc-features.h.  */
 
 int
-x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
+x86_linux_xstate_bv_mask_to_tdesc_idx (uint64_t xstate_bv_mask)
 {
   /* The following table shows which features are checked for when creating
      the target descriptions (see nat/x86-linux-tdesc.c), the feature order
@@ -160,7 +163,7 @@ x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
 
   for (int i = 0; i < ARRAY_SIZE (x86_linux_all_xstate_features); ++i)
     {
-      if ((xcr0 & x86_linux_all_xstate_features[i].feature)
+      if ((xstate_bv_mask & x86_linux_all_xstate_features[i].feature)
 	  == x86_linux_all_xstate_features[i].feature)
 	idx |= (1 << i);
     }
@@ -250,17 +253,17 @@ x86_linux_i386_tdesc_count ()
 /* See arch/x86-linux-tdesc-features.h.  */
 
 uint64_t
-x86_linux_tdesc_idx_to_xcr0 (int idx)
+x86_linux_tdesc_idx_to_xstate_bv_mask (int idx)
 {
-  uint64_t xcr0 = 0;
+  uint64_t xstate_bv_mask = 0;
 
   for (int i = 0; i < ARRAY_SIZE (x86_linux_all_xstate_features); ++i)
     {
       if ((idx & (1 << i)) != 0)
-	xcr0 |= x86_linux_all_xstate_features[i].feature;
+	xstate_bv_mask |= x86_linux_all_xstate_features[i].feature;
     }
 
-  return xcr0;
+  return xstate_bv_mask;
 }
 
 #endif /* IN_PROCESS_AGENT */
diff --git a/gdb/arch/x86-linux-tdesc-features.h b/gdb/arch/x86-linux-tdesc-features.h
index 89fe7cecc68..d1d74e70df8 100644
--- a/gdb/arch/x86-linux-tdesc-features.h
+++ b/gdb/arch/x86-linux-tdesc-features.h
@@ -27,17 +27,20 @@
    the set of features which are checked for when creating the target
    description for each of amd64, x32, and i386.  */
 
-extern uint64_t x86_linux_amd64_xcr0_feature_mask ();
-extern uint64_t x86_linux_x32_xcr0_feature_mask ();
-extern uint64_t x86_linux_i386_xcr0_feature_mask ();
+extern uint64_t x86_linux_amd64_xstate_bv_feature_mask ();
+extern uint64_t x86_linux_x32_xstate_bv_feature_mask ();
+extern uint64_t x86_linux_i386_xstate_bv_feature_mask ();
 
 #ifdef GDBSERVER
 
-/* Convert an xcr0 value into an integer.  The integer will be passed from
+/* Convert an XSTATE_BV_MASK value into an integer.  XSTATE_BV_MASK has
+   the same format than the state component bitmap and does include user
+   and supervisor state components.  The integer will be passed from
    gdbserver to the in-process-agent where it will then be passed through
-   x86_linux_tdesc_idx_to_xcr0 to get back the original xcr0 value.  */
+   x86_linux_tdesc_idx_to_xstate_bv_mask to get back the original mask.  */
 
-extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
+
+extern int x86_linux_xstate_bv_mask_to_tdesc_idx (uint64_t xstate_bv_mask);
 
 #endif /* GDBSERVER */
 
@@ -51,11 +54,13 @@ extern int x86_linux_amd64_tdesc_count ();
 extern int x86_linux_x32_tdesc_count ();
 extern int x86_linux_i386_tdesc_count ();
 
-/* Convert an index number (as returned from x86_linux_xcr0_to_tdesc_idx)
-   into an xcr0 value which can then be used to create a target
-   description.  */
+/* Convert an index number (as returned from
+   x86_linux_xstate_bv_mask_to_tdesc_idx) into an xstate_bv_mask
+   value which can then be used to create a target description.
+   The return mask has the same format than the state component bitmap
+   and does include user and supervisor state components.*/
 
-extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
+extern uint64_t x86_linux_tdesc_idx_to_xstate_bv_mask (int idx);
 
 #endif /* IN_PROCESS_AGENT */
 
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 9be4748c880..90ff0c5c706 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -8949,23 +8949,23 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
 
 \f
 
-/* Return the target description for a specified XSAVE feature mask.  */
+/* See i386-tdep.h.  */
 
 const struct target_desc *
-i386_target_description (uint64_t xcr0, bool segments)
+i386_target_description (uint64_t xstate_bv_mask, bool segments)
 {
   static target_desc *i386_tdescs \
     [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
   target_desc **tdesc;
 
-  tdesc = &i386_tdescs[(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
-    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0]
+  tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
+    [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
+    [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
+    [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
     [segments ? 1 : 0];
 
   if (*tdesc == NULL)
-    *tdesc = i386_create_target_description (xcr0, false, segments);
+    *tdesc = i386_create_target_description (xstate_bv_mask, false, segments);
 
   return *tdesc;
 }
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index e849b336e12..239bc8674e8 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -450,8 +450,11 @@ extern int i386_svr4_reg_to_regnum (struct gdbarch *gdbarch, int reg);
 
 extern int i386_process_record (struct gdbarch *gdbarch,
 				struct regcache *regcache, CORE_ADDR addr);
-extern const struct target_desc *i386_target_description (uint64_t xcr0,
-							  bool segments);
+
+/* Return the target description for the specified xsave features as
+   defined in XSTATE_BV_MASK and SEGMENTS.  */
+extern const struct target_desc *i386_target_description
+  (uint64_t xstate_bv_mask, bool segments);
 
 /* Functions and variables exported from i386-bsd-tdep.c.  */
 
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
index 80e4337a270..e9cf2527c5f 100644
--- a/gdb/nat/x86-linux-tdesc.c
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -43,7 +43,7 @@
 /* See nat/x86-linux-tdesc.h.  */
 
 const target_desc *
-x86_linux_tdesc_for_tid (int tid, uint64_t *xcr0_storage,
+x86_linux_tdesc_for_tid (int tid, uint64_t *xstate_bv_storage,
 			 x86_xsave_layout *xsave_layout_storage)
 {
 #ifdef __x86_64__
@@ -96,30 +96,32 @@ x86_linux_tdesc_for_tid (int tid, uint64_t *xcr0_storage,
 	     these bits being set we generate a completely empty tdesc for
 	     i386 which will be rejected by GDB.  */
 	  have_ptrace_getregset = TRIBOOL_FALSE;
-	  *xcr0_storage = X86_XSTATE_SSE_MASK;
+	  *xstate_bv_storage = X86_XSTATE_SSE_MASK;
 	}
       else
 	{
 	  have_ptrace_getregset = TRIBOOL_TRUE;
 
 	  /* Get XCR0 from XSAVE extended state.  */
-	  *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
+	  uint64_t xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
 				      / sizeof (uint64_t))];
 
 	  *xsave_layout_storage
-	    = x86_fetch_xsave_layout (*xcr0_storage, x86_xsave_length ());
+	    = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
+
+	  *xstate_bv_storage = xcr0;
 	}
     }
 
-  /* Use cached xcr0 value.  */
-  uint64_t xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
+  /* Use cached XSTATE_BV_STORAGE value.  */
+  uint64_t xstate_bv_features_bits = *xstate_bv_storage & X86_XSTATE_ALL_MASK;
 
 #ifdef __x86_64__
   if (is_64bit)
-    return amd64_linux_read_description (xcr0_features_bits, is_x32);
+    return amd64_linux_read_description (xstate_bv_features_bits, is_x32);
   else
 #endif
-    return i386_linux_read_description (xcr0_features_bits);
+    return i386_linux_read_description (xstate_bv_features_bits);
 }
 
 #endif /* !IN_PROCESS_AGENT */
diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
index 38c71f10df2..19aa84f6b3f 100644
--- a/gdb/nat/x86-linux-tdesc.h
+++ b/gdb/nat/x86-linux-tdesc.h
@@ -27,9 +27,9 @@ struct x86_xsave_layout;
 
 /* Return the target description for Linux thread TID.
 
-   The storage pointed to by XCR0_STORAGE and XSAVE_LAYOUT_STORAGE must
+   The storage pointed to by XSTATE_BV_STORAGE and XSAVE_LAYOUT_STORAGE must
    exist until the program (GDB or gdbserver) terminates, this storage is
-   used to cache the xcr0 and xsave layout values.  The values pointed to
+   used to cache the xstate_bv and xsave layout values.  The values pointed to
    by these arguments are only updated at most once, the first time this
    function is called if the have_ptrace_getregset global is set to
    TRIBOOL_UNKNOWN.
@@ -45,6 +45,7 @@ struct x86_xsave_layout;
    returned.  */
 
 extern const target_desc *x86_linux_tdesc_for_tid
-  (int tid, uint64_t *xcr0_storage, x86_xsave_layout *xsave_layout_storage);
+  (int tid, uint64_t *xstate_bv_storage,
+   x86_xsave_layout *xsave_layout_storage);
 
 #endif /* GDB_NAT_X86_LINUX_TDESC_H */
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index 81db5d86a5e..1b7dd8506dd 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -97,15 +97,20 @@ const struct target_desc *
 x86_linux_nat_target::read_description ()
 {
   /* The x86_linux_tdesc_for_tid call only reads xcr0 the first time it is
-     called, the xcr0 value is stored here and reused on subsequent calls.  */
-  static uint64_t xcr0_storage;
+     called.  The mask is stored in XSTATE_BV_STORAGE and reused on
+     subsequent calls.  Note that GDB currently supports features for user
+     state components only.  However, once supervisor state components are
+     supported in GDB, the value XSTATE_BV_STORAGE will not be configured
+     based on xcr0 only.  */
+  static uint64_t xstate_bv_storage;
 
   if (inferior_ptid == null_ptid)
     return this->beneath ()->read_description ();
 
   int tid = inferior_ptid.pid ();
 
-  return x86_linux_tdesc_for_tid (tid, &xcr0_storage, &this->m_xsave_layout);
+  return x86_linux_tdesc_for_tid (tid, &xstate_bv_storage,
+				  &this->m_xsave_layout);
 }
 \f
 
diff --git a/gdbserver/i387-fp.cc b/gdbserver/i387-fp.cc
index 4be0083a6fb..90824bdaee5 100644
--- a/gdbserver/i387-fp.cc
+++ b/gdbserver/i387-fp.cc
@@ -21,7 +21,7 @@
 #include "nat/x86-xstate.h"
 
 /* Default to SSE.  */
-static uint64_t x86_xcr0 = X86_XSTATE_SSE_MASK;
+static uint64_t x86_xstate_bv = X86_XSTATE_SSE_MASK;
 
 static const int num_avx512_k_registers = 8;
 static const int num_pkeys_registers = 1;
@@ -265,7 +265,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
 
   /* The supported bits in `xstat_bv' are 8 bytes.  Clear part in
      vector registers if its bit in xstat_bv is zero.  */
-  clear_bv = (~fp->xstate_bv) & x86_xcr0;
+  clear_bv = (~fp->xstate_bv) & x86_xstate_bv;
 
   /* Clear part in x87 and vector registers if its bit in xstat_bv is
      zero.  */
@@ -315,7 +315,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
     }
 
   /* Check if any x87 registers are changed.  */
-  if ((x86_xcr0 & X86_XSTATE_X87))
+  if ((x86_xstate_bv & X86_XSTATE_X87))
     {
       int st0_regnum = find_regno (regcache->tdesc, "st0");
 
@@ -332,7 +332,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
     }
 
   /* Check if any SSE registers are changed.  */
-  if ((x86_xcr0 & X86_XSTATE_SSE))
+  if ((x86_xstate_bv & X86_XSTATE_SSE))
     {
       int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
 
@@ -349,7 +349,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
     }
 
   /* Check if any AVX registers are changed.  */
-  if ((x86_xcr0 & X86_XSTATE_AVX))
+  if ((x86_xstate_bv & X86_XSTATE_AVX))
     {
       int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");
 
@@ -366,7 +366,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
     }
 
   /* Check if any K registers are changed.  */
-  if ((x86_xcr0 & X86_XSTATE_K))
+  if ((x86_xstate_bv & X86_XSTATE_K))
     {
       int k0_regnum = find_regno (regcache->tdesc, "k0");
 
@@ -383,7 +383,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
     }
 
   /* Check if any of ZMM0H-ZMM15H registers are changed.  */
-  if ((x86_xcr0 & X86_XSTATE_ZMM_H))
+  if ((x86_xstate_bv & X86_XSTATE_ZMM_H))
     {
       int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");
 
@@ -400,7 +400,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
     }
 
   /* Check if any of ZMM16-ZMM31 registers are changed.  */
-  if ((x86_xcr0 & X86_XSTATE_ZMM) && num_zmm_high_registers != 0)
+  if ((x86_xstate_bv & X86_XSTATE_ZMM) && num_zmm_high_registers != 0)
     {
       int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
       int ymm16h_regnum = find_regno (regcache->tdesc, "ymm16h");
@@ -437,7 +437,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
     }
 
   /* Check if any PKEYS registers are changed.  */
-  if ((x86_xcr0 & X86_XSTATE_PKRU))
+  if ((x86_xstate_bv & X86_XSTATE_PKRU))
     {
       int pkru_regnum = find_regno (regcache->tdesc, "pkru");
 
@@ -453,7 +453,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
 	}
     }
 
-  if ((x86_xcr0 & X86_XSTATE_SSE) || (x86_xcr0 & X86_XSTATE_AVX))
+  if ((x86_xstate_bv & X86_XSTATE_SSE) || (x86_xstate_bv & X86_XSTATE_AVX))
     {
       collect_register_by_name (regcache, "mxcsr", raw);
       if (memcmp (raw, &fp->mxcsr, 4) != 0)
@@ -465,7 +465,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
 	}
     }
 
-  if (x86_xcr0 & X86_XSTATE_X87)
+  if (x86_xstate_bv & X86_XSTATE_X87)
     {
       collect_register_by_name (regcache, "fioff", raw);
       if (memcmp (raw, &fp->fioff, 4) != 0)
@@ -658,10 +658,10 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
 
   /* The supported bits in `xstat_bv' are 8 bytes.  Clear part in
      vector registers if its bit in xstat_bv is zero.  */
-  clear_bv = (~fp->xstate_bv) & x86_xcr0;
+  clear_bv = (~fp->xstate_bv) & x86_xstate_bv;
 
   /* Check if any x87 registers are changed.  */
-  if ((x86_xcr0 & X86_XSTATE_X87) != 0)
+  if ((x86_xstate_bv & X86_XSTATE_X87) != 0)
     {
       int st0_regnum = find_regno (regcache->tdesc, "st0");
 
@@ -678,7 +678,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
 	}
     }
 
-  if ((x86_xcr0 & X86_XSTATE_SSE) != 0)
+  if ((x86_xstate_bv & X86_XSTATE_SSE) != 0)
     {
       int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
 
@@ -695,7 +695,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
 	}
     }
 
-  if ((x86_xcr0 & X86_XSTATE_AVX) != 0)
+  if ((x86_xstate_bv & X86_XSTATE_AVX) != 0)
     {
       int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");
 
@@ -712,7 +712,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
 	}
     }
 
-  if ((x86_xcr0 & X86_XSTATE_K) != 0)
+  if ((x86_xstate_bv & X86_XSTATE_K) != 0)
     {
       int k0_regnum = find_regno (regcache->tdesc, "k0");
 
@@ -729,7 +729,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
 	}
     }
 
-  if ((x86_xcr0 & X86_XSTATE_ZMM_H) != 0)
+  if ((x86_xstate_bv & X86_XSTATE_ZMM_H) != 0)
     {
       int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");
 
@@ -746,7 +746,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
 	}
     }
 
-  if ((x86_xcr0 & X86_XSTATE_ZMM) != 0 && num_zmm_high_registers != 0)
+  if ((x86_xstate_bv & X86_XSTATE_ZMM) != 0 && num_zmm_high_registers != 0)
     {
       int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
       int ymm16h_regnum = find_regno (regcache->tdesc, "ymm16h");
@@ -773,7 +773,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
 	}
     }
 
-  if ((x86_xcr0 & X86_XSTATE_PKRU) != 0)
+  if ((x86_xstate_bv & X86_XSTATE_PKRU) != 0)
     {
       int pkru_regnum = find_regno (regcache->tdesc, "pkru");
 
@@ -858,5 +858,5 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
 std::pair<uint64_t *, x86_xsave_layout *>
 i387_get_xsave_storage ()
 {
-  return { &x86_xcr0, &xsave_layout };
+  return { &x86_xstate_bv, &xsave_layout };
 }
diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
index af4eb03b9b8..75d70a8787c 100644
--- a/gdbserver/linux-amd64-ipa.cc
+++ b/gdbserver/linux-amd64-ipa.cc
@@ -82,7 +82,7 @@ get_raw_reg (const unsigned char *raw_regs, int regnum)
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask (idx);
 
 #if defined __ILP32__
   bool is_x32 = true;
@@ -90,7 +90,7 @@ get_ipa_tdesc (int idx)
   bool is_x32 = false;
 #endif
 
-  return amd64_linux_read_description (xcr0, is_x32);
+  return amd64_linux_read_description (xstate_bv_mask, is_x32);
 }
 
 /* Allocate buffer for the jump pads.  The branch instruction has a
@@ -159,9 +159,11 @@ initialize_low_tracepoint (void)
 {
 #if defined __ILP32__
   for (int i = 0; i < x86_linux_x32_tdesc_count (); i++)
-    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true);
+    amd64_linux_read_description
+      (x86_linux_tdesc_idx_to_xstate_bv_mask (i), true);
 #else
   for (int i = 0; i < x86_linux_amd64_tdesc_count (); i++)
-    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false);
+    amd64_linux_read_description
+      (x86_linux_tdesc_idx_to_xstate_bv_mask (i), false);
 #endif
 }
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 17af6eb3610..1a0393d127c 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -174,9 +174,9 @@ initialize_fast_tracepoint_trampoline_buffer (void)
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
+  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask  (idx);
 
-  return i386_linux_read_description (xcr0);
+  return i386_linux_read_description (xstate_bv_mask);
 }
 
 /* Allocate buffer for the jump pads.  On i386, we can reach an arbitrary
@@ -199,5 +199,5 @@ initialize_low_tracepoint (void)
 {
   initialize_fast_tracepoint_trampoline_buffer ();
   for (int i = 0; i < x86_linux_i386_tdesc_count (); i++)
-    i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i));
+    i386_linux_read_description (x86_linux_tdesc_idx_to_xstate_bv_mask (i));
 }
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index 24920e71a53..dbc11d6856d 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -873,7 +873,7 @@ x86_linux_read_description ()
   bool have_ptrace_getregset_was_unknown
     = have_ptrace_getregset == TRIBOOL_UNKNOWN;
 
-  /* Get pointers to where we should store the xcr0 and xsave_layout
+  /* Get pointers to where we should store the xstate_bv and xsave_layout
      values.  These will be filled in by x86_linux_tdesc_for_tid the first
      time that the function is called.  Subsequent calls will not modify
      the stored values.  */
@@ -2892,17 +2892,16 @@ x86_target::get_ipa_tdesc_idx ()
 		  || tdesc == tdesc_amd64_linux_no_xml.get ()
 #endif /* __x86_64__ */
 		  );
-      return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK);
+      return x86_linux_xstate_bv_mask_to_tdesc_idx (X86_XSTATE_SSE_MASK);
     }
 
-  /* The xcr0 value and xsave layout value are cached when the target
+  /* The xstate_bv value and xsave layout value are cached when the target
      description is read.  Grab their cache location, and use the cached
      value to calculate a tdesc index.  */
   std::pair<uint64_t *, x86_xsave_layout *> storage
     = i387_get_xsave_storage ();
-  uint64_t xcr0 = *storage.first;
 
-  return x86_linux_xcr0_to_tdesc_idx (xcr0);
+  return x86_linux_xstate_bv_mask_to_tdesc_idx (*storage.first);
 }
 
 /* The linux target ops object.  */
diff --git a/gdbsupport/x86-xstate.h b/gdbsupport/x86-xstate.h
index 5d563ff4622..9bb373c3100 100644
--- a/gdbsupport/x86-xstate.h
+++ b/gdbsupport/x86-xstate.h
@@ -83,8 +83,10 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
 #define X86_XSTATE_AVX_AVX512_PKU_MASK 	(X86_XSTATE_AVX_MASK\
 					| X86_XSTATE_AVX512 | X86_XSTATE_PKRU)
 
-#define X86_XSTATE_ALL_MASK		(X86_XSTATE_AVX_AVX512_PKU_MASK)
+/* Supported mask of state-component bitmap xstate_bv.  The SDM defines
+   xstate_bv as XCR0 | IA32_XSS.  */
 
+#define X86_XSTATE_ALL_MASK		(X86_XSTATE_AVX_AVX512_PKU_MASK)
 
 #define X86_XSTATE_SSE_SIZE	576
 #define X86_XSTATE_AVX_SIZE	832
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (4 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86 Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-07-25 12:49   ` Andrew Burgess
                     ` (2 more replies)
  2025-06-28  8:28 ` [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack Christina Schimpe
                   ` (8 subsequent siblings)
  14 siblings, 3 replies; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

This patch adds the user mode register PL3_SSP which is part of the
Intel(R) Control-Flow Enforcement Technology (CET) feature for support
of shadow stack.
For now, only native and remote debugging support for shadow stack
userspace on amd64 linux are covered by this patch including 64 bit and
x32 support.  32 bit support is not covered due to missing Linux kernel
support.

This patch requires fixing the test gdb.base/inline-frame-cycle-unwind
which is failing in case the shadow stack pointer is unavailable.
Such a state is possible if shadow stack is disabled for the current thread
but supported by HW.

This test uses the Python unwinder inline-frame-cycle-unwind.py which fakes
the cyclic stack cycle by reading the pending frame's registers and adding
them to the unwinder:

~~~
for reg in pending_frame.architecture().registers("general"):
     val = pending_frame.read_register(reg)
     unwinder.add_saved_register(reg, val)
     return unwinder
~~~

However, in case the python unwinder is used we add a register (pl3_ssp) that is
unavailable.  This leads to a NOT_AVAILABLE_ERROR caught in
gdb/frame-unwind.c:frame_unwind_try_unwinder and it is continued with standard
unwinders.  This destroys the faked cyclic behavior and the stack is
further unwinded after frame 5.

In the working scenario an error should be triggered:
~~~
bt
0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
2  0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
4  0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5: backtrace when the unwind is broken at frame 5
~~~

To fix the Python unwinder, we simply skip the unavailable registers.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
 gdb/NEWS                                      |  3 +
 gdb/amd64-linux-nat.c                         | 17 +++++
 gdb/amd64-linux-tdep.c                        |  1 +
 gdb/amd64-tdep.c                              |  6 +-
 gdb/amd64-tdep.h                              |  1 +
 gdb/arch/amd64.c                              | 10 +++
 gdb/arch/i386.c                               |  4 ++
 gdb/arch/x86-linux-tdesc-features.c           |  1 +
 gdb/doc/gdb.texinfo                           |  4 ++
 gdb/features/Makefile                         |  2 +
 gdb/features/i386/32bit-ssp.c                 | 14 ++++
 gdb/features/i386/32bit-ssp.xml               | 11 +++
 gdb/features/i386/64bit-ssp.c                 | 14 ++++
 gdb/features/i386/64bit-ssp.xml               | 11 +++
 gdb/i386-tdep.c                               | 22 +++++-
 gdb/i386-tdep.h                               |  4 ++
 gdb/nat/x86-linux-tdesc.c                     |  2 +
 gdb/nat/x86-linux.c                           | 57 +++++++++++++++
 gdb/nat/x86-linux.h                           |  4 ++
 gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
 gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
 .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
 gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
 gdb/x86-linux-nat.c                           | 49 +++++++++++--
 gdb/x86-linux-nat.h                           | 11 +++
 gdb/x86-tdep.c                                | 21 ++++++
 gdb/x86-tdep.h                                |  9 +++
 gdbserver/linux-x86-low.cc                    | 28 +++++++-
 gdbsupport/x86-xstate.h                       |  5 +-
 29 files changed, 447 insertions(+), 10 deletions(-)
 create mode 100644 gdb/features/i386/32bit-ssp.c
 create mode 100644 gdb/features/i386/32bit-ssp.xml
 create mode 100644 gdb/features/i386/64bit-ssp.c
 create mode 100644 gdb/features/i386/64bit-ssp.xml
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index 6c8a008d39d..ba555f0dea1 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,9 @@
 
 *** Changes since GDB 16
 
+* Support for the shadow stack pointer register on x86-64 or x86-64 with
+  32-bit pointer size (X32) GNU/Linux.
+
 * Debugger Adapter Protocol changes
 
   ** GDB now supports the "completions" request.
diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index dbb9b3223cb..4df99ccca54 100644
--- a/gdb/amd64-linux-nat.c
+++ b/gdb/amd64-linux-nat.c
@@ -32,6 +32,7 @@
 #include "amd64-tdep.h"
 #include "amd64-linux-tdep.h"
 #include "i386-linux-tdep.h"
+#include "x86-tdep.h"
 #include "gdbsupport/x86-xstate.h"
 
 #include "x86-linux-nat.h"
@@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
 
       if (have_ptrace_getregset == TRIBOOL_TRUE)
 	{
+	  if ((regnum == -1 && tdep->ssp_regnum > 0)
+	      || (regnum != -1 && regnum == tdep->ssp_regnum))
+	    {
+	      x86_linux_fetch_ssp (regcache, tid);
+	      if (regnum != -1)
+		return;
+	    }
+
 	  /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
 	     "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure on
 	     Intel Skylake CPUs") that sometimes causes the mxcsr location in
@@ -302,6 +311,14 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
       if (have_ptrace_getregset == TRIBOOL_TRUE)
 	{
 	  gdb::byte_vector xstateregs (tdep->xsave_layout.sizeof_xsave);
+	  if ((regnum == -1 && tdep->ssp_regnum > 0)
+	      || (regnum != -1 && regnum == tdep->ssp_regnum))
+	    {
+	      x86_linux_store_ssp (regcache, tid);
+	      if (regnum != -1)
+		return;
+	    }
+
 	  struct iovec iov;
 
 	  iov.iov_base = xstateregs.data ();
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 13e9c0e86ea..edb7d8da6ab 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -108,6 +108,7 @@ int amd64_linux_gregset_reg_offset[] =
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1,				/* PKEYS register pkru  */
+  -1, 				/* CET user mode register PL3_SSP.  */
 
   /* End of hardware registers */
   21 * 8, 22 * 8,		      /* fs_base and gs_base.  */
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 04539dd288a..450dbc38047 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -3395,6 +3395,9 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
       tdep->num_pkeys_regs = 1;
     }
 
+  if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pl3_ssp") != nullptr)
+    tdep->ssp_regnum = AMD64_PL3_SSP_REGNUM;
+
   tdep->num_byte_regs = 20;
   tdep->num_word_regs = 16;
   tdep->num_dword_regs = 16;
@@ -3557,12 +3560,13 @@ const struct target_desc *
 amd64_target_description (uint64_t xstate_bv_mask, bool segments)
 {
   static target_desc *amd64_tdescs \
-    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
+    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/][2/*segments*/] = {};
   target_desc **tdesc;
 
   tdesc = &amd64_tdescs[(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
     [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
     [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
+    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
     [segments ? 1 : 0];
 
   if (*tdesc == NULL)
diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
index 82f781bf404..eb294f3dfbe 100644
--- a/gdb/amd64-tdep.h
+++ b/gdb/amd64-tdep.h
@@ -81,6 +81,7 @@ enum amd64_regnum
   AMD64_ZMM0H_REGNUM,
   AMD64_ZMM31H_REGNUM = AMD64_ZMM0H_REGNUM + 31,
   AMD64_PKRU_REGNUM,
+  AMD64_PL3_SSP_REGNUM,
   AMD64_FSBASE_REGNUM,
   AMD64_GSBASE_REGNUM
 };
diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
index 3181f827356..c9925fa6b66 100644
--- a/gdb/arch/amd64.c
+++ b/gdb/arch/amd64.c
@@ -28,6 +28,8 @@
 #include "../features/i386/64bit-sse.c"
 #include "../features/i386/pkeys.c"
 
+#include "../features/i386/64bit-ssp.c"
+#include "../features/i386/32bit-ssp.c"
 #include "../features/i386/x32-core.c"
 
 /* See arch/amd64.h.  */
@@ -68,5 +70,13 @@ amd64_create_target_description (uint64_t xstate_bv_mask, bool is_x32,
   if (xstate_bv_mask & X86_XSTATE_PKRU)
     regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
 
+  if (xstate_bv_mask & X86_XSTATE_CET_U)
+    {
+      if (!is_x32)
+	regnum = create_feature_i386_64bit_ssp (tdesc.get (), regnum);
+      else
+	regnum = create_feature_i386_32bit_ssp (tdesc.get (), regnum);
+    }
+
   return tdesc.release ();
 }
diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c
index e04d8c5dd94..87058e32dcb 100644
--- a/gdb/arch/i386.c
+++ b/gdb/arch/i386.c
@@ -28,6 +28,7 @@
 #include "../features/i386/32bit-avx512.c"
 #include "../features/i386/32bit-segments.c"
 #include "../features/i386/pkeys.c"
+#include "../features/i386/32bit-ssp.c"
 
 /* See arch/i386.h.  */
 
@@ -66,5 +67,8 @@ i386_create_target_description (uint64_t xstate_bv_mask, bool is_linux,
   if (xstate_bv_mask & X86_XSTATE_PKRU)
     regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
 
+  if (xstate_bv_mask & X86_XSTATE_CET_U)
+    regnum = create_feature_i386_32bit_ssp (tdesc.get (), regnum);
+
   return tdesc.release ();
 }
diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
index 68f37fccaef..f288f120cfb 100644
--- a/gdb/arch/x86-linux-tdesc-features.c
+++ b/gdb/arch/x86-linux-tdesc-features.c
@@ -65,6 +65,7 @@ struct x86_xstate_feature {
 
 static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
   /* Feature,           i386,	amd64,	x32.  */
+  { X86_XSTATE_CET_U,	false,	true, 	true },
   { X86_XSTATE_PKRU,	true,	true, 	true },
   { X86_XSTATE_AVX512,	true,	true, 	true },
   { X86_XSTATE_AVX,	true,	true, 	true },
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4ef640698bd..0881ac4aee5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -49959,6 +49959,10 @@ The @samp{org.gnu.gdb.i386.pkeys} feature is optional.  It should
 describe a single register, @samp{pkru}.  It is a 32-bit register
 valid for i386 and amd64.
 
+The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should describe the
+user mode register @samp{pl3_ssp} which has 64 bits on amd64.  Following the
+restriction of the Linux kernel, only amd64 is supported for now.
+
 @node LoongArch Features
 @subsection LoongArch Features
 @cindex target descriptions, LoongArch Features
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index 7a8c7999733..2afda1ccd00 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -225,6 +225,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
 	i386/32bit-avx.xml \
 	i386/32bit-avx512.xml \
 	i386/32bit-segments.xml \
+	i386/32bit-ssp.xml \
 	i386/64bit-avx512.xml \
 	i386/64bit-core.xml \
 	i386/64bit-segments.xml \
@@ -232,6 +233,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
 	i386/64bit-linux.xml \
 	i386/64bit-sse.xml \
 	i386/pkeys.xml \
+	i386/64bit-ssp.xml \
 	i386/x32-core.xml \
 	loongarch/base32.xml \
 	loongarch/base64.xml \
diff --git a/gdb/features/i386/32bit-ssp.c b/gdb/features/i386/32bit-ssp.c
new file mode 100644
index 00000000000..991bae3c1e6
--- /dev/null
+++ b/gdb/features/i386/32bit-ssp.c
@@ -0,0 +1,14 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: 32bit-ssp.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_i386_32bit_ssp (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pl3_ssp");
+  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 32, "data_ptr");
+  return regnum;
+}
diff --git a/gdb/features/i386/32bit-ssp.xml b/gdb/features/i386/32bit-ssp.xml
new file mode 100644
index 00000000000..d17e7004eec
--- /dev/null
+++ b/gdb/features/i386/32bit-ssp.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.pl3_ssp">
+  <reg name="pl3_ssp" bitsize="32" type="data_ptr"/>
+</feature>
diff --git a/gdb/features/i386/64bit-ssp.c b/gdb/features/i386/64bit-ssp.c
new file mode 100644
index 00000000000..5468099ddf6
--- /dev/null
+++ b/gdb/features/i386/64bit-ssp.c
@@ -0,0 +1,14 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: 64bit-ssp.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_i386_64bit_ssp (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pl3_ssp");
+  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 64, "data_ptr");
+  return regnum;
+}
diff --git a/gdb/features/i386/64bit-ssp.xml b/gdb/features/i386/64bit-ssp.xml
new file mode 100644
index 00000000000..a0688d018a5
--- /dev/null
+++ b/gdb/features/i386/64bit-ssp.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.i386.pl3_ssp">
+  <reg name="pl3_ssp" bitsize="64" type="data_ptr"/>
+</feature>
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 90ff0c5c706..8eb5b4fac86 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -8403,7 +8403,8 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
   const struct tdesc_feature *feature_core;
 
   const struct tdesc_feature *feature_sse, *feature_avx, *feature_avx512,
-			     *feature_pkeys, *feature_segments;
+			     *feature_pkeys, *feature_segments,
+			     *feature_pl3_ssp;
   int i, num_regs, valid_p;
 
   if (! tdesc_has_registers (tdesc))
@@ -8429,6 +8430,9 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
   /* Try PKEYS  */
   feature_pkeys = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys");
 
+  /* Try Shadow Stack.  */
+  feature_pl3_ssp = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pl3_ssp");
+
   valid_p = 1;
 
   /* The XCR0 bits.  */
@@ -8544,6 +8548,15 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
 					    tdep->pkeys_register_names[i]);
     }
 
+  if (feature_pl3_ssp != nullptr)
+    {
+      if (tdep->ssp_regnum < 0)
+	tdep->ssp_regnum = I386_PL3_SSP_REGNUM;
+
+      valid_p &= tdesc_numbered_register (feature_pl3_ssp, tdesc_data,
+					  tdep->ssp_regnum, "pl3_ssp");
+    }
+
   return valid_p;
 }
 
@@ -8835,6 +8848,9 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   /* No segment base registers.  */
   tdep->fsbase_regnum = -1;
 
+  /* No shadow stack pointer register.  */
+  tdep->ssp_regnum = -1;
+
   tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
 
   set_gdbarch_relocate_instruction (gdbarch, i386_relocate_instruction);
@@ -8955,13 +8971,15 @@ const struct target_desc *
 i386_target_description (uint64_t xstate_bv_mask, bool segments)
 {
   static target_desc *i386_tdescs \
-    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
+    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/] \
+    [2/*segments*/] = {};
   target_desc **tdesc;
 
   tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
     [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
     [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
     [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
+    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
     [segments ? 1 : 0];
 
   if (*tdesc == NULL)
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 239bc8674e8..60d6f3eb732 100644
--- a/gdb/i386-tdep.h
+++ b/gdb/i386-tdep.h
@@ -191,6 +191,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
   /* PKEYS register names.  */
   const char * const *pkeys_register_names = nullptr;
 
+  /* Shadow stack pointer register.  */
+  int ssp_regnum = 0;
+
   /* Register number for %fsbase.  Set this to -1 to indicate the
      absence of segment base registers.  */
   int fsbase_regnum = 0;
@@ -293,6 +296,7 @@ enum i386_regnum
   I386_ZMM0H_REGNUM,		/* %zmm0h */
   I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
   I386_PKRU_REGNUM,
+  I386_PL3_SSP_REGNUM,
   I386_FSBASE_REGNUM,
   I386_GSBASE_REGNUM
 };
diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
index e9cf2527c5f..5bc36b6bef2 100644
--- a/gdb/nat/x86-linux-tdesc.c
+++ b/gdb/nat/x86-linux-tdesc.c
@@ -110,6 +110,8 @@ x86_linux_tdesc_for_tid (int tid, uint64_t *xstate_bv_storage,
 	    = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
 
 	  *xstate_bv_storage = xcr0;
+	  if (x86_check_ssp_support (tid))
+	    *xstate_bv_storage |= X86_XSTATE_CET_U;
 	}
     }
 
diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index 0bdff736f8a..1756d5441fc 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -17,6 +17,12 @@
    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 "elf/common.h"
+#include "gdbsupport/common-defs.h"
+#include "nat/gdb_ptrace.h"
+#include "nat/linux-ptrace.h"
+#include "nat/x86-cpuid.h"
+#include <sys/uio.h>
 #include "x86-linux.h"
 #include "x86-linux-dregs.h"
 #include "nat/gdb_ptrace.h"
@@ -126,3 +132,54 @@ x86_linux_ptrace_get_arch_size (int tid)
   return x86_linux_arch_size (false, false);
 #endif
 }
+
+/* See nat/x86-linux.h.  */
+
+bool
+x86_check_ssp_support (const int tid)
+{
+  /* It's not enough to check shadow stack support with the ptrace call
+     below only, as we cannot distinguish between shadow stack not enabled
+     for the current thread and shadow stack is not supported by HW.  In
+     both scenarios the ptrace call fails with ENODEV.  In case shadow
+     stack is not enabled for the current thread, we still want to return
+     true.  */
+  unsigned int eax, ebx, ecx, edx;
+
+  __get_cpuid_count (7, 0, &eax, &ebx, &ecx, &edx);
+
+  if ((ecx & bit_SHSTK) == 0)
+    return false;
+
+  /* Further check for NT_X86_SHSTK kernel support.  */
+  uint64_t ssp;
+  iovec iov {&ssp, sizeof (ssp) };
+
+  errno = 0;
+  int res = ptrace (PTRACE_GETREGSET, tid, NT_X86_SHSTK, &iov);
+  if (res < 0)
+    {
+      if (errno == EINVAL)
+	{
+	  /* The errno EINVAL for a PTRACE_GETREGSET call indicates that
+	     kernel support is not available.  */
+	  return false;
+	}
+      else if (errno == ENODEV)
+	{
+	  /* At this point, since we already checked CPUID, the errno
+	     ENODEV for a PTRACE_GETREGSET call indicates that shadow
+	     stack is not enabled for the current thread.  As it could be
+	     enabled later, we still want to return true here.  */
+	  return true;
+	}
+      else
+	{
+	  warning (_("Unknown ptrace error for NT_X86_SHSTK: %s"),
+		   safe_strerror (errno));
+	  return false;
+	}
+    }
+
+  return true;
+}
diff --git a/gdb/nat/x86-linux.h b/gdb/nat/x86-linux.h
index dbdef081515..1783aae05d0 100644
--- a/gdb/nat/x86-linux.h
+++ b/gdb/nat/x86-linux.h
@@ -75,4 +75,8 @@ struct x86_linux_arch_size
 
 extern x86_linux_arch_size x86_linux_ptrace_get_arch_size (int tid);
 
+/* Check shadow stack hardware and kernel support.  */
+
+extern bool x86_check_ssp_support (const int tid);
+
 #endif /* GDB_NAT_X86_LINUX_H */
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 00000000000..643ef2d5f56
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c
@@ -0,0 +1,22 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2018-2024 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+int
+main ()
+{
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp b/gdb/testsuite/gdb.arch/amd64-ssp.exp
new file mode 100644
index 00000000000..6ddc875b9a3
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
@@ -0,0 +1,50 @@
+# Copyright 2018-2024 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 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 -1
+    }
+
+    if {![runto_main]} {
+	return -1
+    }
+
+    # Read PL3_SSP register.
+    set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read pl3_ssp value"]
+
+    # Write PL3_SSP register.
+    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"
+
+    # Restore original value.
+    gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main" "restore original pl3_ssp"
+
+    # Potential CET violations often only occur after resuming normal execution.
+    # Therefore, it is important to test normal program continuation after
+    # configuring the shadow stack pointer.
+    gdb_continue_to_end
+}
+
diff --git a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py
index bc4a673b847..654ff447523 100644
--- a/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py
+++ b/gdb/testsuite/gdb.base/inline-frame-cycle-unwind.py
@@ -65,6 +65,10 @@ class TestUnwinder(Unwinder):
 
         for reg in pending_frame.architecture().registers("general"):
             val = pending_frame.read_register(reg)
+            # Having unavailable registers leads to a fall back to the standard
+            # unwinders.  Don't add unavailable registers to avoid this.
+            if (str (val) == "<unavailable>"):
+                continue
             unwinder.add_saved_register(reg, val)
         return unwinder
 
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index bb17c4e91a5..8b546914844 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -4287,6 +4287,76 @@ gdb_caching_proc allow_tsx_tests {} {
     return $allow_tsx_tests
 }
 
+# Run a test on the target to check if it supports x86 shadow stack.  Return 1
+# if shadow stack is enabled, 0 otherwise.
+
+gdb_caching_proc allow_ssp_tests {} {
+    global srcdir subdir gdb_prompt hex
+
+    set me "allow_ssp_tests"
+
+    if { ![istarget i?86-*-*] && ![istarget x86_64-*-* ] } {
+	verbose "$me: target known to not support shadow stack."
+	return 0
+    }
+
+    # There is no need to check the actual HW in addition to ptrace support.
+    # We need both checks and ptrace will tell us about the HW state.
+    set compile_flags "{additional_flags=-fcf-protection=return}"
+    set src { int main() { return 0; } }
+    if {![gdb_simple_compile $me $src executable $compile_flags]} {
+	return 0
+    }
+
+    save_vars { ::env(GLIBC_TUNABLES) } {
+
+	append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK"
+
+	# No error message, compilation succeeded so now run it via gdb.
+	gdb_exit
+	gdb_start
+	gdb_reinitialize_dir $srcdir/$subdir
+	gdb_load $obj
+	if {![runto_main]} {
+	    remote_file build delete $obj
+	    return 0
+	}
+	set shadow_stack_disabled_re "(<unavailable>)"
+	if {[istarget *-*-linux*]} {
+	    # Starting with v6.6, the Linux kernel supports CET shadow stack.
+	    # Dependent on the target we can see a nullptr or "<unavailable>"
+	    # when shadow stack is supported by HW and the Linux kernel but
+	    # not enabled for the current thread (for example due to a lack
+	    # of compiler or glibc support for -fcf-protection).
+	    set shadow_stack_disabled_re "$shadow_stack_disabled_re|(.*0x0)"
+	}
+
+	set allow_ssp_tests 0
+	gdb_test_multiple "print \$pl3_ssp" "test shadow stack support" {
+	    -re -wrap "(.*$hex)((?!(.*0x0)).)" {
+		verbose -log "$me: Shadow stack support detected."
+		set allow_ssp_tests 1
+	    }
+	    -re -wrap $shadow_stack_disabled_re {
+		# In case shadow stack is not enabled (for example due to a
+		# lack of compiler or glibc support for -fcf-protection).
+		verbose -log "$me: Shadow stack is not enabled."
+	    }
+	    -re -wrap "void" {
+		# In case we don't have hardware or kernel support.
+		verbose -log "$me: No shadow stack support."
+	    }
+	}
+
+	gdb_exit
+    }
+
+    remote_file build delete $obj
+
+    verbose "$me: returning $allow_ssp_tests" 2
+    return $allow_ssp_tests
+}
+
 # Run a test on the target to see if it supports avx512bf16.  Return 1 if so,
 # 0 if it does not.  Based on 'check_vmx_hw_available' from the GCC testsuite.
 
diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
index 1b7dd8506dd..660a906cdb6 100644
--- a/gdb/x86-linux-nat.c
+++ b/gdb/x86-linux-nat.c
@@ -41,6 +41,7 @@
 #include "nat/x86-linux.h"
 #include "nat/x86-linux-dregs.h"
 #include "nat/linux-ptrace.h"
+#include "x86-tdep.h"
 #include "nat/x86-linux-tdesc.h"
 
 /* linux_nat_target::low_new_fork implementation.  */
@@ -97,11 +98,10 @@ const struct target_desc *
 x86_linux_nat_target::read_description ()
 {
   /* The x86_linux_tdesc_for_tid call only reads xcr0 the first time it is
-     called.  The mask is stored in XSTATE_BV_STORAGE and reused on
-     subsequent calls.  Note that GDB currently supports features for user
-     state components only.  However, once supervisor state components are
-     supported in GDB, the value XSTATE_BV_STORAGE will not be configured
-     based on xcr0 only.  */
+     called.  Also it checks the enablement state of features which are
+     not configured in xcr0, such as CET shadow stack.  Once the supported
+     features are identified, the XSTATE_BV_STORAGE value is configured
+     accordingly and preserved for subsequent calls of this function.  */
   static uint64_t xstate_bv_storage;
 
   if (inferior_ptid == null_ptid)
@@ -215,6 +215,45 @@ x86_linux_get_thread_area (pid_t pid, void *addr, unsigned int *base_addr)
 }
 \f
 
+/* See x86-linux-nat.h.  */
+
+void
+x86_linux_fetch_ssp (regcache *regcache, const int tid)
+{
+  uint64_t ssp = 0x0;
+  iovec iov {&ssp, sizeof (ssp)};
+
+  /* The shadow stack may be enabled and disabled at runtime.  Reading the
+     ssp might fail as shadow stack was not activated for the current
+     thread.  We don't want to show a warning but silently return.  The
+     register will be shown as unavailable for the user.  */
+  if (ptrace (PTRACE_GETREGSET, tid, NT_X86_SHSTK, &iov) != 0)
+    return;
+
+  x86_supply_ssp (regcache, ssp);
+}
+
+/* See x86-linux-nat.h.  */
+
+void
+x86_linux_store_ssp (const regcache *regcache, const int tid)
+{
+  uint64_t ssp = 0x0;
+  iovec iov {&ssp, sizeof (ssp)};
+  x86_collect_ssp (regcache, ssp);
+
+  /* Dependent on the target the ssp register can be unavailable or
+     nullptr when shadow stack is supported by HW and the Linux kernel but
+     not enabled for the current thread.  In case of nullptr, GDB tries to
+     restore the shadow stack pointer after an inferior call.  The ptrace
+     call with PTRACE_SETREGSET will fail here with errno ENODEV.  We
+     don't want to throw an error in this case but silently continue.  */
+  errno = 0;
+  if ((ptrace (PTRACE_SETREGSET, tid, NT_X86_SHSTK, &iov) != 0)
+      && (errno != ENODEV))
+    perror_with_name (_("Failed to write pl3_ssp register"));
+}
+
 INIT_GDB_FILE (x86_linux_nat)
 {
   /* Initialize the debug register function vectors.  */
diff --git a/gdb/x86-linux-nat.h b/gdb/x86-linux-nat.h
index a62cc4d7ac0..c4556532226 100644
--- a/gdb/x86-linux-nat.h
+++ b/gdb/x86-linux-nat.h
@@ -92,4 +92,15 @@ struct x86_linux_nat_target : public x86_nat_target<linux_nat_target>
 extern ps_err_e x86_linux_get_thread_area (pid_t pid, void *addr,
 					   unsigned int *base_addr);
 
+/* Fetch the value of the shadow stack pointer register from process/thread
+   TID and store it to GDB's register cache.  */
+
+extern void x86_linux_fetch_ssp (regcache *regcache, const int tid);
+
+/* Read the value of the shadow stack pointer from GDB's register cache
+   and store it in the shadow stack pointer register of process/thread TID.
+   Throw an error in case of failure.  */
+
+extern void x86_linux_store_ssp (const regcache *regcache, const int tid);
+
 #endif /* GDB_X86_LINUX_NAT_H */
diff --git a/gdb/x86-tdep.c b/gdb/x86-tdep.c
index 6646b1157c8..06dec4e1f90 100644
--- a/gdb/x86-tdep.c
+++ b/gdb/x86-tdep.c
@@ -17,10 +17,31 @@
    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 "i386-tdep.h"
 #include "x86-tdep.h"
 #include "symtab.h"
 
 
+/* See x86-tdep.h.  */
+
+void
+x86_supply_ssp (regcache *regcache, const uint64_t ssp)
+{
+  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (regcache->arch ());
+  gdb_assert (tdep != nullptr && tdep->ssp_regnum > 0);
+  regcache->raw_supply (tdep->ssp_regnum, &ssp);
+}
+
+/* See x86-tdep.h.  */
+
+void
+x86_collect_ssp (const regcache *regcache, uint64_t& ssp)
+{
+  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (regcache->arch ());
+  gdb_assert (tdep != nullptr && tdep->ssp_regnum > 0);
+  regcache->raw_collect (tdep->ssp_regnum, &ssp);
+}
+
 /* Check whether NAME is included in NAMES[LO] (inclusive) to NAMES[HI]
    (exclusive).  */
 
diff --git a/gdb/x86-tdep.h b/gdb/x86-tdep.h
index 35e3905f2e3..53c3956ea5e 100644
--- a/gdb/x86-tdep.h
+++ b/gdb/x86-tdep.h
@@ -20,6 +20,15 @@
 #ifndef GDB_X86_TDEP_H
 #define GDB_X86_TDEP_H
 
+/* Fill SSP to the shadow stack pointer in GDB's REGCACHE.  */
+
+extern void x86_supply_ssp (regcache *regcache, const uint64_t ssp);
+
+/* Collect the value of the shadow stack pointer in GDB's REGCACHE and
+   write it to SSP.  */
+
+extern void x86_collect_ssp (const regcache *regcache, uint64_t& ssp);
+
 /* Checks whether PC lies in an indirect branch thunk using registers
    REGISTER_NAMES[LO] (inclusive) to REGISTER_NAMES[HI] (exclusive).  */
 
diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
index dbc11d6856d..ec7064c2834 100644
--- a/gdbserver/linux-x86-low.cc
+++ b/gdbserver/linux-x86-low.cc
@@ -253,7 +253,8 @@ static const int x86_64_regmap[] =
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1,
-  -1					/* pkru  */
+  -1,					/* pkru  */
+  -1					/* CET user mode register PL3_SSP.  */
 };
 
 #define X86_64_NUM_REGS (sizeof (x86_64_regmap) / sizeof (x86_64_regmap[0]))
@@ -405,6 +406,18 @@ x86_target::low_cannot_fetch_register (int regno)
   return regno >= I386_NUM_REGS;
 }
 
+static void
+x86_fill_ssp_reg (regcache *regcache, void *buf)
+{
+  collect_register_by_name (regcache, "pl3_ssp", buf);
+}
+
+static void
+x86_store_ssp_reg (regcache *regcache, const void *buf)
+{
+  supply_register_by_name (regcache, "pl3_ssp", buf);
+}
+
 static void
 collect_register_i386 (struct regcache *regcache, int regno, void *buf)
 {
@@ -544,6 +557,8 @@ static struct regset_info x86_regsets[] =
     x86_fill_gregset, x86_store_gregset },
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_X86_XSTATE, 0,
     EXTENDED_REGS, x86_fill_xstateregset, x86_store_xstateregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_X86_SHSTK, 0,
+    OPTIONAL_RUNTIME_REGS, x86_fill_ssp_reg, x86_store_ssp_reg },
 # ifndef __x86_64__
 #  ifdef HAVE_PTRACE_GETFPXREGS
   { PTRACE_GETFPXREGS, PTRACE_SETFPXREGS, 0, sizeof (elf_fpxregset_t),
@@ -897,6 +912,17 @@ x86_linux_read_description ()
 	    {
 	      if (regset->nt_type == NT_X86_XSTATE)
 		regset->size = xsave_len;
+	      else if (regset->nt_type == NT_X86_SHSTK)
+		{
+		  /* We must configure the size of the NT_X86_SHSTK regset
+		     from non-zero value to it's appropriate size, even though
+		     the ptrace call is only tested for NT_X86_XSTATE request,
+		     because the NT_X86_SHSTK regset is of type
+		     OPTIONAL_RUNTIME_REGS.  A ptrace call with NT_X86_SHSTK
+		     request may only be successful later on, once shadow
+		     stack is enabled for the current thread.  */
+		    regset->size = sizeof (CORE_ADDR);
+		}
 	      else
 		gdb_assert_not_reached ("invalid regset type.");
 	    }
diff --git a/gdbsupport/x86-xstate.h b/gdbsupport/x86-xstate.h
index 9bb373c3100..6657c450c88 100644
--- a/gdbsupport/x86-xstate.h
+++ b/gdbsupport/x86-xstate.h
@@ -28,6 +28,7 @@
 #define X86_XSTATE_ZMM_H_ID	6
 #define X86_XSTATE_ZMM_ID	7
 #define X86_XSTATE_PKRU_ID	9
+#define X86_XSTATE_CET_U_ID	11
 
 /* The extended state feature bits.  */
 #define X86_XSTATE_X87		(1ULL << X86_XSTATE_X87_ID)
@@ -42,6 +43,7 @@
 				 | X86_XSTATE_ZMM)
 
 #define X86_XSTATE_PKRU		(1ULL << X86_XSTATE_PKRU_ID)
+#define X86_XSTATE_CET_U	(1ULL << X86_XSTATE_CET_U_ID)
 
 /* Total size of the XSAVE area extended region and offsets of
    register states within the region.  Offsets are set to 0 to
@@ -86,7 +88,8 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
 /* Supported mask of state-component bitmap xstate_bv.  The SDM defines
    xstate_bv as XCR0 | IA32_XSS.  */
 
-#define X86_XSTATE_ALL_MASK		(X86_XSTATE_AVX_AVX512_PKU_MASK)
+#define X86_XSTATE_ALL_MASK		(X86_XSTATE_AVX_AVX512_PKU_MASK\
+					| X86_XSTATE_CET_U)
 
 #define X86_XSTATE_SSE_SIZE	576
 #define X86_XSTATE_AVX_SIZE	832
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (5 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-07-29 14:46   ` Andrew Burgess
  2025-06-28  8:28 ` [PATCH v5 08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux Christina Schimpe
                   ` (7 subsequent siblings)
  14 siblings, 1 reply; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

Intel's Control-Flow Enforcement Technology (CET) provides the shadow
stack feature for the x86 architecture.

This commit adds support to write and read the shadow-stack node in
corefiles.  This helps debugging return address violations post-mortem.
The format is synced with the linux kernel commit "x86: Add PTRACE
interface for shadow stack".  As the linux kernel restricts shadow
stack support to 64-bit, apply the fix for amd64 only.

Co-Authored-By: Christina Schimpe <christina.schimpe@intel.com>

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
---
 gdb/amd64-linux-tdep.c                        |  57 ++++++++-
 .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 +++++++
 .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 ++++++++++++++++++
 3 files changed, 205 insertions(+), 4 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index edb7d8da6ab..9af7a41ea26 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -47,6 +47,7 @@
 #include "expop.h"
 #include "arch/amd64-linux-tdesc.h"
 #include "inferior.h"
+#include "x86-tdep.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
@@ -1593,6 +1594,14 @@ amd64_linux_record_signal (struct gdbarch *gdbarch,
   return 0;
 }
 
+/* Get shadow stack pointer state from core dump.  */
+
+static bool
+amd64_linux_core_read_ssp_state_p (bfd *abfd)
+{
+  return bfd_get_section_by_name (abfd, ".reg-ssp") != nullptr;
+}
+
 /* Get Linux/x86 target description from core dump.  */
 
 static const struct target_desc *
@@ -1602,11 +1611,14 @@ amd64_linux_core_read_description (struct gdbarch *gdbarch,
 {
   /* Linux/x86-64.  */
   x86_xsave_layout layout;
-  uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
-  if (xcr0 == 0)
-    xcr0 = X86_XSTATE_SSE_MASK;
+  uint64_t xstate_bv_mask = i386_linux_core_read_xsave_info (abfd, layout);
+  if (xstate_bv_mask == 0)
+    xstate_bv_mask = X86_XSTATE_SSE_MASK;
+
+  if (amd64_linux_core_read_ssp_state_p (abfd))
+    xstate_bv_mask |= X86_XSTATE_CET_U;
 
-  return amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
+  return amd64_linux_read_description (xstate_bv_mask & X86_XSTATE_ALL_MASK,
 				       gdbarch_ptr_bit (gdbarch) == 32);
 }
 
@@ -1637,6 +1649,35 @@ static const struct regset amd64_linux_xstateregset =
     amd64_linux_collect_xstateregset
   };
 
+/* Supply shadow stack pointer register from SSP to the register cache
+   REGCACHE.  */
+
+static void
+amd64_linux_supply_ssp (const regset *regset,
+			regcache *regcache, int regnum,
+			const void *ssp, size_t len)
+{
+  x86_supply_ssp (regcache, *static_cast<const uint64_t *> (ssp));
+}
+
+/* Collect the shadow stack pointer register from the register cache
+   REGCACHE and store it in SSP.  */
+
+static void
+amd64_linux_collect_ssp (const regset *regset,
+			 const regcache *regcache, int regnum,
+			 void *ssp, size_t len)
+{
+  x86_collect_ssp (regcache, *static_cast<uint64_t *> (ssp));
+}
+
+/* Shadow stack pointer register.  */
+
+static const struct regset amd64_linux_ssp_register
+  {
+    NULL, amd64_linux_supply_ssp, amd64_linux_collect_ssp
+  };
+
 /* Iterate over core file register note sections.  */
 
 static void
@@ -1653,6 +1694,14 @@ amd64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
     cb (".reg-xstate", tdep->xsave_layout.sizeof_xsave,
 	tdep->xsave_layout.sizeof_xsave, &amd64_linux_xstateregset,
 	"XSAVE extended state", cb_data);
+
+  /* SSP can be unavailable.  Thus, we need to check the register status
+     in case we write a core file (regcache != nullptr).  */
+  if (tdep->ssp_regnum > 0
+      && (regcache == nullptr
+	  || REG_VALID == regcache->get_register_status (tdep->ssp_regnum)))
+    cb (".reg-ssp", 8, 8, &amd64_linux_ssp_register,
+	"shadow stack pointer", cb_data);
 }
 
 /* The instruction sequences used in x86_64 machines for a
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 00000000000..f078e33810d
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
@@ -0,0 +1,42 @@
+/* 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;
+  asm volatile("xor %0, %0; rdsspq %0" : "=r" (ssp));
+
+  /* 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 00000000000..8784fc3622c
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
@@ -0,0 +1,110 @@
+# Copyright 2021-2024 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.
+
+require allow_ssp_tests
+
+standard_testfile
+
+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
+    }
+
+    # 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"]
+
+    # 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 != ""}]
+    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.
+
+    if {$gcore_generated} {
+	clean_restart $binfile
+
+	with_test_prefix "gcore corefile" {
+	    check_core_file $gcore_filename $ssp_in_gcore
+	}
+    } else {
+	fail "gcore corefile not generated"
+    }
+
+    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
+	}
+    } else {
+	untested "OS corefile not generated"
+    }
+}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (6 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-07-30  9:58   ` Andrew Burgess
  2025-06-28  8:28 ` [PATCH v5 09/12] gdb, gdbarch: Enable inferior calls for shadow stack support Christina Schimpe
                   ` (6 subsequent siblings)
  14 siblings, 1 reply; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

Unwind the $pl3_ssp register.
We now have an updated value for the shadow stack pointer when
moving up or down the frame level.  Note that $pl3_ssp can
become unavailable when moving to a frame before the shadow
stack enablement.  In the example below, shadow stack is enabled
in the function 'call1'.  Thus, when moving to a frame level above
the function, $pl3_ssp will become unavaiable.
Following the restriction of the linux kernel, implement the unwinding
for amd64 linux only.

Before this patch:
~~~
Breakpoint 1, call2 (j=3) at sample.c:44
44	  return 42;
(gdb) p $pl3_ssp
$1 = (void *) 0x7ffff79ffff8
(gdb) up
55	  call2 (3);
(gdb) p $pl3_ssp
$2 = (void *) 0x7ffff79ffff8
(gdb) up
68	  call1 (43);
(gdb) p $pl3_ssp
$3 = (void *) 0x7ffff79ffff8
~~~

After this patch:
~~~
Breakpoint 1, call2 (j=3) at sample.c:44
44	  return 42;
(gdb) p $pl3_ssp
$1 = (void *) 0x7ffff79ffff8
(gdb) up
55	  call2 (3);
(gdb) p $pl3_ssp
$2 = (void *) 0x7ffff7a00000
(gdb) up
68	  call1 (43i);
(gdb) p $pl3_ssp
$3 = <unavailable>
~~~

As we now have an updated value for each selected frame, the
return command is now enabled for shadow stack enabled programs, too.

We therefore add a test for the return command and shadow stack support,
and for an updated shadow stack pointer after a frame level change.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
 gdb/amd64-linux-tdep.c                        | 85 ++++++++++++++++++
 gdb/linux-tdep.c                              | 47 ++++++++++
 gdb/linux-tdep.h                              |  7 ++
 .../gdb.arch/amd64-shadow-stack-cmds.exp      | 88 +++++++++++++++++++
 gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 13 +++
 5 files changed, 240 insertions(+)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 9af7a41ea26..f6ae5395870 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -48,6 +48,8 @@
 #include "arch/amd64-linux-tdesc.h"
 #include "inferior.h"
 #include "x86-tdep.h"
+#include "dwarf2/frame.h"
+#include "frame-unwind.h"
 
 /* The syscall's XML filename for i386.  */
 #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
@@ -1918,6 +1920,88 @@ amd64_linux_get_tls_dtv_addr (struct gdbarch *gdbarch, ptid_t ptid,
   return dtv_addr;
 }
 
+/* Return the number of bytes required to update the shadow stack pointer
+   by one element.  For x32 the shadow stack elements are still 64-bit
+   aligned.  Thus, gdbarch_addr_bit cannot be used to compute the new
+   stack pointer.  */
+
+static inline int
+amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch)
+{
+  const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
+  return (binfo->bits_per_word / binfo->bits_per_byte);
+}
+
+
+/* Implement shadow stack pointer unwinding. For each new shadow stack
+   pointer check if its address is still in the shadow stack memory range.
+   If it's outside the range set the returned value to unavailable,
+   otherwise return a value containing the new shadow stack pointer.  */
+
+static value *
+amd64_linux_dwarf2_prev_ssp (const frame_info_ptr &this_frame,
+			     void **this_cache, int regnum)
+{
+  value *v = frame_unwind_got_register (this_frame, regnum, regnum);
+  gdb_assert (v != nullptr);
+
+  gdbarch *gdbarch = get_frame_arch (this_frame);
+
+  if (v->entirely_available () && !v->optimized_out ())
+    {
+      int size = register_size (gdbarch, regnum);
+      bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+      CORE_ADDR ssp = extract_unsigned_integer (v->contents_all ().data (),
+						size, byte_order);
+
+      /* Using /proc/PID/smaps we can only check if the current shadow
+	 stack pointer SSP points to shadow stack memory.  Only if this is
+	 the case a valid previous shadow stack pointer can be
+	 calculated.  */
+      std::pair<CORE_ADDR, CORE_ADDR> range;
+      if (linux_address_in_shadow_stack_mem_range (ssp, &range))
+	{
+	  /* The shadow stack grows downwards.  To compute the previous
+	     shadow stack pointer, we need to increment SSP.  */
+	  CORE_ADDR new_ssp
+	    = ssp + amd64_linux_shadow_stack_element_size_aligned (gdbarch);
+
+	  /* There can be scenarios where we have a shadow stack pointer
+	     but the shadow stack is empty, as no call instruction has
+	     been executed yet.  If NEW_SSP points to the end of or before
+	     (<=) the current shadow stack memory range we consider
+	     NEW_SSP as valid (but empty).  */
+	  if (new_ssp <= range.second)
+	    return frame_unwind_got_address (this_frame, regnum, new_ssp);
+	}
+    }
+
+  /* Return a value which is marked as unavailable in case we could not
+     calculate a valid previous shadow stack pointer.  */
+  value *retval
+    = value::allocate_register (get_next_frame_sentinel_okay (this_frame),
+				regnum, register_type (gdbarch, regnum));
+  retval->mark_bytes_unavailable (0, retval->type ()->length ());
+  return retval;
+}
+
+/* Implement the "init_reg" dwarf2_frame_ops method.  */
+
+static void
+amd64_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg,
+		const frame_info_ptr &this_frame)
+{
+  if (regnum == gdbarch_pc_regnum (gdbarch))
+    reg->how = DWARF2_FRAME_REG_RA;
+  else if (regnum == gdbarch_sp_regnum (gdbarch))
+    reg->how = DWARF2_FRAME_REG_CFA;
+  else if (regnum == AMD64_PL3_SSP_REGNUM)
+    {
+      reg->how = DWARF2_FRAME_REG_FN;
+      reg->loc.fn = amd64_linux_dwarf2_prev_ssp;
+    }
+}
+
 static void
 amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
 			    int num_disp_step_buffers)
@@ -1975,6 +2059,7 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
 
   set_gdbarch_remove_non_address_bits_watchpoint
     (gdbarch, amd64_linux_remove_non_address_bits_watchpoint);
+  dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);
 }
 
 static void
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index 5c4bbf66563..a6a50f93727 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -47,6 +47,7 @@
 #include "gdbsupport/unordered_map.h"
 
 #include <ctype.h>
+#include <algorithm>
 
 /* This enum represents the values that the user can choose when
    informing the Linux kernel about which memory mappings will be
@@ -96,6 +97,10 @@ struct smaps_vmflags
     /* Memory map has memory tagging enabled.  */
 
     unsigned int memory_tagging : 1;
+
+    /* Memory map used for shadow stack.  */
+
+    unsigned int shadow_stack_memory : 1;
   };
 
 /* Data structure that holds the information contained in the
@@ -537,6 +542,8 @@ decode_vmflags (char *p, struct smaps_vmflags *v)
 	v->shared_mapping = 1;
       else if (strcmp (s, "mt") == 0)
 	v->memory_tagging = 1;
+      else if (strcmp (s, "ss") == 0)
+	v->shadow_stack_memory = 1;
     }
 }
 
@@ -3036,6 +3043,46 @@ show_dump_excluded_mappings (struct ui_file *file, int from_tty,
 		      " flag is %s.\n"), value);
 }
 
+/* See linux-tdep.h.  */
+
+bool
+linux_address_in_shadow_stack_mem_range
+  (CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range)
+{
+  if (!target_has_execution () || current_inferior ()->fake_pid_p)
+    return false;
+
+  const int pid = current_inferior ()->pid;
+
+  std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
+
+  gdb::unique_xmalloc_ptr<char> data
+    = target_fileio_read_stralloc (nullptr, smaps_file.c_str ());
+
+  if (data == nullptr)
+    return false;
+
+  const std::vector<smaps_data> smaps
+    = parse_smaps_data (data.get (), std::move (smaps_file));
+
+  auto find_addr_mem_range = [&addr] (const smaps_data &map)
+    {
+      bool addr_in_mem_range
+	= (addr >= map.start_address && addr < map.end_address);
+      return (addr_in_mem_range && map.vmflags.shadow_stack_memory);
+    };
+  auto it = std::find_if (smaps.begin (), smaps.end (), find_addr_mem_range);
+
+  if (it != smaps.end ())
+    {
+      range->first = it->start_address;
+      range->second = it->end_address;
+      return true;
+    }
+
+  return false;
+}
+
 /* To be called from the various GDB_OSABI_LINUX handlers for the
    various GNU/Linux architectures and machine types.
 
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index 3d82ea5bbdf..2e7022dfa39 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -113,4 +113,11 @@ extern CORE_ADDR linux_get_hwcap2 (const std::optional<gdb::byte_vector> &auxv,
 
 extern CORE_ADDR linux_get_hwcap2 ();
 
+/* Returns true if ADDR belongs to a shadow stack memory range.  If this
+   is the case, assign the shadow stack memory range to RANGE
+   [start_address, end_address).  */
+
+extern bool linux_address_in_shadow_stack_mem_range
+  (CORE_ADDR addr, std::pair<CORE_ADDR, CORE_ADDR> *range);
+
 #endif /* GDB_LINUX_TDEP_H */
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 00000000000..17f32ce3964
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
@@ -0,0 +1,88 @@
+# Copyright 2018-2024 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 and the return command.
+
+require allow_ssp_tests
+
+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} \
+	  {debug additional_flags="-fcf-protection=return"}] } {
+	return -1
+    }
+
+    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.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c
index 643ef2d5f56..80389730bc2 100644
--- a/gdb/testsuite/gdb.arch/amd64-shadow-stack.c
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack.c
@@ -15,8 +15,21 @@
  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 ()
 {
+  call1 (); /* break main.  */
   return 0;
 }
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 09/12] gdb, gdbarch: Enable inferior calls for shadow stack support.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (7 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-07-30 10:42   ` Andrew Burgess
  2025-06-28  8:28 ` [PATCH v5 10/12] gdb: Implement amd64 linux shadow stack support for inferior calls Christina Schimpe
                   ` (5 subsequent siblings)
  14 siblings, 1 reply; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

Inferior calls in GDB reset the current PC to the beginning of the function
that is called.  As no call instruction is executed the new return address
needs to be pushed to the shadow stack and the shadow stack pointer needs
to be updated.

This commit adds a new gdbarch method to push an address on the shadow
stack.  The method is used to adapt the function 'call_function_by_hand_dummy'
for inferior call shadow stack support.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
 gdb/gdbarch-gen.c         | 32 ++++++++++++++++++++++++++++++++
 gdb/gdbarch-gen.h         | 14 ++++++++++++++
 gdb/gdbarch_components.py | 16 ++++++++++++++++
 gdb/infcall.c             | 14 ++++++++++----
 4 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
index fc570d37a8d..a4b72793fd8 100644
--- a/gdb/gdbarch-gen.c
+++ b/gdb/gdbarch-gen.c
@@ -262,6 +262,7 @@ struct gdbarch
   gdbarch_read_core_file_mappings_ftype *read_core_file_mappings = default_read_core_file_mappings;
   gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes;
   gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context;
+  gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -535,6 +536,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of read_core_file_mappings, invalid_p == 0.  */
   /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0.  */
   /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
+  /* Skip verify of shadow_stack_push, has predicate.  */
   if (!log.empty ())
     internal_error (_("verify_gdbarch: the following are invalid ...%s"),
 		    log.c_str ());
@@ -1406,6 +1408,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: core_parse_exec_context = <%s>\n",
 	      host_address_to_string (gdbarch->core_parse_exec_context));
+  gdb_printf (file,
+	      "gdbarch_dump: gdbarch_shadow_stack_push_p() = %d\n",
+	      gdbarch_shadow_stack_push_p (gdbarch));
+  gdb_printf (file,
+	      "gdbarch_dump: shadow_stack_push = <%s>\n",
+	      host_address_to_string (gdbarch->shadow_stack_push));
   if (gdbarch->dump_tdep != NULL)
     gdbarch->dump_tdep (gdbarch, file);
 }
@@ -5551,3 +5559,27 @@ set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch,
 {
   gdbarch->core_parse_exec_context = core_parse_exec_context;
 }
+
+bool
+gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  return gdbarch->shadow_stack_push != NULL;
+}
+
+void
+gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->shadow_stack_push != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_push called\n");
+  gdbarch->shadow_stack_push (gdbarch, new_addr, regcache);
+}
+
+void
+set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
+			       gdbarch_shadow_stack_push_ftype shadow_stack_push)
+{
+  gdbarch->shadow_stack_push = shadow_stack_push;
+}
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 281b97b7aa8..71142332540 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -1802,3 +1802,17 @@ extern void set_gdbarch_use_target_description_from_corefile_notes (struct gdbar
 typedef core_file_exec_context (gdbarch_core_parse_exec_context_ftype) (struct gdbarch *gdbarch, bfd *cbfd);
 extern core_file_exec_context gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, bfd *cbfd);
 extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarch_core_parse_exec_context_ftype *core_parse_exec_context);
+
+/* Some targets support special hardware-assisted control-flow protection
+   technologies.  For example, the Intel Control-Flow Enforcement Technology
+   (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
+   To enable shadow stack support for inferior calls the shadow_stack_push
+   gdbarch hook has to be provided.
+
+   Push NEW_ADDR to the shadow stack and update the shadow stack pointer. */
+
+extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch);
+
+typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
+extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
+extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push);
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index 91c867e69bf..abc79588473 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -2848,3 +2848,19 @@ which all assume current_inferior() is the one to read from.
     predefault="default_core_parse_exec_context",
     invalid=False,
 )
+
+Method(
+    comment="""
+Some targets support special hardware-assisted control-flow protection
+technologies.  For example, the Intel Control-Flow Enforcement Technology
+(Intel CET) on x86 provides a shadow stack and indirect branch tracking.
+To enable shadow stack support for inferior calls the shadow_stack_push
+gdbarch hook has to be provided.
+
+Push NEW_ADDR to the shadow stack and update the shadow stack pointer.
+""",
+    type="void",
+    name="shadow_stack_push",
+    params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
+    predicate=True,
+)
diff --git a/gdb/infcall.c b/gdb/infcall.c
index 2b5936d1621..db6d6774367 100644
--- a/gdb/infcall.c
+++ b/gdb/infcall.c
@@ -1448,10 +1448,16 @@ call_function_by_hand_dummy (struct value *function,
   /* Create the dummy stack frame.  Pass in the call dummy address as,
      presumably, the ABI code knows where, in the call dummy, the
      return address should be pointed.  */
-  sp = gdbarch_push_dummy_call (gdbarch, function,
-				get_thread_regcache (inferior_thread ()),
-				bp_addr, args.size (), args.data (),
-				sp, return_method, struct_addr);
+  regcache *regcache = get_thread_regcache (inferior_thread ());
+  sp = gdbarch_push_dummy_call (gdbarch, function, regcache, bp_addr,
+				args.size (), args.data (), sp,
+				return_method, struct_addr);
+
+  /* Push the return address of the inferior (bp_addr) to the shadow stack
+     and update the shadow stack pointer.  As we don't execute a call
+     instruction to call the function we need to handle this manually.  */
+  if (gdbarch_shadow_stack_push_p (gdbarch))
+    gdbarch_shadow_stack_push (gdbarch, bp_addr, regcache);
 
   /* Set up a frame ID for the dummy frame so we can pass it to
      set_momentary_breakpoint.  We need to give the breakpoint a frame
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 10/12] gdb: Implement amd64 linux shadow stack support for inferior calls.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (8 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 09/12] gdb, gdbarch: Enable inferior calls for shadow stack support Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-07-30 11:58   ` Andrew Burgess
  2025-06-28  8:28 ` [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer Christina Schimpe
                   ` (4 subsequent siblings)
  14 siblings, 1 reply; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

This patch enables inferior calls to support Intel's Control-Flow
Enforcement Technology (CET), which provides the shadow stack feature
for the x86 architecture.
Following the restriction of the linux kernel, enable inferior calls
for amd64 only.

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
 gdb/amd64-linux-tdep.c                        | 63 +++++++++++++++++++
 gdb/doc/gdb.texinfo                           | 29 +++++++++
 .../gdb.arch/amd64-shadow-stack-cmds.exp      | 55 +++++++++++++++-
 3 files changed, 146 insertions(+), 1 deletion(-)

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index f6ae5395870..899fe2df02c 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1932,6 +1932,67 @@ amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch)
   return (binfo->bits_per_word / binfo->bits_per_byte);
 }
 
+/* Read the shadow stack pointer register and return its value, if
+   possible.  */
+
+static std::optional<CORE_ADDR>
+amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache)
+{
+  const i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+
+  if (tdep == nullptr || tdep->ssp_regnum < 0)
+    return {};
+
+  CORE_ADDR ssp;
+  if (regcache_raw_read_unsigned (regcache, tdep->ssp_regnum, &ssp)
+      != REG_VALID)
+    return {};
+
+  /* Dependent on the target in case the shadow stack pointer is
+     unavailable, the ssp register can be invalid or 0x0 when shadow stack
+     is supported by HW and the linux kernel but not enabled for the
+     current thread.  */
+  if (ssp == 0x0)
+    return {};
+
+  return ssp;
+}
+
+/* If shadow stack is enabled, push the address NEW_ADDR to the shadow
+   stack and increment the shadow stack pointer accordingly.  */
+
+static void
+amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
+			       regcache *regcache)
+{
+  std::optional<CORE_ADDR> ssp
+    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache);
+  if (!ssp.has_value ())
+    return;
+
+  /* The shadow stack grows downwards.  To push addresses to the stack,
+     we need to decrement SSP.    */
+  const int element_size
+    = amd64_linux_shadow_stack_element_size_aligned (gdbarch);
+  const CORE_ADDR new_ssp = *ssp - element_size;
+
+  /* Using /proc/PID/smaps we can only check if NEW_SSP points to shadow
+     stack memory.  If it doesn't, we assume the stack is full.  */
+  std::pair<CORE_ADDR, CORE_ADDR> memrange;
+  if (!linux_address_in_shadow_stack_mem_range (new_ssp, &memrange))
+    error (_("No space left on the shadow stack."));
+
+  /* On x86 there can be a shadow stack token at bit 63.  For x32, the
+     address size is only 32 bit.  Thus, we must use ELEMENT_SIZE (and
+     not gdbarch_addr_bit) to determine the width of the address to be
+     written.  */
+  const bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  write_memory_unsigned_integer (new_ssp, element_size, byte_order,
+				 (ULONGEST) new_addr);
+
+  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
+  regcache_raw_write_unsigned (regcache, tdep->ssp_regnum, new_ssp);
+}
 
 /* Implement shadow stack pointer unwinding. For each new shadow stack
    pointer check if its address is still in the shadow stack memory range.
@@ -2059,6 +2120,8 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
 
   set_gdbarch_remove_non_address_bits_watchpoint
     (gdbarch, amd64_linux_remove_non_address_bits_watchpoint);
+
+  set_gdbarch_shadow_stack_push (gdbarch, amd64_linux_shadow_stack_push);
   dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);
 }
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 0881ac4aee5..b5120b78426 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -27037,6 +27037,35 @@ registers
 
 @end itemize
 
+@subsubsection Intel Control-Flow Enforcement Technology.
+@cindex Intel Control-Flow Enforcement Technology.
+
+The @dfn{Intel Control-Flow Enforcement Technology} (@acronym{Intel CET})
+provides two capabilities to defend against ``Return-oriented Programming''
+and ``call/jmp-oriented programming'' style control-flow attacks:
+
+@itemize @bullet
+@item Shadow Stack:
+A shadow stack is a second stack for a program.  It holds the return
+addresses pushed by the call instruction.  The @code{RET} instruction pops the
+return addresses from both call and shadow stack.  If the return addresses from
+the two stacks do not match, the processor signals a control protection
+exception.
+@item Indirect Branch Tracking (IBT):
+When IBT is enabled, the CPU implements a state machine that tracks indirect
+@code{JMP} and @code{CALL} instructions.  The state machine can be either IDLE
+or WAIT_FOR_ENDBRANCH.  In WAIT_FOR_ENDBRANCH state the next instruction in
+the program stream must be an @code{ENDBR} instruction, otherwise the
+processor signals a control protection exception.
+@end itemize
+
+Impact on Call/Print:
+Inferior calls in @value{GDBN} reset the current PC to the beginning of the
+function that is called.  No call instruction is executed, but the @code{RET}
+instruction actually is.  To avoid a control protection exception due to the
+missing return address on the shadow stack, @value{GDBN} pushes the new return
+address to the shadow stack and updates the shadow stack pointer.
+
 @node Alpha
 @subsection Alpha
 
diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
index 17f32ce3964..622612d2f7d 100644
--- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
@@ -13,12 +13,29 @@
 # 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 and the return command.
+# 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
 
+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"
@@ -33,6 +50,42 @@ save_vars { ::env(GLIBC_TUNABLES) } {
 	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" ]
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (9 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 10/12] gdb: Implement amd64 linux shadow stack support for inferior calls Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-07-30 12:22   ` Andrew Burgess
  2025-06-28  8:28 ` [PATCH v5 12/12] gdb: Enable displaced stepping with shadow stack on amd64 linux Christina Schimpe
                   ` (3 subsequent siblings)
  14 siblings, 1 reply; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

This patch is required by the following commit
"gdb: Enable displaced stepping with shadow stack on amd64 linux."

Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Reviewed-By: Luis Machado <luis.machado@arm.com>
---
 gdb/arch-utils.c          | 10 ++++++++++
 gdb/arch-utils.h          |  5 +++++
 gdb/gdbarch-gen.c         | 22 ++++++++++++++++++++++
 gdb/gdbarch-gen.h         | 12 +++++++++++-
 gdb/gdbarch_components.py | 17 ++++++++++++++++-
 5 files changed, 64 insertions(+), 2 deletions(-)

diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index f320d3d7365..c396e9e3840 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -1218,6 +1218,16 @@ default_gdbarch_return_value
 				readbuf, writebuf);
 }
 
+/* See arch-utils.h.  */
+
+std::optional<CORE_ADDR>
+default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
+				  bool &shadow_stack_enabled)
+{
+  shadow_stack_enabled = false;
+  return {};
+}
+
 obstack *gdbarch_obstack (gdbarch *arch)
 {
   return &arch->obstack;
diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index 1509cb7441e..14a84b74733 100644
--- a/gdb/arch-utils.h
+++ b/gdb/arch-utils.h
@@ -414,4 +414,9 @@ extern enum return_value_convention default_gdbarch_return_value
       struct regcache *regcache, struct value **read_value,
       const gdb_byte *writebuf);
 
+/* Default implementation of gdbarch default_get_shadow_stack_pointer
+   method.  */
+extern std::optional<CORE_ADDR> default_get_shadow_stack_pointer
+  (gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
+
 #endif /* GDB_ARCH_UTILS_H */
diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
index a4b72793fd8..caeda3cefae 100644
--- a/gdb/gdbarch-gen.c
+++ b/gdb/gdbarch-gen.c
@@ -263,6 +263,7 @@ struct gdbarch
   gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes;
   gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context;
   gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
+  gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer = default_get_shadow_stack_pointer;
 };
 
 /* Create a new ``struct gdbarch'' based on information provided by
@@ -537,6 +538,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0.  */
   /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
   /* Skip verify of shadow_stack_push, has predicate.  */
+  /* Skip verify of get_shadow_stack_pointer, invalid_p == 0.  */
   if (!log.empty ())
     internal_error (_("verify_gdbarch: the following are invalid ...%s"),
 		    log.c_str ());
@@ -1414,6 +1416,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   gdb_printf (file,
 	      "gdbarch_dump: shadow_stack_push = <%s>\n",
 	      host_address_to_string (gdbarch->shadow_stack_push));
+  gdb_printf (file,
+	      "gdbarch_dump: get_shadow_stack_pointer = <%s>\n",
+	      host_address_to_string (gdbarch->get_shadow_stack_pointer));
   if (gdbarch->dump_tdep != NULL)
     gdbarch->dump_tdep (gdbarch, file);
 }
@@ -5583,3 +5588,20 @@ set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
 {
   gdbarch->shadow_stack_push = shadow_stack_push;
 }
+
+std::optional<CORE_ADDR>
+gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_shadow_stack_pointer != NULL);
+  if (gdbarch_debug >= 2)
+    gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n");
+  return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled);
+}
+
+void
+set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
+				      gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer)
+{
+  gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer;
+}
diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
index 71142332540..c36171b089e 100644
--- a/gdb/gdbarch-gen.h
+++ b/gdb/gdbarch-gen.h
@@ -1807,7 +1807,8 @@ extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc
    technologies.  For example, the Intel Control-Flow Enforcement Technology
    (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
    To enable shadow stack support for inferior calls the shadow_stack_push
-   gdbarch hook has to be provided.
+   gdbarch hook has to be provided.  The get_shadow_stack_pointer gdbarch
+   hook has to be provided to enable displaced stepping.
 
    Push NEW_ADDR to the shadow stack and update the shadow stack pointer. */
 
@@ -1816,3 +1817,12 @@ extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch);
 typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
 extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
 extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push);
+
+/* If possible, return the shadow stack pointer.  On some architectures, the
+   shadow stack pointer is available even if the feature is disabled.  To
+   return the feature's enablement state configure SHADOW_STACK_ENABLED.
+   Set it to true in case the shadow stack is enabled. */
+
+typedef std::optional<CORE_ADDR> (gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
+extern std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
+extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer);
diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
index abc79588473..73459064170 100644
--- a/gdb/gdbarch_components.py
+++ b/gdb/gdbarch_components.py
@@ -2855,7 +2855,8 @@ Some targets support special hardware-assisted control-flow protection
 technologies.  For example, the Intel Control-Flow Enforcement Technology
 (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
 To enable shadow stack support for inferior calls the shadow_stack_push
-gdbarch hook has to be provided.
+gdbarch hook has to be provided.  The get_shadow_stack_pointer gdbarch
+hook has to be provided to enable displaced stepping.
 
 Push NEW_ADDR to the shadow stack and update the shadow stack pointer.
 """,
@@ -2864,3 +2865,17 @@ Push NEW_ADDR to the shadow stack and update the shadow stack pointer.
     params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
     predicate=True,
 )
+
+Method(
+    comment="""
+If possible, return the shadow stack pointer.  On some architectures, the
+shadow stack pointer is available even if the feature is disabled.  To
+return the feature's enablement state configure SHADOW_STACK_ENABLED.
+Set it to true in case the shadow stack is enabled.
+""",
+    type="std::optional<CORE_ADDR>",
+    name="get_shadow_stack_pointer",
+    params=[("regcache *", "regcache"), ("bool &", "shadow_stack_enabled")],
+    predefault="default_get_shadow_stack_pointer",
+    invalid=False,
+)
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* [PATCH v5 12/12] gdb: Enable displaced stepping with shadow stack on amd64 linux.
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (10 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer Christina Schimpe
@ 2025-06-28  8:28 ` Christina Schimpe
  2025-07-30 13:59   ` Andrew Burgess
  2025-07-08 15:18 ` [PATCH v5 00/12] Add CET shadow stack support Schimpe, Christina
                   ` (2 subsequent siblings)
  14 siblings, 1 reply; 67+ messages in thread
From: Christina Schimpe @ 2025-06-28  8:28 UTC (permalink / raw)
  To: gdb-patches; +Cc: thiago.bauermann, luis.machado

Currently, if displaced stepping is active and the single stepped instruction
is a call instruction, the return address atop the stack is the address
following the copied instruction.  However, to allow normal program execution
it has to be the address following the original instruction.  Due to that
reason, the return address is corrected in amd64_displaced_step_fixup and
i386_displaced_step_fixup.

For programs that are shadow-stack enabled we see a control-protection
exception, as the address on the shadow stack does not match the address
atop the stack.

Fix this by correcting the shadow stack top address as well.

Reviewed-By: Luis Machado <luis.machado@arm.com>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/NEWS                                      |  3 +
 gdb/amd64-linux-tdep.c                        | 16 +++-
 gdb/amd64-tdep.c                              | 15 +++
 gdb/doc/gdb.texinfo                           | 11 ++-
 gdb/i386-tdep.c                               | 15 +++
 .../gdb.arch/amd64-shadow-stack-disp-step.exp | 92 +++++++++++++++++++
 6 files changed, 149 insertions(+), 3 deletions(-)
 create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp

diff --git a/gdb/NEWS b/gdb/NEWS
index ba555f0dea1..60510fefea4 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,9 @@
 
 *** Changes since GDB 16
 
+* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
+  size (X32) Shadow Stacks are now supported.
+
 * Support for the shadow stack pointer register on x86-64 or x86-64 with
   32-bit pointer size (X32) GNU/Linux.
 
diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 899fe2df02c..782b66f1467 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1936,8 +1936,10 @@ amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch)
    possible.  */
 
 static std::optional<CORE_ADDR>
-amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache)
+amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
+				      bool &shadow_stack_enabled)
 {
+  shadow_stack_enabled = false;
   const i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
 
   if (tdep == nullptr || tdep->ssp_regnum < 0)
@@ -1955,6 +1957,9 @@ amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache)
   if (ssp == 0x0)
     return {};
 
+  /* In case there is a shadow stack pointer available which is non-null,
+     the shadow stack feature is enabled.  */
+  shadow_stack_enabled = true;
   return ssp;
 }
 
@@ -1965,8 +1970,13 @@ static void
 amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
 			       regcache *regcache)
 {
+  bool shadow_stack_enabled;
   std::optional<CORE_ADDR> ssp
-    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache);
+    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache,
+					    shadow_stack_enabled);
+
+  /* For amd64/Linux, if SSP has a value that means shadow stack is
+     enabled.  */
   if (!ssp.has_value ())
     return;
 
@@ -2122,6 +2132,8 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
     (gdbarch, amd64_linux_remove_non_address_bits_watchpoint);
 
   set_gdbarch_shadow_stack_push (gdbarch, amd64_linux_shadow_stack_push);
+  set_gdbarch_get_shadow_stack_pointer (gdbarch,
+					amd64_linux_get_shadow_stack_pointer);
   dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);
 }
 
diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
index 450dbc38047..8afb3a7abba 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -1917,6 +1917,21 @@ amd64_displaced_step_fixup (struct gdbarch *gdbarch,
       displaced_debug_printf ("relocated return addr at %s to %s",
 			      paddress (gdbarch, rsp),
 			      paddress (gdbarch, retaddr));
+
+      /* If shadow stack is enabled, we need to correct the return address
+	 on the shadow stack too.  */
+      bool shadow_stack_enabled;
+      std::optional<CORE_ADDR> ssp
+	= gdbarch_get_shadow_stack_pointer (gdbarch, regs,
+					    shadow_stack_enabled);
+      if (ssp.has_value () && shadow_stack_enabled)
+	{
+	  write_memory_unsigned_integer (*ssp, retaddr_len, byte_order,
+					 retaddr);
+	  displaced_debug_printf ("relocated shadow stack return addr at %s "
+				  "to %s", paddress (gdbarch, *ssp),
+				  paddress (gdbarch, retaddr));
+	}
     }
 }
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b5120b78426..488816d5ca2 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -27059,12 +27059,20 @@ the program stream must be an @code{ENDBR} instruction, otherwise the
 processor signals a control protection exception.
 @end itemize
 
-Impact on Call/Print:
+Impact on GDB commands:
+@itemize @bullet
+@item Call/Print:
 Inferior calls in @value{GDBN} reset the current PC to the beginning of the
 function that is called.  No call instruction is executed, but the @code{RET}
 instruction actually is.  To avoid a control protection exception due to the
 missing return address on the shadow stack, @value{GDBN} pushes the new return
 address to the shadow stack and updates the shadow stack pointer.
+@item Step:
+With displaced stepping, @value{GDBN} may run an out of line copy of a call
+instruction.  In this case, the wrong return address is pushed to the shadow
+stack.  @value{GDBN} corrects this value to avoid a control protection
+exception.  For more details on displaced stepping, see @ref{displaced-stepping}.
+@end itemize
 
 @node Alpha
 @subsection Alpha
@@ -41741,6 +41749,7 @@ GLOBAL              Disassembler_2	(Matches current architecture)
 @cindex out-of-line single-stepping
 @item set displaced-stepping
 @itemx show displaced-stepping
+@anchor{displaced-stepping}
 Control whether or not @value{GDBN} will do @dfn{displaced stepping}
 if the target supports it.  Displaced stepping is a way to single-step
 over breakpoints without removing them from the inferior, by executing
diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
index 8eb5b4fac86..3b05ace2142 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -899,6 +899,21 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch,
       displaced_debug_printf ("relocated return addr at %s to %s",
 			      paddress (gdbarch, esp),
 			      paddress (gdbarch, retaddr));
+
+      /* If shadow stack is enabled, we need to correct the return address
+	 on the shadow stack too.  */
+      bool shadow_stack_enabled;
+      std::optional<CORE_ADDR> ssp
+	= gdbarch_get_shadow_stack_pointer (gdbarch, regs,
+					    shadow_stack_enabled);
+      if (ssp.has_value () && shadow_stack_enabled)
+	{
+	  write_memory_unsigned_integer (*ssp, retaddr_len, byte_order,
+					 retaddr);
+	  displaced_debug_printf ("relocated shadow stack return addr at %s "
+				  "to %s", paddress (gdbarch, *ssp),
+				  paddress (gdbarch, retaddr));
+	}
     }
 }
 
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 00000000000..47bb4df8cfe
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
@@ -0,0 +1,92 @@
+# 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 -1
+    }
+
+    # Enable displaced stepping.
+    gdb_test_no_output "set displaced-stepping on"
+    gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*"
+
+    if { ![runto_main] } {
+	return -1
+    }
+
+    # Get the address of the call1 instruction.
+    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 -1
+    }
+
+    # Get the address of the call2 instruction.
+    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 -1
+    }
+
+    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"
+
+    # Depending on instruction generation we might end up in the call
+    # instruction after "runto_main".  Only resume until call1 instruction
+    # in case the first instruction we're stopped at is not yet the call1
+    # instruction.
+    set stop_addr [get_valueof "/x" "\$pc" "" "value of pc after runto_main"]
+    if {[eval expr "$stop_addr < $call1_addr"]} {
+	gdb_test "continue" \
+	    "Breakpoint $decimal, $call1_addr in main ().*" \
+	    "continue until call1 instruction"
+    }
+    gdb_assert {$call1_addr == [get_valueof "/x" "\$pc" ""]}
+
+    # 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"
+}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (11 preceding siblings ...)
  2025-06-28  8:28 ` [PATCH v5 12/12] gdb: Enable displaced stepping with shadow stack on amd64 linux Christina Schimpe
@ 2025-07-08 15:18 ` Schimpe, Christina
  2025-08-14  7:52   ` Schimpe, Christina
  2025-07-11 10:36 ` Luis Machado
  2025-08-20  9:16 ` Schimpe, Christina
  14 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-08 15:18 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches
  Cc: thiago.bauermann, luis.machado, Tom Tromey

I actually missed to mention Tom's feedback - my apologies for that.

Tom's review included:
- Approval for patch #4 " gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch." 
- Improvements for comments in patch #11 "gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer. ".

Kind Regards,
Christina

> -----Original Message-----
> From: Christina Schimpe <christina.schimpe@intel.com>
> Sent: Saturday, June 28, 2025 10:28 AM
> To: gdb-patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: [PATCH v5 00/12] Add CET shadow stack support
> 
> Hi all,
> 
> this is my v5 of the series to add amd64 shadow stack support to GDB on
> linux.
> It addresses the feedback of Luis.
> 
> v4 can be found here:
> https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html
> 
> Changes since v4:
> - Improve some comments.
> - Change the test in "gdb: amd64 linux coredump support with shadow
>   stack." to also test core file generated by the linux kernel.  This
>   requires changes for the core_find procedure to save program output,
>   that have been implemented by Thiago already, so we include this part
>   of the patch in this series: "gdb, testsuite: Extend core_find procedure
>   to save program output.".  The test is now very similar to the test
>   implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
>   providing the input here!
> 
> I am looking forward to your feedback!
> 
> Regards,
> 
> Christina
> 
> Christina Schimpe (12):
>   gdb, testsuite: Extend core_find procedure to save program output.
>   gdbserver: Add optional runtime register set type.
>   gdbserver: Add assert in x86_linux_read_description.
>   gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
>   gdb, gdbserver: Use xstate_bv for target description creation on x86.
>   gdb, gdbserver: Add support of Intel shadow stack pointer register.
>   gdb: amd64 linux coredump support with shadow stack.
>   gdb: Handle shadow stack pointer register unwinding for amd64 linux.
>   gdb, gdbarch: Enable inferior calls for shadow stack support.
>   gdb: Implement amd64 linux shadow stack support for inferior calls.
>   gdb, gdbarch: Introduce gdbarch method to get the shadow stack
>     pointer.
>   gdb: Enable displaced stepping with shadow stack on amd64 linux.
> 
>  gdb/NEWS                                      |   6 +
>  gdb/amd64-linux-nat.c                         |  17 ++
>  gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
>  gdb/amd64-tdep.c                              |  35 ++-
>  gdb/amd64-tdep.h                              |   9 +-
>  gdb/arch-utils.c                              |  10 +
>  gdb/arch-utils.h                              |   5 +
>  gdb/arch/amd64-linux-tdesc.c                  |  33 +--
>  gdb/arch/amd64-linux-tdesc.h                  |   7 +-
>  gdb/arch/amd64.c                              |  25 +-
>  gdb/arch/amd64.h                              |  10 +-
>  gdb/arch/i386-linux-tdesc.c                   |  29 +--
>  gdb/arch/i386-linux-tdesc.h                   |   5 +-
>  gdb/arch/i386.c                               |  19 +-
>  gdb/arch/i386.h                               |   8 +-
>  gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
>  gdb/arch/x86-linux-tdesc-features.h           |  25 +-
>  gdb/doc/gdb.texinfo                           |  42 ++++
>  gdb/features/Makefile                         |   2 +
>  gdb/features/i386/32bit-ssp.c                 |  14 ++
>  gdb/features/i386/32bit-ssp.xml               |  11 +
>  gdb/features/i386/64bit-ssp.c                 |  14 ++
>  gdb/features/i386/64bit-ssp.xml               |  11 +
>  gdb/gdbarch-gen.c                             |  54 +++++
>  gdb/gdbarch-gen.h                             |  24 ++
>  gdb/gdbarch_components.py                     |  31 +++
>  gdb/i386-tdep.c                               |  51 +++-
>  gdb/i386-tdep.h                               |  11 +-
>  gdb/infcall.c                                 |  14 +-
>  gdb/linux-tdep.c                              |  47 ++++
>  gdb/linux-tdep.h                              |   7 +
>  gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
>  gdb/nat/x86-linux-tdesc.c                     |  20 +-
>  gdb/nat/x86-linux-tdesc.h                     |   7 +-
>  gdb/nat/x86-linux.c                           |  57 +++++
>  gdb/nat/x86-linux.h                           |   4 +
>  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
>  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
>  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
> .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
>  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
>  gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
>  .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
>  gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
>  gdb/x86-linux-nat.c                           |  50 +++-
>  gdb/x86-linux-nat.h                           |  11 +
>  gdb/x86-tdep.c                                |  21 ++
>  gdb/x86-tdep.h                                |   9 +
>  gdbserver/i387-fp.cc                          |  40 ++--
>  gdbserver/linux-amd64-ipa.cc                  |  10 +-
>  gdbserver/linux-i386-ipa.cc                   |   6 +-
>  gdbserver/linux-low.cc                        |  50 ++--
>  gdbserver/linux-low.h                         |   7 +-
>  gdbserver/linux-x86-low.cc                    |  44 +++-
>  gdbsupport/x86-xstate.h                       |   7 +-
>  55 files changed, 1687 insertions(+), 217 deletions(-)  create mode 100644
> gdb/features/i386/32bit-ssp.c  create mode 100644 gdb/features/i386/32bit-
> ssp.xml  create mode 100644 gdb/features/i386/64bit-ssp.c  create mode
> 100644 gdb/features/i386/64bit-ssp.xml  create mode 100644
> gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-
> corefile.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-
> step.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> 
> --
> 2.43.0

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 00/12] Add CET shadow stack support
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (12 preceding siblings ...)
  2025-07-08 15:18 ` [PATCH v5 00/12] Add CET shadow stack support Schimpe, Christina
@ 2025-07-11 10:36 ` Luis Machado
  2025-07-11 13:54   ` Schimpe, Christina
  2025-08-20  9:16 ` Schimpe, Christina
  14 siblings, 1 reply; 67+ messages in thread
From: Luis Machado @ 2025-07-11 10:36 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann

Hi Christina,

I went through v5 and I have no further comments.

I did try running the testsuite on my local x86-64 machine and noticed a couple additional failures for native gdb testing.

FAIL: gdb.base/gcore.exp: corefile restored general registers
FAIL: gdb.base/gcore.exp: corefile restored all registers

Have you seen those? The problem seems to be that the pl3_ssp register is showing up in a live session, but not in the core file.

pl3_ssp        <unavailable>

On 6/28/25 09:27, Christina Schimpe wrote:
> Hi all,
> 
> this is my v5 of the series to add amd64 shadow stack support to GDB on linux.
> It addresses the feedback of Luis.
> 
> v4 can be found here:
> https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html
> 
> Changes since v4:
> - Improve some comments.
> - Change the test in "gdb: amd64 linux coredump support with shadow
>   stack." to also test core file generated by the linux kernel.  This
>   requires changes for the core_find procedure to save program output,
>   that have been implemented by Thiago already, so we include this part
>   of the patch in this series: "gdb, testsuite: Extend core_find procedure
>   to save program output.".  The test is now very similar to the test
>   implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
>   providing the input here!
> 
> I am looking forward to your feedback!
> 
> Regards,
> 
> Christina
> 
> Christina Schimpe (12):
>   gdb, testsuite: Extend core_find procedure to save program output.
>   gdbserver: Add optional runtime register set type.
>   gdbserver: Add assert in x86_linux_read_description.
>   gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
>   gdb, gdbserver: Use xstate_bv for target description creation on x86.
>   gdb, gdbserver: Add support of Intel shadow stack pointer register.
>   gdb: amd64 linux coredump support with shadow stack.
>   gdb: Handle shadow stack pointer register unwinding for amd64 linux.
>   gdb, gdbarch: Enable inferior calls for shadow stack support.
>   gdb: Implement amd64 linux shadow stack support for inferior calls.
>   gdb, gdbarch: Introduce gdbarch method to get the shadow stack
>     pointer.
>   gdb: Enable displaced stepping with shadow stack on amd64 linux.
> 
>  gdb/NEWS                                      |   6 +
>  gdb/amd64-linux-nat.c                         |  17 ++
>  gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
>  gdb/amd64-tdep.c                              |  35 ++-
>  gdb/amd64-tdep.h                              |   9 +-
>  gdb/arch-utils.c                              |  10 +
>  gdb/arch-utils.h                              |   5 +
>  gdb/arch/amd64-linux-tdesc.c                  |  33 +--
>  gdb/arch/amd64-linux-tdesc.h                  |   7 +-
>  gdb/arch/amd64.c                              |  25 +-
>  gdb/arch/amd64.h                              |  10 +-
>  gdb/arch/i386-linux-tdesc.c                   |  29 +--
>  gdb/arch/i386-linux-tdesc.h                   |   5 +-
>  gdb/arch/i386.c                               |  19 +-
>  gdb/arch/i386.h                               |   8 +-
>  gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
>  gdb/arch/x86-linux-tdesc-features.h           |  25 +-
>  gdb/doc/gdb.texinfo                           |  42 ++++
>  gdb/features/Makefile                         |   2 +
>  gdb/features/i386/32bit-ssp.c                 |  14 ++
>  gdb/features/i386/32bit-ssp.xml               |  11 +
>  gdb/features/i386/64bit-ssp.c                 |  14 ++
>  gdb/features/i386/64bit-ssp.xml               |  11 +
>  gdb/gdbarch-gen.c                             |  54 +++++
>  gdb/gdbarch-gen.h                             |  24 ++
>  gdb/gdbarch_components.py                     |  31 +++
>  gdb/i386-tdep.c                               |  51 +++-
>  gdb/i386-tdep.h                               |  11 +-
>  gdb/infcall.c                                 |  14 +-
>  gdb/linux-tdep.c                              |  47 ++++
>  gdb/linux-tdep.h                              |   7 +
>  gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
>  gdb/nat/x86-linux-tdesc.c                     |  20 +-
>  gdb/nat/x86-linux-tdesc.h                     |   7 +-
>  gdb/nat/x86-linux.c                           |  57 +++++
>  gdb/nat/x86-linux.h                           |   4 +
>  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
>  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
>  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
>  .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
>  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
>  gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
>  .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
>  gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
>  gdb/x86-linux-nat.c                           |  50 +++-
>  gdb/x86-linux-nat.h                           |  11 +
>  gdb/x86-tdep.c                                |  21 ++
>  gdb/x86-tdep.h                                |   9 +
>  gdbserver/i387-fp.cc                          |  40 ++--
>  gdbserver/linux-amd64-ipa.cc                  |  10 +-
>  gdbserver/linux-i386-ipa.cc                   |   6 +-
>  gdbserver/linux-low.cc                        |  50 ++--
>  gdbserver/linux-low.h                         |   7 +-
>  gdbserver/linux-x86-low.cc                    |  44 +++-
>  gdbsupport/x86-xstate.h                       |   7 +-
>  55 files changed, 1687 insertions(+), 217 deletions(-)
>  create mode 100644 gdb/features/i386/32bit-ssp.c
>  create mode 100644 gdb/features/i386/32bit-ssp.xml
>  create mode 100644 gdb/features/i386/64bit-ssp.c
>  create mode 100644 gdb/features/i386/64bit-ssp.xml
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> 


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-11 10:36 ` Luis Machado
@ 2025-07-11 13:54   ` Schimpe, Christina
  2025-07-11 15:54     ` Luis Machado
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-11 13:54 UTC (permalink / raw)
  To: Luis Machado, gdb-patches; +Cc: thiago.bauermann

Hi Luis,

Thanks a lot for your testing efforts and spotting this.
That's interesting, I just double checked and am not able to reproduce. Would you mind sharing a couple of details, e.g the kernel version? I assume that your cpu has CET shadow stack support since the register appears as "unavailable".

For me the shadow stack register is available in the corefile (ubuntu with linux kernel 6.13). 

(gdb) PASS: gdb.base/gcore.exp: where in corefile
info registers^M
rax            0x0                 0^M
[...]
k7             0x0                 0^M
pl3_ssp        <unavailable>^M
fs_base        0x7ffff7fae740      140737353803584^M
gs_base        0x0                 0^M
(gdb) PASS: gdb.base/gcore.exp: corefile restored general registers

Christina

> -----Original Message-----
> From: Luis Machado <luis.machado@arm.com>
> Sent: Friday, July 11, 2025 12:37 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org
> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
> 
> Hi Christina,
> 
> I went through v5 and I have no further comments.
> 
> I did try running the testsuite on my local x86-64 machine and noticed a
> couple additional failures for native gdb testing.
> 
> FAIL: gdb.base/gcore.exp: corefile restored general registers
> FAIL: gdb.base/gcore.exp: corefile restored all registers
> 
> Have you seen those? The problem seems to be that the pl3_ssp register is
> showing up in a live session, but not in the core file.
> 
> pl3_ssp        <unavailable>
> 
> On 6/28/25 09:27, Christina Schimpe wrote:
> > Hi all,
> >
> > this is my v5 of the series to add amd64 shadow stack support to GDB on
> linux.
> > It addresses the feedback of Luis.
> >
> > v4 can be found here:
> > https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html
> >
> > Changes since v4:
> > - Improve some comments.
> > - Change the test in "gdb: amd64 linux coredump support with shadow
> >   stack." to also test core file generated by the linux kernel.  This
> >   requires changes for the core_find procedure to save program output,
> >   that have been implemented by Thiago already, so we include this part
> >   of the patch in this series: "gdb, testsuite: Extend core_find procedure
> >   to save program output.".  The test is now very similar to the test
> >   implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
> >   providing the input here!
> >
> > I am looking forward to your feedback!
> >
> > Regards,
> >
> > Christina
> >
> > Christina Schimpe (12):
> >   gdb, testsuite: Extend core_find procedure to save program output.
> >   gdbserver: Add optional runtime register set type.
> >   gdbserver: Add assert in x86_linux_read_description.
> >   gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
> >   gdb, gdbserver: Use xstate_bv for target description creation on x86.
> >   gdb, gdbserver: Add support of Intel shadow stack pointer register.
> >   gdb: amd64 linux coredump support with shadow stack.
> >   gdb: Handle shadow stack pointer register unwinding for amd64 linux.
> >   gdb, gdbarch: Enable inferior calls for shadow stack support.
> >   gdb: Implement amd64 linux shadow stack support for inferior calls.
> >   gdb, gdbarch: Introduce gdbarch method to get the shadow stack
> >     pointer.
> >   gdb: Enable displaced stepping with shadow stack on amd64 linux.
> >
> >  gdb/NEWS                                      |   6 +
> >  gdb/amd64-linux-nat.c                         |  17 ++
> >  gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
> >  gdb/amd64-tdep.c                              |  35 ++-
> >  gdb/amd64-tdep.h                              |   9 +-
> >  gdb/arch-utils.c                              |  10 +
> >  gdb/arch-utils.h                              |   5 +
> >  gdb/arch/amd64-linux-tdesc.c                  |  33 +--
> >  gdb/arch/amd64-linux-tdesc.h                  |   7 +-
> >  gdb/arch/amd64.c                              |  25 +-
> >  gdb/arch/amd64.h                              |  10 +-
> >  gdb/arch/i386-linux-tdesc.c                   |  29 +--
> >  gdb/arch/i386-linux-tdesc.h                   |   5 +-
> >  gdb/arch/i386.c                               |  19 +-
> >  gdb/arch/i386.h                               |   8 +-
> >  gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
> >  gdb/arch/x86-linux-tdesc-features.h           |  25 +-
> >  gdb/doc/gdb.texinfo                           |  42 ++++
> >  gdb/features/Makefile                         |   2 +
> >  gdb/features/i386/32bit-ssp.c                 |  14 ++
> >  gdb/features/i386/32bit-ssp.xml               |  11 +
> >  gdb/features/i386/64bit-ssp.c                 |  14 ++
> >  gdb/features/i386/64bit-ssp.xml               |  11 +
> >  gdb/gdbarch-gen.c                             |  54 +++++
> >  gdb/gdbarch-gen.h                             |  24 ++
> >  gdb/gdbarch_components.py                     |  31 +++
> >  gdb/i386-tdep.c                               |  51 +++-
> >  gdb/i386-tdep.h                               |  11 +-
> >  gdb/infcall.c                                 |  14 +-
> >  gdb/linux-tdep.c                              |  47 ++++
> >  gdb/linux-tdep.h                              |   7 +
> >  gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
> >  gdb/nat/x86-linux-tdesc.c                     |  20 +-
> >  gdb/nat/x86-linux-tdesc.h                     |   7 +-
> >  gdb/nat/x86-linux.c                           |  57 +++++
> >  gdb/nat/x86-linux.h                           |   4 +
> >  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
> >  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
> >  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
> > .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
> >  gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
> >  .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
> >  gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
> >  gdb/x86-linux-nat.c                           |  50 +++-
> >  gdb/x86-linux-nat.h                           |  11 +
> >  gdb/x86-tdep.c                                |  21 ++
> >  gdb/x86-tdep.h                                |   9 +
> >  gdbserver/i387-fp.cc                          |  40 ++--
> >  gdbserver/linux-amd64-ipa.cc                  |  10 +-
> >  gdbserver/linux-i386-ipa.cc                   |   6 +-
> >  gdbserver/linux-low.cc                        |  50 ++--
> >  gdbserver/linux-low.h                         |   7 +-
> >  gdbserver/linux-x86-low.cc                    |  44 +++-
> >  gdbsupport/x86-xstate.h                       |   7 +-
> >  55 files changed, 1687 insertions(+), 217 deletions(-)  create mode
> > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
> > gdb/features/i386/32bit-ssp.xml  create mode 100644
> > gdb/features/i386/64bit-ssp.c  create mode 100644
> > gdb/features/i386/64bit-ssp.xml  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> >  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> >  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
> >  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> >

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-11 13:54   ` Schimpe, Christina
@ 2025-07-11 15:54     ` Luis Machado
  2025-07-13 14:01       ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Luis Machado @ 2025-07-11 15:54 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches; +Cc: thiago.bauermann

Hi Christina,

On 7/11/25 14:54, Schimpe, Christina wrote:
> Hi Luis,
> 
> Thanks a lot for your testing efforts and spotting this.
> That's interesting, I just double checked and am not able to reproduce. Would you mind sharing a couple of details, e.g the kernel version? I assume that your cpu has CET shadow stack support since the register appears as "unavailable".

I'm running Ubuntu 24.04 on 6.8.0-63-generic.

CPU is Intel(R) Core(TM) Ultra 7 165U, with these features:

    Flags:                fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge m
                          ca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 s
                          s ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc 
                          art arch_perfmon pebs bts rep_good nopl xtopology nons
                          top_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq 
                          dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma c
                          x16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt t
                          sc_deadline_timer aes xsave avx f16c rdrand lahf_lm ab
                          m 3dnowprefetch cpuid_fault epb intel_ppin ssbd ibrs i
                          bpb stibp ibrs_enhanced tpr_shadow flexpriority ept vp
                          id ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms
                           invpcid rdseed adx smap clflushopt clwb intel_pt sha_
                          ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect us
                          er_shstk avx_vnni dtherm ida arat pln pts hwp hwp_noti
                          fy hwp_act_window hwp_epp hwp_pkg_req hfi vnmi umip pk
                          u ospke waitpkg gfni vaes vpclmulqdq tme rdpid bus_loc
                          k_detect movdiri movdir64b fsrm md_clear serialize pco
                          nfig arch_lbr ibt flush_l1d arch_capabilities


So slightly older than yours. Anything else I can share that would help?

> 
> For me the shadow stack register is available in the corefile (ubuntu with linux kernel 6.13). 
> 
> (gdb) PASS: gdb.base/gcore.exp: where in corefile
> info registers^M
> rax            0x0                 0^M
> [...]
> k7             0x0                 0^M
> pl3_ssp        <unavailable>^M
> fs_base        0x7ffff7fae740      140737353803584^M
> gs_base        0x0                 0^M
> (gdb) PASS: gdb.base/gcore.exp: corefile restored general registers
> 
> Christina
> 
>> -----Original Message-----
>> From: Luis Machado <luis.machado@arm.com>
>> Sent: Friday, July 11, 2025 12:37 PM
>> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
>> patches@sourceware.org
>> Cc: thiago.bauermann@linaro.org
>> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
>>
>> Hi Christina,
>>
>> I went through v5 and I have no further comments.
>>
>> I did try running the testsuite on my local x86-64 machine and noticed a
>> couple additional failures for native gdb testing.
>>
>> FAIL: gdb.base/gcore.exp: corefile restored general registers
>> FAIL: gdb.base/gcore.exp: corefile restored all registers
>>
>> Have you seen those? The problem seems to be that the pl3_ssp register is
>> showing up in a live session, but not in the core file.
>>
>> pl3_ssp        <unavailable>
>>
>> On 6/28/25 09:27, Christina Schimpe wrote:
>>> Hi all,
>>>
>>> this is my v5 of the series to add amd64 shadow stack support to GDB on
>> linux.
>>> It addresses the feedback of Luis.
>>>
>>> v4 can be found here:
>>> https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html
>>>
>>> Changes since v4:
>>> - Improve some comments.
>>> - Change the test in "gdb: amd64 linux coredump support with shadow
>>>   stack." to also test core file generated by the linux kernel.  This
>>>   requires changes for the core_find procedure to save program output,
>>>   that have been implemented by Thiago already, so we include this part
>>>   of the patch in this series: "gdb, testsuite: Extend core_find procedure
>>>   to save program output.".  The test is now very similar to the test
>>>   implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
>>>   providing the input here!
>>>
>>> I am looking forward to your feedback!
>>>
>>> Regards,
>>>
>>> Christina
>>>
>>> Christina Schimpe (12):
>>>   gdb, testsuite: Extend core_find procedure to save program output.
>>>   gdbserver: Add optional runtime register set type.
>>>   gdbserver: Add assert in x86_linux_read_description.
>>>   gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
>>>   gdb, gdbserver: Use xstate_bv for target description creation on x86.
>>>   gdb, gdbserver: Add support of Intel shadow stack pointer register.
>>>   gdb: amd64 linux coredump support with shadow stack.
>>>   gdb: Handle shadow stack pointer register unwinding for amd64 linux.
>>>   gdb, gdbarch: Enable inferior calls for shadow stack support.
>>>   gdb: Implement amd64 linux shadow stack support for inferior calls.
>>>   gdb, gdbarch: Introduce gdbarch method to get the shadow stack
>>>     pointer.
>>>   gdb: Enable displaced stepping with shadow stack on amd64 linux.
>>>
>>>  gdb/NEWS                                      |   6 +
>>>  gdb/amd64-linux-nat.c                         |  17 ++
>>>  gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
>>>  gdb/amd64-tdep.c                              |  35 ++-
>>>  gdb/amd64-tdep.h                              |   9 +-
>>>  gdb/arch-utils.c                              |  10 +
>>>  gdb/arch-utils.h                              |   5 +
>>>  gdb/arch/amd64-linux-tdesc.c                  |  33 +--
>>>  gdb/arch/amd64-linux-tdesc.h                  |   7 +-
>>>  gdb/arch/amd64.c                              |  25 +-
>>>  gdb/arch/amd64.h                              |  10 +-
>>>  gdb/arch/i386-linux-tdesc.c                   |  29 +--
>>>  gdb/arch/i386-linux-tdesc.h                   |   5 +-
>>>  gdb/arch/i386.c                               |  19 +-
>>>  gdb/arch/i386.h                               |   8 +-
>>>  gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
>>>  gdb/arch/x86-linux-tdesc-features.h           |  25 +-
>>>  gdb/doc/gdb.texinfo                           |  42 ++++
>>>  gdb/features/Makefile                         |   2 +
>>>  gdb/features/i386/32bit-ssp.c                 |  14 ++
>>>  gdb/features/i386/32bit-ssp.xml               |  11 +
>>>  gdb/features/i386/64bit-ssp.c                 |  14 ++
>>>  gdb/features/i386/64bit-ssp.xml               |  11 +
>>>  gdb/gdbarch-gen.c                             |  54 +++++
>>>  gdb/gdbarch-gen.h                             |  24 ++
>>>  gdb/gdbarch_components.py                     |  31 +++
>>>  gdb/i386-tdep.c                               |  51 +++-
>>>  gdb/i386-tdep.h                               |  11 +-
>>>  gdb/infcall.c                                 |  14 +-
>>>  gdb/linux-tdep.c                              |  47 ++++
>>>  gdb/linux-tdep.h                              |   7 +
>>>  gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
>>>  gdb/nat/x86-linux-tdesc.c                     |  20 +-
>>>  gdb/nat/x86-linux-tdesc.h                     |   7 +-
>>>  gdb/nat/x86-linux.c                           |  57 +++++
>>>  gdb/nat/x86-linux.h                           |   4 +
>>>  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
>>>  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
>>>  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
>>> .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
>>>  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
>>>  gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
>>>  .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
>>>  gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
>>>  gdb/x86-linux-nat.c                           |  50 +++-
>>>  gdb/x86-linux-nat.h                           |  11 +
>>>  gdb/x86-tdep.c                                |  21 ++
>>>  gdb/x86-tdep.h                                |   9 +
>>>  gdbserver/i387-fp.cc                          |  40 ++--
>>>  gdbserver/linux-amd64-ipa.cc                  |  10 +-
>>>  gdbserver/linux-i386-ipa.cc                   |   6 +-
>>>  gdbserver/linux-low.cc                        |  50 ++--
>>>  gdbserver/linux-low.h                         |   7 +-
>>>  gdbserver/linux-x86-low.cc                    |  44 +++-
>>>  gdbsupport/x86-xstate.h                       |   7 +-
>>>  55 files changed, 1687 insertions(+), 217 deletions(-)  create mode
>>> 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
>>> gdb/features/i386/32bit-ssp.xml  create mode 100644
>>> gdb/features/i386/64bit-ssp.c  create mode 100644
>>> gdb/features/i386/64bit-ssp.xml  create mode 100644
>>> gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
>>>  create mode 100644
>>> gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
>>>  create mode 100644
>>> gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
>>>  create mode 100644
>>> gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
>>>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
>>>  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
>>>
> 
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de
> Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-11 15:54     ` Luis Machado
@ 2025-07-13 14:01       ` Schimpe, Christina
  2025-07-13 19:05         ` Luis Machado
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-13 14:01 UTC (permalink / raw)
  To: Luis Machado, gdb-patches; +Cc: thiago.bauermann

Hi Luis, 

Thanks for sharing that info.
I tried to reproduces with the same ubuntu & kernel versions and a very similar system but am not successful so far...
Just to be sure, I assume you test with gcc compiler (which version?) and compiled GDB with latest upstream master + my cet shadow stack patches?

Do you see this for target_board=native-gdbserver and target_board=unix?

Thanks,
Christina

> -----Original Message-----
> From: Luis Machado <luis.machado@arm.com>
> Sent: Friday, July 11, 2025 5:55 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org
> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
> 
> Hi Christina,
> 
> On 7/11/25 14:54, Schimpe, Christina wrote:
> > Hi Luis,
> >
> > Thanks a lot for your testing efforts and spotting this.
> > That's interesting, I just double checked and am not able to reproduce.
> Would you mind sharing a couple of details, e.g the kernel version? I assume
> that your cpu has CET shadow stack support since the register appears as
> "unavailable".
> 
> I'm running Ubuntu 24.04 on 6.8.0-63-generic.
> 
> CPU is Intel(R) Core(TM) Ultra 7 165U, with these features:
> 
>     Flags:                fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge m
>                           ca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 s
>                           s ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc
>                           art arch_perfmon pebs bts rep_good nopl xtopology nons
>                           top_tsc cpuid aperfmperf tsc_known_freq pni pclmulqdq
>                           dtes64 monitor ds_cpl vmx smx est tm2 ssse3 sdbg fma c
>                           x16 xtpr pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt t
>                           sc_deadline_timer aes xsave avx f16c rdrand lahf_lm ab
>                           m 3dnowprefetch cpuid_fault epb intel_ppin ssbd ibrs i
>                           bpb stibp ibrs_enhanced tpr_shadow flexpriority ept vp
>                           id ept_ad fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms
>                            invpcid rdseed adx smap clflushopt clwb intel_pt sha_
>                           ni xsaveopt xsavec xgetbv1 xsaves split_lock_detect us
>                           er_shstk avx_vnni dtherm ida arat pln pts hwp hwp_noti
>                           fy hwp_act_window hwp_epp hwp_pkg_req hfi vnmi umip pk
>                           u ospke waitpkg gfni vaes vpclmulqdq tme rdpid bus_loc
>                           k_detect movdiri movdir64b fsrm md_clear serialize pco
>                           nfig arch_lbr ibt flush_l1d arch_capabilities
> 
> 
> So slightly older than yours. Anything else I can share that would help?
> 
> >
> > For me the shadow stack register is available in the corefile (ubuntu with
> linux kernel 6.13).
> >
> > (gdb) PASS: gdb.base/gcore.exp: where in corefile info registers^M
> > rax            0x0                 0^M
> > [...]
> > k7             0x0                 0^M
> > pl3_ssp        <unavailable>^M
> > fs_base        0x7ffff7fae740      140737353803584^M
> > gs_base        0x0                 0^M
> > (gdb) PASS: gdb.base/gcore.exp: corefile restored general registers
> >
> > Christina
> >
> >> -----Original Message-----
> >> From: Luis Machado <luis.machado@arm.com>
> >> Sent: Friday, July 11, 2025 12:37 PM
> >> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> >> patches@sourceware.org
> >> Cc: thiago.bauermann@linaro.org
> >> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
> >>
> >> Hi Christina,
> >>
> >> I went through v5 and I have no further comments.
> >>
> >> I did try running the testsuite on my local x86-64 machine and
> >> noticed a couple additional failures for native gdb testing.
> >>
> >> FAIL: gdb.base/gcore.exp: corefile restored general registers
> >> FAIL: gdb.base/gcore.exp: corefile restored all registers
> >>
> >> Have you seen those? The problem seems to be that the pl3_ssp
> >> register is showing up in a live session, but not in the core file.
> >>
> >> pl3_ssp        <unavailable>
> >>
> >> On 6/28/25 09:27, Christina Schimpe wrote:
> >>> Hi all,
> >>>
> >>> this is my v5 of the series to add amd64 shadow stack support to GDB
> >>> on
> >> linux.
> >>> It addresses the feedback of Luis.
> >>>
> >>> v4 can be found here:
> >>> https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html
> >>>
> >>> Changes since v4:
> >>> - Improve some comments.
> >>> - Change the test in "gdb: amd64 linux coredump support with shadow
> >>>   stack." to also test core file generated by the linux kernel.  This
> >>>   requires changes for the core_find procedure to save program output,
> >>>   that have been implemented by Thiago already, so we include this part
> >>>   of the patch in this series: "gdb, testsuite: Extend core_find procedure
> >>>   to save program output.".  The test is now very similar to the test
> >>>   implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
> >>>   providing the input here!
> >>>
> >>> I am looking forward to your feedback!
> >>>
> >>> Regards,
> >>>
> >>> Christina
> >>>
> >>> Christina Schimpe (12):
> >>>   gdb, testsuite: Extend core_find procedure to save program output.
> >>>   gdbserver: Add optional runtime register set type.
> >>>   gdbserver: Add assert in x86_linux_read_description.
> >>>   gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
> >>>   gdb, gdbserver: Use xstate_bv for target description creation on x86.
> >>>   gdb, gdbserver: Add support of Intel shadow stack pointer register.
> >>>   gdb: amd64 linux coredump support with shadow stack.
> >>>   gdb: Handle shadow stack pointer register unwinding for amd64 linux.
> >>>   gdb, gdbarch: Enable inferior calls for shadow stack support.
> >>>   gdb: Implement amd64 linux shadow stack support for inferior calls.
> >>>   gdb, gdbarch: Introduce gdbarch method to get the shadow stack
> >>>     pointer.
> >>>   gdb: Enable displaced stepping with shadow stack on amd64 linux.
> >>>
> >>>  gdb/NEWS                                      |   6 +
> >>>  gdb/amd64-linux-nat.c                         |  17 ++
> >>>  gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
> >>>  gdb/amd64-tdep.c                              |  35 ++-
> >>>  gdb/amd64-tdep.h                              |   9 +-
> >>>  gdb/arch-utils.c                              |  10 +
> >>>  gdb/arch-utils.h                              |   5 +
> >>>  gdb/arch/amd64-linux-tdesc.c                  |  33 +--
> >>>  gdb/arch/amd64-linux-tdesc.h                  |   7 +-
> >>>  gdb/arch/amd64.c                              |  25 +-
> >>>  gdb/arch/amd64.h                              |  10 +-
> >>>  gdb/arch/i386-linux-tdesc.c                   |  29 +--
> >>>  gdb/arch/i386-linux-tdesc.h                   |   5 +-
> >>>  gdb/arch/i386.c                               |  19 +-
> >>>  gdb/arch/i386.h                               |   8 +-
> >>>  gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
> >>>  gdb/arch/x86-linux-tdesc-features.h           |  25 +-
> >>>  gdb/doc/gdb.texinfo                           |  42 ++++
> >>>  gdb/features/Makefile                         |   2 +
> >>>  gdb/features/i386/32bit-ssp.c                 |  14 ++
> >>>  gdb/features/i386/32bit-ssp.xml               |  11 +
> >>>  gdb/features/i386/64bit-ssp.c                 |  14 ++
> >>>  gdb/features/i386/64bit-ssp.xml               |  11 +
> >>>  gdb/gdbarch-gen.c                             |  54 +++++
> >>>  gdb/gdbarch-gen.h                             |  24 ++
> >>>  gdb/gdbarch_components.py                     |  31 +++
> >>>  gdb/i386-tdep.c                               |  51 +++-
> >>>  gdb/i386-tdep.h                               |  11 +-
> >>>  gdb/infcall.c                                 |  14 +-
> >>>  gdb/linux-tdep.c                              |  47 ++++
> >>>  gdb/linux-tdep.h                              |   7 +
> >>>  gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
> >>>  gdb/nat/x86-linux-tdesc.c                     |  20 +-
> >>>  gdb/nat/x86-linux-tdesc.h                     |   7 +-
> >>>  gdb/nat/x86-linux.c                           |  57 +++++
> >>>  gdb/nat/x86-linux.h                           |   4 +
> >>>  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
> >>>  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
> >>>  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
> >>> .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
> >>>  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
> >>>  gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
> >>>  .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
> >>>  gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
> >>>  gdb/x86-linux-nat.c                           |  50 +++-
> >>>  gdb/x86-linux-nat.h                           |  11 +
> >>>  gdb/x86-tdep.c                                |  21 ++
> >>>  gdb/x86-tdep.h                                |   9 +
> >>>  gdbserver/i387-fp.cc                          |  40 ++--
> >>>  gdbserver/linux-amd64-ipa.cc                  |  10 +-
> >>>  gdbserver/linux-i386-ipa.cc                   |   6 +-
> >>>  gdbserver/linux-low.cc                        |  50 ++--
> >>>  gdbserver/linux-low.h                         |   7 +-
> >>>  gdbserver/linux-x86-low.cc                    |  44 +++-
> >>>  gdbsupport/x86-xstate.h                       |   7 +-
> >>>  55 files changed, 1687 insertions(+), 217 deletions(-)  create mode
> >>> 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
> >>> gdb/features/i386/32bit-ssp.xml  create mode 100644
> >>> gdb/features/i386/64bit-ssp.c  create mode 100644
> >>> gdb/features/i386/64bit-ssp.xml  create mode 100644
> >>> gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> >>>  create mode 100644
> >>> gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> >>>  create mode 100644
> >>> gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
> >>>  create mode 100644
> >>> gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
> >>>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
> >>>  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> >>>
> >
> > Intel Deutschland GmbH
> > Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> > Tel: +49 89 99 8853-0, www.intel.de
> > Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon
> > Silva Chairperson of the Supervisory Board: Nicole Lau Registered
> > Office: Munich Commercial Register: Amtsgericht Muenchen HRB 186928

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-13 14:01       ` Schimpe, Christina
@ 2025-07-13 19:05         ` Luis Machado
  2025-07-13 19:57           ` Schimpe, Christina
  2025-07-14  7:13           ` Luis Machado
  0 siblings, 2 replies; 67+ messages in thread
From: Luis Machado @ 2025-07-13 19:05 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches; +Cc: thiago.bauermann

Hi,

On 7/13/25 15:01, Schimpe, Christina wrote:
> Hi Luis, 
> 
> Thanks for sharing that info.
> I tried to reproduces with the same ubuntu & kernel versions and a very similar system but am not successful so far...
> Just to be sure, I assume you test with gcc compiler (which version?) and compiled GDB with latest upstream master + my cet shadow stack patches?

Interesting. GCC says: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0

I applied the CET shadow stack patches from v5. And yes, gdb is the latest tip-of-tree.
> 
> Do you see this for target_board=native-gdbserver and target_board=unix?

No. native-gdbserver, native-extended-gdbserver and unix work just fine.

Let me have another look to see what might be going on.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-13 19:05         ` Luis Machado
@ 2025-07-13 19:57           ` Schimpe, Christina
  2025-07-14  7:13           ` Luis Machado
  1 sibling, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-13 19:57 UTC (permalink / raw)
  To: Luis Machado, gdb-patches; +Cc: thiago.bauermann

Hi Luis, 

> > Interesting. GCC says: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0

I tested with exactly the same gcc version.

> No. native-gdbserver, native-extended-gdbserver and unix work just fine.

Maybe there is a misunderstanding. 
Which configuration is failing then? I thought with "native gdb testing"  you mean unix boardfile.

Christina


> -----Original Message-----
> From: Luis Machado <luis.machado@arm.com>
> Sent: Sunday, July 13, 2025 9:05 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org
> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
> 
> Hi,
> 
> On 7/13/25 15:01, Schimpe, Christina wrote:
> > Hi Luis,
> >
> > Thanks for sharing that info.
> > I tried to reproduces with the same ubuntu & kernel versions and a very
> similar system but am not successful so far...
> > Just to be sure, I assume you test with gcc compiler (which version?) and
> compiled GDB with latest upstream master + my cet shadow stack patches?
> 
> Interesting. GCC says: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
> 
> I applied the CET shadow stack patches from v5. And yes, gdb is the latest tip-
> of-tree.
> >
> > Do you see this for target_board=native-gdbserver and target_board=unix?
> 
> No. native-gdbserver, native-extended-gdbserver and unix work just fine.
> 
> Let me have another look to see what might be going on.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-13 19:05         ` Luis Machado
  2025-07-13 19:57           ` Schimpe, Christina
@ 2025-07-14  7:13           ` Luis Machado
  2025-07-17 12:01             ` Schimpe, Christina
  1 sibling, 1 reply; 67+ messages in thread
From: Luis Machado @ 2025-07-14  7:13 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches; +Cc: thiago.bauermann

On 7/13/25 20:05, Luis Machado wrote:
> Hi,
> 
> On 7/13/25 15:01, Schimpe, Christina wrote:
>> Hi Luis, 
>>
>> Thanks for sharing that info.
>> I tried to reproduces with the same ubuntu & kernel versions and a very similar system but am not successful so far...
>> Just to be sure, I assume you test with gcc compiler (which version?) and compiled GDB with latest upstream master + my cet shadow stack patches?
> 
> Interesting. GCC says: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
> 
> I applied the CET shadow stack patches from v5. And yes, gdb is the latest tip-of-tree.
>>
>> Do you see this for target_board=native-gdbserver and target_board=unix?
> 
> No. native-gdbserver, native-extended-gdbserver and unix work just fine.
> 
> Let me have another look to see what might be going on.

Heh, found it. I had a freshly-installed system and was missing libexpat-dev, so no XML was being generated.

With libexpat-dev installed, everything looks fine. So this is good. Sorry for the noise.

I spotted two things while building gdb with the patch series applied

There is a uninitialized usage warning

gdb/nat/x86-linux.c:151:12: warning: ‘ecx’ may be used uninitialized [-Wmaybe-uninitialized]
  151 |   if ((ecx & bit_SHSTK) == 0)
      |       ~~~~~^~~~~~~~~~~~
../../../repos/binutils-gdb/gdb/nat/x86-linux.c:147:26: note: ‘ecx’ was declared here
  147 |   unsigned int eax, ebx, ecx, edx;

Also, git am seems to complain about some whitespace issues:

Applying: gdb, gdbserver: Add support of Intel shadow stack pointer register.
.git/rebase-apply/patch:596: indent with spaces.
            # Having unavailable registers leads to a fall back to the standard
.git/rebase-apply/patch:597: indent with spaces.
            # unwinders.  Don't add unavailable registers to avoid this.
.git/rebase-apply/patch:598: indent with spaces.
            if (str (val) == "<unavailable>"):
.git/rebase-apply/patch:599: indent with spaces.
                continue
.git/rebase-apply/patch:587: new blank line at EOF.
+
warning: 5 lines add whitespace errors.


Could you please address those locally?

Otherwise...

Approved-By: Luis Machado <luis.machado@arm.com>

I'd give this another week in case someone has any more comments, and then push it.

Thanks for the series.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save program output.
  2025-06-28  8:27 ` [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save program output Christina Schimpe
@ 2025-07-14 12:21   ` Andrew Burgess
  2025-07-17 13:37     ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-07-14 12:21 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>
> The change comes from ARM's GCS series:
>
> [PATCH v3 5/9] GDB, gdbserver: aarch64-linux: Initial Guarded Control Stack support.
>
> We need it for testing coredmp files, too.  So include it in this
> patch series.

Typo: "corecmp"

With that fixed:

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew

>
> Abridged-by: Christina Schimpe <christina.schimpe@intel.com>
> ---
> This is the patch mentioned above:
>
> https://sourceware.org/pipermail/gdb-patches/2025-June/218892.html
>
> Minus everything except for the change in gdb.exp's corefind procedure.
> ---
>  gdb/testsuite/lib/gdb.exp | 10 ++++++++--
>  1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 3f1cd55d727..bb17c4e91a5 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -9352,7 +9352,13 @@ proc remove_core {pid {test ""}} {
>      }
>  }
>  
> -proc core_find {binfile {deletefiles {}} {arg ""}} {
> +# Runs ${binfile} expecting it to crash and generate a core file.
> +# If DELETEFILES is provided, remove these files after running the program.
> +# If ARG is provided, pass it as a command line argument to the program.
> +# If OUTPUT_FILE is provided, save the program output to it.
> +# Returns the name of the core dump, or empty string if not found.
> +
> +proc core_find {binfile {deletefiles {}} {arg ""} {output_file "/dev/null"}} {
>      global objdir subdir
>  
>      set destcore "$binfile.core"
> @@ -9374,7 +9380,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}"
>      set binfile_basename [file tail $binfile]
>      foreach i [list \
> -- 
> 2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.
  2025-06-28  8:28 ` [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86 Christina Schimpe
@ 2025-07-14 13:52   ` Andrew Burgess
  2025-07-15 10:28     ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-07-14 13:52 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> The XSAVE features set is organized in state components, which are a set
> of or parts of registers.  So-called XSAVE-supported features are

Maybe here you mean: "..., which are a set of, or parts of, registers."
If not, then this sentence doesn't make sense to me.

> organized using state-component bitmaps, each bit corresponding to a
> single state component.
>
> The SDM uses the term xstate_bv for a state-component bitmap, which is

Would it be possible to define what SDM it please.

> defined as XCR0 | IA32_XSS.  The control register XCR0 only contains a
> state-component bitmap that specifies user state components, while IA32_XSS
> contains a state-component bitmap that specifies supervisor state components.
>
> Until now, XCR0 is used as input for target description creation in GDB.
> However, a following patch will add userspace support for the CET shadow
> stack feature by Intel.  The CET state is configured in IA32_XSS and consists
> of 2 state components:
> - State component 11 used for the 2 MSRs controlling user-mode
>   functionality for CET (CET_U state)
> - State component 12 used for the 3 MSRs containing shadow-stack pointers
>   for privilege levels 0-2 (CET_S state).
>
> Reading the CET shadow stack pointer register on linux requires a separate
> ptrace call using NT_X86_SHSTK.  To pass the CET shadow stack enablement
> state we would like to pass the xstate_bv value instead of xcr0 for target
> description creation.  To prepare for that, we rename the xcr0 mask
> values for target description creation to xstate_bv.  However, this
> patch doesn't add any functional changes in GDB.
>
> Future states specified in IA32_XSS such as CET will create a combined
> xstate_bv_mask including xcr0 register value and its corresponding bit in
> the state component bitmap.  This combined mask will then be used to create
> the target descriptions.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Reviewed-By: Luis Machado <luis.machado@arm.com>
> ---
>  gdb/amd64-tdep.c                    | 14 +++----
>  gdb/amd64-tdep.h                    |  8 +++-
>  gdb/arch/amd64-linux-tdesc.c        | 33 ++++++++--------
>  gdb/arch/amd64-linux-tdesc.h        |  7 ++--
>  gdb/arch/amd64.c                    | 15 +++-----
>  gdb/arch/amd64.h                    | 10 ++++-
>  gdb/arch/i386-linux-tdesc.c         | 29 +++++++-------
>  gdb/arch/i386-linux-tdesc.h         |  5 ++-
>  gdb/arch/i386.c                     | 15 ++++----
>  gdb/arch/i386.h                     |  8 +++-
>  gdb/arch/x86-linux-tdesc-features.c | 59 +++++++++++++++--------------
>  gdb/arch/x86-linux-tdesc-features.h | 25 +++++++-----
>  gdb/i386-tdep.c                     | 14 +++----
>  gdb/i386-tdep.h                     |  7 +++-
>  gdb/nat/x86-linux-tdesc.c           | 18 +++++----
>  gdb/nat/x86-linux-tdesc.h           |  7 ++--
>  gdb/x86-linux-nat.c                 | 11 ++++--
>  gdbserver/i387-fp.cc                | 40 +++++++++----------
>  gdbserver/linux-amd64-ipa.cc        | 10 +++--
>  gdbserver/linux-i386-ipa.cc         |  6 +--
>  gdbserver/linux-x86-low.cc          |  9 ++---
>  gdbsupport/x86-xstate.h             |  4 +-
>  22 files changed, 198 insertions(+), 156 deletions(-)
>
> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index 82dd1e07cf3..04539dd288a 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -3551,23 +3551,23 @@ amd64_x32_none_init_abi (gdbarch_info info, gdbarch *arch)
>  		      amd64_target_description (X86_XSTATE_SSE_MASK, true));
>  }
>  
> -/* Return the target description for a specified XSAVE feature mask.  */
> +/* See amd64-tdep.h.  */
>  
>  const struct target_desc *
> -amd64_target_description (uint64_t xcr0, bool segments)
> +amd64_target_description (uint64_t xstate_bv_mask, bool segments)

I'd like to ask about the use of 'mask' in this variable name.  We used
to pass in an xcr0 value, which was then combined with various masks to
extract the state bits we were interested in.

You've renamed the new variable as a mask, but continue to combine it
with the same masks (e.g. X86_XSTATE_AVX), which makes me suspect the
variable is not a mask at all.

Now, I can see where the confusion might have come from.  Almost every
call to amd64_target_description passes in a *_MASK constant.  But
that's OK, given these are bit sets, then the MASK constants actually
define valid values.

I am aware that this sounds like a trivial complaint over a variable
name, but I think calling this a mask is pretty confusing (at least to
me), so unless I'm not understanding this, then I think it is worth
renaming this.

The use of `mask` for the value is present throughout this patch, not
just this one function.

Thanks,
Andrew

>  {
>    static target_desc *amd64_tdescs \
>      [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
>    target_desc **tdesc;
>  
> -  tdesc = &amd64_tdescs[(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
> -    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
> -    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0]
> +  tdesc = &amd64_tdescs[(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
> +    [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
> +    [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
>      [segments ? 1 : 0];
>  
>    if (*tdesc == NULL)
> -    *tdesc = amd64_create_target_description (xcr0, false, false,
> -					      segments);
> +    *tdesc = amd64_create_target_description (xstate_bv_mask, false,
> +					      false, segments);
>  
>    return *tdesc;
>  }
> diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
> index cbf8a97fa3c..82f781bf404 100644
> --- a/gdb/amd64-tdep.h
> +++ b/gdb/amd64-tdep.h
> @@ -108,8 +108,12 @@ extern void amd64_init_abi (struct gdbarch_info info,
>  extern void amd64_x32_init_abi (struct gdbarch_info info,
>  				struct gdbarch *gdbarch,
>  				const target_desc *default_tdesc);
> -extern const struct target_desc *amd64_target_description (uint64_t xcr0,
> -							   bool segments);
> +
> +/* Return the target description for the specified xsave features as
> +   defined in XSTATE_BV_MASK and SEGMENTS.  */
> +
> +extern const struct target_desc *amd64_target_description
> +  (uint64_t xstate_bv_mask, bool segments);
>  
>  /* Fill register REGNUM in REGCACHE with the appropriate
>     floating-point or SSE register value from *FXSAVE.  If REGNUM is
> diff --git a/gdb/arch/amd64-linux-tdesc.c b/gdb/arch/amd64-linux-tdesc.c
> index 91de75873a1..771badc0ca7 100644
> --- a/gdb/arch/amd64-linux-tdesc.c
> +++ b/gdb/arch/amd64-linux-tdesc.c
> @@ -26,41 +26,42 @@
>  /* See arch/amd64-linux-tdesc.h.  */
>  
>  const struct target_desc *
> -amd64_linux_read_description (uint64_t xcr0, bool is_x32)
> +amd64_linux_read_description (uint64_t xstate_bv_mask, bool is_x32)
>  {
>    /* The type used for the amd64 and x32 target description caches.  */
>    using tdesc_cache_type = std::unordered_map<uint64_t, const target_desc_up>;
>  
>    /* Caches for the previously seen amd64 and x32 target descriptions,
> -     indexed by the xcr0 value that created the target description.  These
> -     need to be static within this function to ensure they are initialised
> -     before first use.  */
> +     indexed by the xstate_bv_mask value that created the target
> +     description.  These need to be static within this function to ensure
> +     they are initialised before first use.  */
>    static tdesc_cache_type amd64_tdesc_cache, x32_tdesc_cache;
>  
>    tdesc_cache_type &tdesc_cache = is_x32 ? x32_tdesc_cache : amd64_tdesc_cache;
>  
> -  /* Only some bits are checked when creating a tdesc, but the XCR0 value
> -     contains other feature bits that are not relevant for tdesc creation.
> -     When indexing into the TDESC_CACHE we need to use a consistent xcr0
> -     value otherwise we might fail to find an existing tdesc which has the
> -     same set of relevant bits set.  */
> -  xcr0 &= is_x32
> -    ? x86_linux_x32_xcr0_feature_mask ()
> -    : x86_linux_amd64_xcr0_feature_mask ();
> +  /* Only some bits are checked when creating a tdesc, but the
> +     xstate_bv_mask value contains other feature bits that are not
> +     relevant for tdesc creation.
> +     When indexing into the TDESC_CACHE we need to use a consistent
> +     xstate_bv_mask value otherwise we might fail to find an existing
> +     tdesc which has the same set of relevant bits set.  */
> +  xstate_bv_mask &= is_x32
> +    ? x86_linux_x32_xstate_bv_feature_mask ()
> +    : x86_linux_amd64_xstate_bv_feature_mask ();
>  
> -  const auto it = tdesc_cache.find (xcr0);
> +  const auto it = tdesc_cache.find (xstate_bv_mask);
>    if (it != tdesc_cache.end ())
>      return it->second.get ();
>  
>    /* Create the previously unseen target description.  */
> -  target_desc_up tdesc (amd64_create_target_description (xcr0, is_x32,
> -							 true, true));
> +  target_desc_up tdesc (amd64_create_target_description (xstate_bv_mask,
> +							 is_x32, true, true));
>    x86_linux_post_init_tdesc (tdesc.get (), true);
>  
>    /* Add to the cache, and return a pointer borrowed from the
>       target_desc_up.  This is safe as the cache (and the pointers contained
>       within it) are not deleted until GDB exits.  */
>    target_desc *ptr = tdesc.get ();
> -  tdesc_cache.emplace (xcr0, std::move (tdesc));
> +  tdesc_cache.emplace (xstate_bv_mask, std::move (tdesc));
>    return ptr;
>  }
> diff --git a/gdb/arch/amd64-linux-tdesc.h b/gdb/arch/amd64-linux-tdesc.h
> index 8806a132562..0d0e1bbff72 100644
> --- a/gdb/arch/amd64-linux-tdesc.h
> +++ b/gdb/arch/amd64-linux-tdesc.h
> @@ -22,9 +22,10 @@
>  
>  struct target_desc;
>  
> -/* Return the AMD64 target descriptions corresponding to XCR0 and IS_X32.  */
> +/* Return the AMD64 target descriptions corresponding to XSTATE_BV_MASK
> +   and IS_X32.  */
>  
> -extern const target_desc *amd64_linux_read_description (uint64_t xcr0,
> -							bool is_x32);
> +extern const target_desc *amd64_linux_read_description
> +  (uint64_t xstate_bv_mask, bool is_x32);
>  
>  #endif /* GDB_ARCH_AMD64_LINUX_TDESC_H */
> diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
> index 252650b6390..3181f827356 100644
> --- a/gdb/arch/amd64.c
> +++ b/gdb/arch/amd64.c
> @@ -30,14 +30,11 @@
>  
>  #include "../features/i386/x32-core.c"
>  
> -/* Create amd64 target descriptions according to XCR0.  If IS_X32 is
> -   true, create the x32 ones.  If IS_LINUX is true, create target
> -   descriptions for Linux.  If SEGMENTS is true, then include
> -   the "org.gnu.gdb.i386.segments" feature registers.  */
> +/* See arch/amd64.h.  */
>  
>  target_desc *
> -amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
> -				 bool segments)
> +amd64_create_target_description (uint64_t xstate_bv_mask, bool is_x32,
> +				 bool is_linux, bool segments)
>  {
>    target_desc_up tdesc = allocate_target_description ();
>  
> @@ -62,13 +59,13 @@ amd64_create_target_description (uint64_t xcr0, bool is_x32, bool is_linux,
>    if (segments)
>      regnum = create_feature_i386_64bit_segments (tdesc.get (), regnum);
>  
> -  if (xcr0 & X86_XSTATE_AVX)
> +  if (xstate_bv_mask & X86_XSTATE_AVX)
>      regnum = create_feature_i386_64bit_avx (tdesc.get (), regnum);
>  
> -  if (xcr0 & X86_XSTATE_AVX512)
> +  if (xstate_bv_mask & X86_XSTATE_AVX512)
>      regnum = create_feature_i386_64bit_avx512 (tdesc.get (), regnum);
>  
> -  if (xcr0 & X86_XSTATE_PKRU)
> +  if (xstate_bv_mask & X86_XSTATE_PKRU)
>      regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
>  
>    return tdesc.release ();
> diff --git a/gdb/arch/amd64.h b/gdb/arch/amd64.h
> index 695660c55d6..54080ee10a9 100644
> --- a/gdb/arch/amd64.h
> +++ b/gdb/arch/amd64.h
> @@ -21,7 +21,13 @@
>  #include "gdbsupport/tdesc.h"
>  #include <stdint.h>
>  
> -target_desc *amd64_create_target_description (uint64_t xcr0, bool is_x32,
> -					      bool is_linux, bool segments);
> +/* Create amd64 target descriptions according to XSTATE_BV_MASK.  If
> +   IS_X32 is true, create the x32 ones.  If IS_LINUX is true, create
> +   target descriptions for Linux.  If SEGMENTS is true, then include
> +   the "org.gnu.gdb.i386.segments" feature registers.  */
> +
> +target_desc *amd64_create_target_description (uint64_t xstate_bv_mask,
> +					      bool is_x32, bool is_linux,
> +					      bool segments);
>  
>  #endif /* GDB_ARCH_AMD64_H */
> diff --git a/gdb/arch/i386-linux-tdesc.c b/gdb/arch/i386-linux-tdesc.c
> index 51513176464..57cdbbfa7e5 100644
> --- a/gdb/arch/i386-linux-tdesc.c
> +++ b/gdb/arch/i386-linux-tdesc.c
> @@ -25,32 +25,35 @@
>  /* See arch/i386-linux-tdesc.h.  */
>  
>  const target_desc *
> -i386_linux_read_description (uint64_t xcr0)
> +i386_linux_read_description (uint64_t xstate_bv_mask)
>  {
> -  /* Cache of previously seen i386 target descriptions, indexed by the xcr0
> -     value that created the target description.  This needs to be static
> -     within this function to ensure it is initialised before first use.  */
> +  /* Cache of previously seen i386 target descriptions, indexed by the
> +     xstate_bv_mask value that created the target description.  This
> +     needs to be static within this function to ensure it is initialised
> +     before first use.  */
>    static std::unordered_map<uint64_t, const target_desc_up> i386_tdesc_cache;
>  
> -  /* Only some bits are checked when creating a tdesc, but the XCR0 value
> -     contains other feature bits that are not relevant for tdesc creation.
> -     When indexing into the I386_TDESC_CACHE we need to use a consistent
> -     xcr0 value otherwise we might fail to find an existing tdesc which has
> -     the same set of relevant bits set.  */
> -  xcr0 &= x86_linux_i386_xcr0_feature_mask ();
> +  /* Only some bits are checked when creating a tdesc, but the
> +     XSTATE_BV_MASK value contains other feature bits that are not
> +     relevant for tdesc creation.  When indexing into the I386_TDESC_CACHE
> +     we need to use a consistent XSTATE_BV_MASK value otherwise we might
> +     fail to find an existing tdesc which has the same set of relevant
> +     bits set.  */
> +  xstate_bv_mask &= x86_linux_i386_xstate_bv_feature_mask ();
>  
> -  const auto it = i386_tdesc_cache.find (xcr0);
> +  const auto it = i386_tdesc_cache.find (xstate_bv_mask);
>    if (it != i386_tdesc_cache.end ())
>      return it->second.get ();
>  
>    /* Create the previously unseen target description.  */
> -  target_desc_up tdesc (i386_create_target_description (xcr0, true, false));
> +  target_desc_up tdesc
> +    (i386_create_target_description (xstate_bv_mask, true, false));
>    x86_linux_post_init_tdesc (tdesc.get (), false);
>  
>    /* Add to the cache, and return a pointer borrowed from the
>       target_desc_up.  This is safe as the cache (and the pointers contained
>       within it) are not deleted until GDB exits.  */
>    target_desc *ptr = tdesc.get ();
> -  i386_tdesc_cache.emplace (xcr0, std::move (tdesc));
> +  i386_tdesc_cache.emplace (xstate_bv_mask, std::move (tdesc));
>    return ptr;
>  }
> diff --git a/gdb/arch/i386-linux-tdesc.h b/gdb/arch/i386-linux-tdesc.h
> index 2c3c1740d81..3392b3fa06e 100644
> --- a/gdb/arch/i386-linux-tdesc.h
> +++ b/gdb/arch/i386-linux-tdesc.h
> @@ -22,8 +22,9 @@
>  
>  struct target_desc;
>  
> -/* Return the i386 target description corresponding to XCR0.  */
> +/* Return the i386 target description corresponding to XSTATE_BV_MASK.  */
>  
> -extern const struct target_desc *i386_linux_read_description (uint64_t xcr0);
> +extern const struct target_desc *i386_linux_read_description
> +  (uint64_t xstate_bv_mask);
>  
>  #endif /* GDB_ARCH_I386_LINUX_TDESC_H */
> diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c
> index 835df53c75d..e04d8c5dd94 100644
> --- a/gdb/arch/i386.c
> +++ b/gdb/arch/i386.c
> @@ -29,10 +29,11 @@
>  #include "../features/i386/32bit-segments.c"
>  #include "../features/i386/pkeys.c"
>  
> -/* Create i386 target descriptions according to XCR0.  */
> +/* See arch/i386.h.  */
>  
>  target_desc *
> -i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
> +i386_create_target_description (uint64_t xstate_bv_mask, bool is_linux,
> +				bool segments)
>  {
>    target_desc_up tdesc = allocate_target_description ();
>  
> @@ -44,10 +45,10 @@ i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
>  
>    long regnum = 0;
>  
> -  if (xcr0 & X86_XSTATE_X87)
> +  if (xstate_bv_mask & X86_XSTATE_X87)
>      regnum = create_feature_i386_32bit_core (tdesc.get (), regnum);
>  
> -  if (xcr0 & X86_XSTATE_SSE)
> +  if (xstate_bv_mask & X86_XSTATE_SSE)
>      regnum = create_feature_i386_32bit_sse (tdesc.get (), regnum);
>  
>    if (is_linux)
> @@ -56,13 +57,13 @@ i386_create_target_description (uint64_t xcr0, bool is_linux, bool segments)
>    if (segments)
>      regnum = create_feature_i386_32bit_segments (tdesc.get (), regnum);
>  
> -  if (xcr0 & X86_XSTATE_AVX)
> +  if (xstate_bv_mask & X86_XSTATE_AVX)
>      regnum = create_feature_i386_32bit_avx (tdesc.get (), regnum);
>  
> -  if (xcr0 & X86_XSTATE_AVX512)
> +  if (xstate_bv_mask & X86_XSTATE_AVX512)
>      regnum = create_feature_i386_32bit_avx512 (tdesc.get (), regnum);
>  
> -  if (xcr0 & X86_XSTATE_PKRU)
> +  if (xstate_bv_mask & X86_XSTATE_PKRU)
>      regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
>  
>    return tdesc.release ();
> diff --git a/gdb/arch/i386.h b/gdb/arch/i386.h
> index 1fc41014624..a23deebc366 100644
> --- a/gdb/arch/i386.h
> +++ b/gdb/arch/i386.h
> @@ -21,7 +21,13 @@
>  #include "gdbsupport/tdesc.h"
>  #include <stdint.h>
>  
> -target_desc *i386_create_target_description (uint64_t xcr0, bool is_linux,
> +/* Create i386 target descriptions according to XSTATE_BV_MASK.  If
> +   IS_LINUX is true, create target descriptions for Linux.  If SEGMENTS
> +   is true, then include the "org.gnu.gdb.i386.segments" feature
> +   registers.  */
> +
> +target_desc *i386_create_target_description (uint64_t xstate_bv_mask,
> +					     bool is_linux,
>  					     bool segments);
>  
>  #endif /* GDB_ARCH_I386_H */
> diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
> index f65920cb95b..68f37fccaef 100644
> --- a/gdb/arch/x86-linux-tdesc-features.c
> +++ b/gdb/arch/x86-linux-tdesc-features.c
> @@ -28,18 +28,21 @@
>  
>     We want to cache target descriptions, and this is currently done in
>     three separate caches, one each for i386, amd64, and x32.  Additionally,
> -   the caching we're discussing here is Linux only, and for Linux, the only
> -   thing that has an impact on target description creation is the xcr0
> -   value.
> -
> -   In order to ensure the cache functions correctly we need to filter out
> -   only those xcr0 feature bits that are relevant, we can then cache target
> -   descriptions based on the relevant feature bits.  Two xcr0 values might
> -   be different, but have the same relevant feature bits.  In this case we
> -   would expect the two xcr0 values to map to the same cache entry.  */
> +   the caching we're discussing here is Linux only.  Currently for Linux,
> +   the only thing that has an impact on target description creation are
> +   the supported features in xsave which are modelled by a xstate_bv_mask
> +   value, which has the same format than the state component bitmap.
> +
> +   In order to ensure the cache functions correctly we need to filter only
> +   those xstate_bv_mask feature bits that are relevant, we can then cache
> +   target descriptions based on the relevant feature bits.  Two
> +   xstate_bv_mask values might be different, but have the same relevant
> +   feature bits.  In this case we would expect the two xstate_bv_mask
> +   values to map to the same cache entry.  */
>  
>  struct x86_xstate_feature {
> -  /* The xstate feature mask.  This is a mask against an xcr0 value.  */
> +  /* The xstate feature mask.  This is a mask against the state component
> +     bitmap.  */
>    uint64_t feature;
>  
>    /* Is this feature checked when creating an i386 target description.  */
> @@ -56,9 +59,9 @@ struct x86_xstate_feature {
>     checked when building a target description for i386, amd64, or x32.
>  
>     If in the future, due to simplifications or refactoring, this table ever
> -   ends up with 'true' for every xcr0 feature on every target type, then this
> -   is an indication that this table should probably be removed, and that the
> -   rest of the code in this file can be simplified.  */
> +   ends up with 'true' for every xsave feature on every target type, then
> +   this is an indication that this table should probably be removed, and
> +   that the rest of the code in this file can be simplified.  */
>  
>  static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
>    /* Feature,           i386,	amd64,	x32.  */
> @@ -73,7 +76,7 @@ static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
>     that are checked for when building an i386 target description.  */
>  
>  static constexpr uint64_t
> -x86_linux_i386_xcr0_feature_mask_1 ()
> +x86_linux_i386_xstate_bv_feature_mask_1 ()
>  {
>    uint64_t mask = 0;
>  
> @@ -88,7 +91,7 @@ x86_linux_i386_xcr0_feature_mask_1 ()
>     that are checked for when building an amd64 target description.  */
>  
>  static constexpr uint64_t
> -x86_linux_amd64_xcr0_feature_mask_1 ()
> +x86_linux_amd64_xstate_bv_feature_mask_1 ()
>  {
>    uint64_t mask = 0;
>  
> @@ -103,7 +106,7 @@ x86_linux_amd64_xcr0_feature_mask_1 ()
>     that are checked for when building an x32 target description.  */
>  
>  static constexpr uint64_t
> -x86_linux_x32_xcr0_feature_mask_1 ()
> +x86_linux_x32_xstate_bv_feature_mask_1 ()
>  {
>    uint64_t mask = 0;
>  
> @@ -117,25 +120,25 @@ x86_linux_x32_xcr0_feature_mask_1 ()
>  /* See arch/x86-linux-tdesc-features.h.  */
>  
>  uint64_t
> -x86_linux_i386_xcr0_feature_mask ()
> +x86_linux_i386_xstate_bv_feature_mask ()
>  {
> -  return x86_linux_i386_xcr0_feature_mask_1 ();
> +  return x86_linux_i386_xstate_bv_feature_mask_1 ();
>  }
>  
>  /* See arch/x86-linux-tdesc-features.h.  */
>  
>  uint64_t
> -x86_linux_amd64_xcr0_feature_mask ()
> +x86_linux_amd64_xstate_bv_feature_mask ()
>  {
> -  return x86_linux_amd64_xcr0_feature_mask_1 ();
> +  return x86_linux_amd64_xstate_bv_feature_mask_1 ();
>  }
>  
>  /* See arch/x86-linux-tdesc-features.h.  */
>  
>  uint64_t
> -x86_linux_x32_xcr0_feature_mask ()
> +x86_linux_x32_xstate_bv_feature_mask ()
>  {
> -  return x86_linux_x32_xcr0_feature_mask_1 ();
> +  return x86_linux_x32_xstate_bv_feature_mask_1 ();
>  }
>  
>  #ifdef GDBSERVER
> @@ -143,7 +146,7 @@ x86_linux_x32_xcr0_feature_mask ()
>  /* See arch/x86-linux-tdesc-features.h.  */
>  
>  int
> -x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
> +x86_linux_xstate_bv_mask_to_tdesc_idx (uint64_t xstate_bv_mask)
>  {
>    /* The following table shows which features are checked for when creating
>       the target descriptions (see nat/x86-linux-tdesc.c), the feature order
> @@ -160,7 +163,7 @@ x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0)
>  
>    for (int i = 0; i < ARRAY_SIZE (x86_linux_all_xstate_features); ++i)
>      {
> -      if ((xcr0 & x86_linux_all_xstate_features[i].feature)
> +      if ((xstate_bv_mask & x86_linux_all_xstate_features[i].feature)
>  	  == x86_linux_all_xstate_features[i].feature)
>  	idx |= (1 << i);
>      }
> @@ -250,17 +253,17 @@ x86_linux_i386_tdesc_count ()
>  /* See arch/x86-linux-tdesc-features.h.  */
>  
>  uint64_t
> -x86_linux_tdesc_idx_to_xcr0 (int idx)
> +x86_linux_tdesc_idx_to_xstate_bv_mask (int idx)
>  {
> -  uint64_t xcr0 = 0;
> +  uint64_t xstate_bv_mask = 0;
>  
>    for (int i = 0; i < ARRAY_SIZE (x86_linux_all_xstate_features); ++i)
>      {
>        if ((idx & (1 << i)) != 0)
> -	xcr0 |= x86_linux_all_xstate_features[i].feature;
> +	xstate_bv_mask |= x86_linux_all_xstate_features[i].feature;
>      }
>  
> -  return xcr0;
> +  return xstate_bv_mask;
>  }
>  
>  #endif /* IN_PROCESS_AGENT */
> diff --git a/gdb/arch/x86-linux-tdesc-features.h b/gdb/arch/x86-linux-tdesc-features.h
> index 89fe7cecc68..d1d74e70df8 100644
> --- a/gdb/arch/x86-linux-tdesc-features.h
> +++ b/gdb/arch/x86-linux-tdesc-features.h
> @@ -27,17 +27,20 @@
>     the set of features which are checked for when creating the target
>     description for each of amd64, x32, and i386.  */
>  
> -extern uint64_t x86_linux_amd64_xcr0_feature_mask ();
> -extern uint64_t x86_linux_x32_xcr0_feature_mask ();
> -extern uint64_t x86_linux_i386_xcr0_feature_mask ();
> +extern uint64_t x86_linux_amd64_xstate_bv_feature_mask ();
> +extern uint64_t x86_linux_x32_xstate_bv_feature_mask ();
> +extern uint64_t x86_linux_i386_xstate_bv_feature_mask ();
>  
>  #ifdef GDBSERVER
>  
> -/* Convert an xcr0 value into an integer.  The integer will be passed from
> +/* Convert an XSTATE_BV_MASK value into an integer.  XSTATE_BV_MASK has
> +   the same format than the state component bitmap and does include user
> +   and supervisor state components.  The integer will be passed from
>     gdbserver to the in-process-agent where it will then be passed through
> -   x86_linux_tdesc_idx_to_xcr0 to get back the original xcr0 value.  */
> +   x86_linux_tdesc_idx_to_xstate_bv_mask to get back the original mask.  */
>  
> -extern int x86_linux_xcr0_to_tdesc_idx (uint64_t xcr0);
> +
> +extern int x86_linux_xstate_bv_mask_to_tdesc_idx (uint64_t xstate_bv_mask);
>  
>  #endif /* GDBSERVER */
>  
> @@ -51,11 +54,13 @@ extern int x86_linux_amd64_tdesc_count ();
>  extern int x86_linux_x32_tdesc_count ();
>  extern int x86_linux_i386_tdesc_count ();
>  
> -/* Convert an index number (as returned from x86_linux_xcr0_to_tdesc_idx)
> -   into an xcr0 value which can then be used to create a target
> -   description.  */
> +/* Convert an index number (as returned from
> +   x86_linux_xstate_bv_mask_to_tdesc_idx) into an xstate_bv_mask
> +   value which can then be used to create a target description.
> +   The return mask has the same format than the state component bitmap
> +   and does include user and supervisor state components.*/
>  
> -extern uint64_t x86_linux_tdesc_idx_to_xcr0 (int idx);
> +extern uint64_t x86_linux_tdesc_idx_to_xstate_bv_mask (int idx);
>  
>  #endif /* IN_PROCESS_AGENT */
>  
> diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
> index 9be4748c880..90ff0c5c706 100644
> --- a/gdb/i386-tdep.c
> +++ b/gdb/i386-tdep.c
> @@ -8949,23 +8949,23 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>  
>  \f
>  
> -/* Return the target description for a specified XSAVE feature mask.  */
> +/* See i386-tdep.h.  */
>  
>  const struct target_desc *
> -i386_target_description (uint64_t xcr0, bool segments)
> +i386_target_description (uint64_t xstate_bv_mask, bool segments)
>  {
>    static target_desc *i386_tdescs \
>      [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
>    target_desc **tdesc;
>  
> -  tdesc = &i386_tdescs[(xcr0 & X86_XSTATE_SSE) ? 1 : 0]
> -    [(xcr0 & X86_XSTATE_AVX) ? 1 : 0]
> -    [(xcr0 & X86_XSTATE_AVX512) ? 1 : 0]
> -    [(xcr0 & X86_XSTATE_PKRU) ? 1 : 0]
> +  tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
> +    [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
> +    [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
> +    [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
>      [segments ? 1 : 0];
>  
>    if (*tdesc == NULL)
> -    *tdesc = i386_create_target_description (xcr0, false, segments);
> +    *tdesc = i386_create_target_description (xstate_bv_mask, false, segments);
>  
>    return *tdesc;
>  }
> diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
> index e849b336e12..239bc8674e8 100644
> --- a/gdb/i386-tdep.h
> +++ b/gdb/i386-tdep.h
> @@ -450,8 +450,11 @@ extern int i386_svr4_reg_to_regnum (struct gdbarch *gdbarch, int reg);
>  
>  extern int i386_process_record (struct gdbarch *gdbarch,
>  				struct regcache *regcache, CORE_ADDR addr);
> -extern const struct target_desc *i386_target_description (uint64_t xcr0,
> -							  bool segments);
> +
> +/* Return the target description for the specified xsave features as
> +   defined in XSTATE_BV_MASK and SEGMENTS.  */
> +extern const struct target_desc *i386_target_description
> +  (uint64_t xstate_bv_mask, bool segments);
>  
>  /* Functions and variables exported from i386-bsd-tdep.c.  */
>  
> diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
> index 80e4337a270..e9cf2527c5f 100644
> --- a/gdb/nat/x86-linux-tdesc.c
> +++ b/gdb/nat/x86-linux-tdesc.c
> @@ -43,7 +43,7 @@
>  /* See nat/x86-linux-tdesc.h.  */
>  
>  const target_desc *
> -x86_linux_tdesc_for_tid (int tid, uint64_t *xcr0_storage,
> +x86_linux_tdesc_for_tid (int tid, uint64_t *xstate_bv_storage,
>  			 x86_xsave_layout *xsave_layout_storage)
>  {
>  #ifdef __x86_64__
> @@ -96,30 +96,32 @@ x86_linux_tdesc_for_tid (int tid, uint64_t *xcr0_storage,
>  	     these bits being set we generate a completely empty tdesc for
>  	     i386 which will be rejected by GDB.  */
>  	  have_ptrace_getregset = TRIBOOL_FALSE;
> -	  *xcr0_storage = X86_XSTATE_SSE_MASK;
> +	  *xstate_bv_storage = X86_XSTATE_SSE_MASK;
>  	}
>        else
>  	{
>  	  have_ptrace_getregset = TRIBOOL_TRUE;
>  
>  	  /* Get XCR0 from XSAVE extended state.  */
> -	  *xcr0_storage = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
> +	  uint64_t xcr0 = xstateregs[(I386_LINUX_XSAVE_XCR0_OFFSET
>  				      / sizeof (uint64_t))];
>  
>  	  *xsave_layout_storage
> -	    = x86_fetch_xsave_layout (*xcr0_storage, x86_xsave_length ());
> +	    = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
> +
> +	  *xstate_bv_storage = xcr0;
>  	}
>      }
>  
> -  /* Use cached xcr0 value.  */
> -  uint64_t xcr0_features_bits = *xcr0_storage & X86_XSTATE_ALL_MASK;
> +  /* Use cached XSTATE_BV_STORAGE value.  */
> +  uint64_t xstate_bv_features_bits = *xstate_bv_storage & X86_XSTATE_ALL_MASK;
>  
>  #ifdef __x86_64__
>    if (is_64bit)
> -    return amd64_linux_read_description (xcr0_features_bits, is_x32);
> +    return amd64_linux_read_description (xstate_bv_features_bits, is_x32);
>    else
>  #endif
> -    return i386_linux_read_description (xcr0_features_bits);
> +    return i386_linux_read_description (xstate_bv_features_bits);
>  }
>  
>  #endif /* !IN_PROCESS_AGENT */
> diff --git a/gdb/nat/x86-linux-tdesc.h b/gdb/nat/x86-linux-tdesc.h
> index 38c71f10df2..19aa84f6b3f 100644
> --- a/gdb/nat/x86-linux-tdesc.h
> +++ b/gdb/nat/x86-linux-tdesc.h
> @@ -27,9 +27,9 @@ struct x86_xsave_layout;
>  
>  /* Return the target description for Linux thread TID.
>  
> -   The storage pointed to by XCR0_STORAGE and XSAVE_LAYOUT_STORAGE must
> +   The storage pointed to by XSTATE_BV_STORAGE and XSAVE_LAYOUT_STORAGE must
>     exist until the program (GDB or gdbserver) terminates, this storage is
> -   used to cache the xcr0 and xsave layout values.  The values pointed to
> +   used to cache the xstate_bv and xsave layout values.  The values pointed to
>     by these arguments are only updated at most once, the first time this
>     function is called if the have_ptrace_getregset global is set to
>     TRIBOOL_UNKNOWN.
> @@ -45,6 +45,7 @@ struct x86_xsave_layout;
>     returned.  */
>  
>  extern const target_desc *x86_linux_tdesc_for_tid
> -  (int tid, uint64_t *xcr0_storage, x86_xsave_layout *xsave_layout_storage);
> +  (int tid, uint64_t *xstate_bv_storage,
> +   x86_xsave_layout *xsave_layout_storage);
>  
>  #endif /* GDB_NAT_X86_LINUX_TDESC_H */
> diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
> index 81db5d86a5e..1b7dd8506dd 100644
> --- a/gdb/x86-linux-nat.c
> +++ b/gdb/x86-linux-nat.c
> @@ -97,15 +97,20 @@ const struct target_desc *
>  x86_linux_nat_target::read_description ()
>  {
>    /* The x86_linux_tdesc_for_tid call only reads xcr0 the first time it is
> -     called, the xcr0 value is stored here and reused on subsequent calls.  */
> -  static uint64_t xcr0_storage;
> +     called.  The mask is stored in XSTATE_BV_STORAGE and reused on
> +     subsequent calls.  Note that GDB currently supports features for user
> +     state components only.  However, once supervisor state components are
> +     supported in GDB, the value XSTATE_BV_STORAGE will not be configured
> +     based on xcr0 only.  */
> +  static uint64_t xstate_bv_storage;
>  
>    if (inferior_ptid == null_ptid)
>      return this->beneath ()->read_description ();
>  
>    int tid = inferior_ptid.pid ();
>  
> -  return x86_linux_tdesc_for_tid (tid, &xcr0_storage, &this->m_xsave_layout);
> +  return x86_linux_tdesc_for_tid (tid, &xstate_bv_storage,
> +				  &this->m_xsave_layout);
>  }
>  \f
>  
> diff --git a/gdbserver/i387-fp.cc b/gdbserver/i387-fp.cc
> index 4be0083a6fb..90824bdaee5 100644
> --- a/gdbserver/i387-fp.cc
> +++ b/gdbserver/i387-fp.cc
> @@ -21,7 +21,7 @@
>  #include "nat/x86-xstate.h"
>  
>  /* Default to SSE.  */
> -static uint64_t x86_xcr0 = X86_XSTATE_SSE_MASK;
> +static uint64_t x86_xstate_bv = X86_XSTATE_SSE_MASK;
>  
>  static const int num_avx512_k_registers = 8;
>  static const int num_pkeys_registers = 1;
> @@ -265,7 +265,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>  
>    /* The supported bits in `xstat_bv' are 8 bytes.  Clear part in
>       vector registers if its bit in xstat_bv is zero.  */
> -  clear_bv = (~fp->xstate_bv) & x86_xcr0;
> +  clear_bv = (~fp->xstate_bv) & x86_xstate_bv;
>  
>    /* Clear part in x87 and vector registers if its bit in xstat_bv is
>       zero.  */
> @@ -315,7 +315,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>      }
>  
>    /* Check if any x87 registers are changed.  */
> -  if ((x86_xcr0 & X86_XSTATE_X87))
> +  if ((x86_xstate_bv & X86_XSTATE_X87))
>      {
>        int st0_regnum = find_regno (regcache->tdesc, "st0");
>  
> @@ -332,7 +332,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>      }
>  
>    /* Check if any SSE registers are changed.  */
> -  if ((x86_xcr0 & X86_XSTATE_SSE))
> +  if ((x86_xstate_bv & X86_XSTATE_SSE))
>      {
>        int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
>  
> @@ -349,7 +349,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>      }
>  
>    /* Check if any AVX registers are changed.  */
> -  if ((x86_xcr0 & X86_XSTATE_AVX))
> +  if ((x86_xstate_bv & X86_XSTATE_AVX))
>      {
>        int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");
>  
> @@ -366,7 +366,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>      }
>  
>    /* Check if any K registers are changed.  */
> -  if ((x86_xcr0 & X86_XSTATE_K))
> +  if ((x86_xstate_bv & X86_XSTATE_K))
>      {
>        int k0_regnum = find_regno (regcache->tdesc, "k0");
>  
> @@ -383,7 +383,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>      }
>  
>    /* Check if any of ZMM0H-ZMM15H registers are changed.  */
> -  if ((x86_xcr0 & X86_XSTATE_ZMM_H))
> +  if ((x86_xstate_bv & X86_XSTATE_ZMM_H))
>      {
>        int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");
>  
> @@ -400,7 +400,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>      }
>  
>    /* Check if any of ZMM16-ZMM31 registers are changed.  */
> -  if ((x86_xcr0 & X86_XSTATE_ZMM) && num_zmm_high_registers != 0)
> +  if ((x86_xstate_bv & X86_XSTATE_ZMM) && num_zmm_high_registers != 0)
>      {
>        int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
>        int ymm16h_regnum = find_regno (regcache->tdesc, "ymm16h");
> @@ -437,7 +437,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>      }
>  
>    /* Check if any PKEYS registers are changed.  */
> -  if ((x86_xcr0 & X86_XSTATE_PKRU))
> +  if ((x86_xstate_bv & X86_XSTATE_PKRU))
>      {
>        int pkru_regnum = find_regno (regcache->tdesc, "pkru");
>  
> @@ -453,7 +453,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>  	}
>      }
>  
> -  if ((x86_xcr0 & X86_XSTATE_SSE) || (x86_xcr0 & X86_XSTATE_AVX))
> +  if ((x86_xstate_bv & X86_XSTATE_SSE) || (x86_xstate_bv & X86_XSTATE_AVX))
>      {
>        collect_register_by_name (regcache, "mxcsr", raw);
>        if (memcmp (raw, &fp->mxcsr, 4) != 0)
> @@ -465,7 +465,7 @@ i387_cache_to_xsave (struct regcache *regcache, void *buf)
>  	}
>      }
>  
> -  if (x86_xcr0 & X86_XSTATE_X87)
> +  if (x86_xstate_bv & X86_XSTATE_X87)
>      {
>        collect_register_by_name (regcache, "fioff", raw);
>        if (memcmp (raw, &fp->fioff, 4) != 0)
> @@ -658,10 +658,10 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
>  
>    /* The supported bits in `xstat_bv' are 8 bytes.  Clear part in
>       vector registers if its bit in xstat_bv is zero.  */
> -  clear_bv = (~fp->xstate_bv) & x86_xcr0;
> +  clear_bv = (~fp->xstate_bv) & x86_xstate_bv;
>  
>    /* Check if any x87 registers are changed.  */
> -  if ((x86_xcr0 & X86_XSTATE_X87) != 0)
> +  if ((x86_xstate_bv & X86_XSTATE_X87) != 0)
>      {
>        int st0_regnum = find_regno (regcache->tdesc, "st0");
>  
> @@ -678,7 +678,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
>  	}
>      }
>  
> -  if ((x86_xcr0 & X86_XSTATE_SSE) != 0)
> +  if ((x86_xstate_bv & X86_XSTATE_SSE) != 0)
>      {
>        int xmm0_regnum = find_regno (regcache->tdesc, "xmm0");
>  
> @@ -695,7 +695,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
>  	}
>      }
>  
> -  if ((x86_xcr0 & X86_XSTATE_AVX) != 0)
> +  if ((x86_xstate_bv & X86_XSTATE_AVX) != 0)
>      {
>        int ymm0h_regnum = find_regno (regcache->tdesc, "ymm0h");
>  
> @@ -712,7 +712,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
>  	}
>      }
>  
> -  if ((x86_xcr0 & X86_XSTATE_K) != 0)
> +  if ((x86_xstate_bv & X86_XSTATE_K) != 0)
>      {
>        int k0_regnum = find_regno (regcache->tdesc, "k0");
>  
> @@ -729,7 +729,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
>  	}
>      }
>  
> -  if ((x86_xcr0 & X86_XSTATE_ZMM_H) != 0)
> +  if ((x86_xstate_bv & X86_XSTATE_ZMM_H) != 0)
>      {
>        int zmm0h_regnum = find_regno (regcache->tdesc, "zmm0h");
>  
> @@ -746,7 +746,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
>  	}
>      }
>  
> -  if ((x86_xcr0 & X86_XSTATE_ZMM) != 0 && num_zmm_high_registers != 0)
> +  if ((x86_xstate_bv & X86_XSTATE_ZMM) != 0 && num_zmm_high_registers != 0)
>      {
>        int zmm16h_regnum = find_regno (regcache->tdesc, "zmm16h");
>        int ymm16h_regnum = find_regno (regcache->tdesc, "ymm16h");
> @@ -773,7 +773,7 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
>  	}
>      }
>  
> -  if ((x86_xcr0 & X86_XSTATE_PKRU) != 0)
> +  if ((x86_xstate_bv & X86_XSTATE_PKRU) != 0)
>      {
>        int pkru_regnum = find_regno (regcache->tdesc, "pkru");
>  
> @@ -858,5 +858,5 @@ i387_xsave_to_cache (struct regcache *regcache, const void *buf)
>  std::pair<uint64_t *, x86_xsave_layout *>
>  i387_get_xsave_storage ()
>  {
> -  return { &x86_xcr0, &xsave_layout };
> +  return { &x86_xstate_bv, &xsave_layout };
>  }
> diff --git a/gdbserver/linux-amd64-ipa.cc b/gdbserver/linux-amd64-ipa.cc
> index af4eb03b9b8..75d70a8787c 100644
> --- a/gdbserver/linux-amd64-ipa.cc
> +++ b/gdbserver/linux-amd64-ipa.cc
> @@ -82,7 +82,7 @@ get_raw_reg (const unsigned char *raw_regs, int regnum)
>  const struct target_desc *
>  get_ipa_tdesc (int idx)
>  {
> -  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
> +  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask (idx);
>  
>  #if defined __ILP32__
>    bool is_x32 = true;
> @@ -90,7 +90,7 @@ get_ipa_tdesc (int idx)
>    bool is_x32 = false;
>  #endif
>  
> -  return amd64_linux_read_description (xcr0, is_x32);
> +  return amd64_linux_read_description (xstate_bv_mask, is_x32);
>  }
>  
>  /* Allocate buffer for the jump pads.  The branch instruction has a
> @@ -159,9 +159,11 @@ initialize_low_tracepoint (void)
>  {
>  #if defined __ILP32__
>    for (int i = 0; i < x86_linux_x32_tdesc_count (); i++)
> -    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), true);
> +    amd64_linux_read_description
> +      (x86_linux_tdesc_idx_to_xstate_bv_mask (i), true);
>  #else
>    for (int i = 0; i < x86_linux_amd64_tdesc_count (); i++)
> -    amd64_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i), false);
> +    amd64_linux_read_description
> +      (x86_linux_tdesc_idx_to_xstate_bv_mask (i), false);
>  #endif
>  }
> diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
> index 17af6eb3610..1a0393d127c 100644
> --- a/gdbserver/linux-i386-ipa.cc
> +++ b/gdbserver/linux-i386-ipa.cc
> @@ -174,9 +174,9 @@ initialize_fast_tracepoint_trampoline_buffer (void)
>  const struct target_desc *
>  get_ipa_tdesc (int idx)
>  {
> -  uint64_t xcr0 = x86_linux_tdesc_idx_to_xcr0 (idx);
> +  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask  (idx);
>  
> -  return i386_linux_read_description (xcr0);
> +  return i386_linux_read_description (xstate_bv_mask);
>  }
>  
>  /* Allocate buffer for the jump pads.  On i386, we can reach an arbitrary
> @@ -199,5 +199,5 @@ initialize_low_tracepoint (void)
>  {
>    initialize_fast_tracepoint_trampoline_buffer ();
>    for (int i = 0; i < x86_linux_i386_tdesc_count (); i++)
> -    i386_linux_read_description (x86_linux_tdesc_idx_to_xcr0 (i));
> +    i386_linux_read_description (x86_linux_tdesc_idx_to_xstate_bv_mask (i));
>  }
> diff --git a/gdbserver/linux-x86-low.cc b/gdbserver/linux-x86-low.cc
> index 24920e71a53..dbc11d6856d 100644
> --- a/gdbserver/linux-x86-low.cc
> +++ b/gdbserver/linux-x86-low.cc
> @@ -873,7 +873,7 @@ x86_linux_read_description ()
>    bool have_ptrace_getregset_was_unknown
>      = have_ptrace_getregset == TRIBOOL_UNKNOWN;
>  
> -  /* Get pointers to where we should store the xcr0 and xsave_layout
> +  /* Get pointers to where we should store the xstate_bv and xsave_layout
>       values.  These will be filled in by x86_linux_tdesc_for_tid the first
>       time that the function is called.  Subsequent calls will not modify
>       the stored values.  */
> @@ -2892,17 +2892,16 @@ x86_target::get_ipa_tdesc_idx ()
>  		  || tdesc == tdesc_amd64_linux_no_xml.get ()
>  #endif /* __x86_64__ */
>  		  );
> -      return x86_linux_xcr0_to_tdesc_idx (X86_XSTATE_SSE_MASK);
> +      return x86_linux_xstate_bv_mask_to_tdesc_idx (X86_XSTATE_SSE_MASK);
>      }
>  
> -  /* The xcr0 value and xsave layout value are cached when the target
> +  /* The xstate_bv value and xsave layout value are cached when the target
>       description is read.  Grab their cache location, and use the cached
>       value to calculate a tdesc index.  */
>    std::pair<uint64_t *, x86_xsave_layout *> storage
>      = i387_get_xsave_storage ();
> -  uint64_t xcr0 = *storage.first;
>  
> -  return x86_linux_xcr0_to_tdesc_idx (xcr0);
> +  return x86_linux_xstate_bv_mask_to_tdesc_idx (*storage.first);
>  }
>  
>  /* The linux target ops object.  */
> diff --git a/gdbsupport/x86-xstate.h b/gdbsupport/x86-xstate.h
> index 5d563ff4622..9bb373c3100 100644
> --- a/gdbsupport/x86-xstate.h
> +++ b/gdbsupport/x86-xstate.h
> @@ -83,8 +83,10 @@ constexpr bool operator!= (const x86_xsave_layout &lhs,
>  #define X86_XSTATE_AVX_AVX512_PKU_MASK 	(X86_XSTATE_AVX_MASK\
>  					| X86_XSTATE_AVX512 | X86_XSTATE_PKRU)
>  
> -#define X86_XSTATE_ALL_MASK		(X86_XSTATE_AVX_AVX512_PKU_MASK)
> +/* Supported mask of state-component bitmap xstate_bv.  The SDM defines
> +   xstate_bv as XCR0 | IA32_XSS.  */
>  
> +#define X86_XSTATE_ALL_MASK		(X86_XSTATE_AVX_AVX512_PKU_MASK)
>  
>  #define X86_XSTATE_SSE_SIZE	576
>  #define X86_XSTATE_AVX_SIZE	832
> -- 
> 2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.
  2025-07-14 13:52   ` Andrew Burgess
@ 2025-07-15 10:28     ` Schimpe, Christina
  2025-07-23 12:47       ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-15 10:28 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

[-- Attachment #1: Type: text/plain, Size: 7685 bytes --]

Hi Andrew,

Thanks a lot for the review.

>> The XSAVE features set is organized in state components, which are a set
>> of or parts of registers.  So-called XSAVE-supported features are

> Maybe here you mean: "..., which are a set of, or parts of, registers."
> If not, then this sentence doesn't make sense to me

I agree, this sentence is a bit confusing. I think I would prefer to write it out actually.
Is the following more understandable?
"The XSAVE function set is organized in state components, which are a set of registers
or parts of registers."

>> The SDM uses the term xstate_bv for a state-component bitmap, which is

> Would it be possible to define what SDM it please.

Do you mean writing it out to "Intel Software Developer’s Manual" ?

> I'd like to ask about the use of 'mask' in this variable name.  We used
> to pass in an xcr0 value, which was then combined with various masks to
> extract the state bits we were interested in.

> You've renamed the new variable as a mask, but continue to combine it
> with the same masks (e.g. X86_XSTATE_AVX), which makes me suspect the
> variable is not a mask at all.

It's not a mask at all. I honestly cannot tell why I decided to call this xstate_bv
instead of xstate_bv_mask when I wrote this patch...

> Now, I can see where the confusion might have come from.  Almost every
> call to amd64_target_description passes in a *_MASK constant.  But
> that's OK, given these are bit sets, then the MASK constants actually
> define valid values.

> I am aware that this sounds like a trivial complaint over a variable
> name, but I think calling this a mask is pretty confusing (at least to
> me), so unless I'm not understanding this, then I think it is worth
> renaming this.

> The use of `mask` for the value is present throughout this patch, not
> just this one function.

I agree and am glad for your feedback here.
The current naming is confusing and I will rename it to xstate_bv.

Christina

________________________________
From: Andrew Burgess <aburgess@redhat.com>
Sent: Monday, July 14, 2025 3:52 PM
To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-patches@sourceware.org <gdb-patches@sourceware.org>
Cc: thiago.bauermann@linaro.org <thiago.bauermann@linaro.org>; luis.machado@arm.com <luis.machado@arm.com>
Subject: Re: [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.

Christina Schimpe <christina.schimpe@intel.com> writes:

> The XSAVE features set is organized in state components, which are a set
> of or parts of registers.  So-called XSAVE-supported features are

Maybe here you mean: "..., which are a set of, or parts of, registers."
If not, then this sentence doesn't make sense to me.

> organized using state-component bitmaps, each bit corresponding to a
> single state component.
>
> The SDM uses the term xstate_bv for a state-component bitmap, which is

Would it be possible to define what SDM it please.

> defined as XCR0 | IA32_XSS.  The control register XCR0 only contains a
> state-component bitmap that specifies user state components, while IA32_XSS
> contains a state-component bitmap that specifies supervisor state components.
>
> Until now, XCR0 is used as input for target description creation in GDB.
> However, a following patch will add userspace support for the CET shadow
> stack feature by Intel.  The CET state is configured in IA32_XSS and consists
> of 2 state components:
> - State component 11 used for the 2 MSRs controlling user-mode
>   functionality for CET (CET_U state)
> - State component 12 used for the 3 MSRs containing shadow-stack pointers
>   for privilege levels 0-2 (CET_S state).
>
> Reading the CET shadow stack pointer register on linux requires a separate
> ptrace call using NT_X86_SHSTK.  To pass the CET shadow stack enablement
> state we would like to pass the xstate_bv value instead of xcr0 for target
> description creation.  To prepare for that, we rename the xcr0 mask
> values for target description creation to xstate_bv.  However, this
> patch doesn't add any functional changes in GDB.
>
> Future states specified in IA32_XSS such as CET will create a combined
> xstate_bv_mask including xcr0 register value and its corresponding bit in
> the state component bitmap.  This combined mask will then be used to create
> the target descriptions.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Reviewed-By: Luis Machado <luis.machado@arm.com>
> ---
>  gdb/amd64-tdep.c                    | 14 +++----
>  gdb/amd64-tdep.h                    |  8 +++-
>  gdb/arch/amd64-linux-tdesc.c        | 33 ++++++++--------
>  gdb/arch/amd64-linux-tdesc.h        |  7 ++--
>  gdb/arch/amd64.c                    | 15 +++-----
>  gdb/arch/amd64.h                    | 10 ++++-
>  gdb/arch/i386-linux-tdesc.c         | 29 +++++++-------
>  gdb/arch/i386-linux-tdesc.h         |  5 ++-
>  gdb/arch/i386.c                     | 15 ++++----
>  gdb/arch/i386.h                     |  8 +++-
>  gdb/arch/x86-linux-tdesc-features.c | 59 +++++++++++++++--------------
>  gdb/arch/x86-linux-tdesc-features.h | 25 +++++++-----
>  gdb/i386-tdep.c                     | 14 +++----
>  gdb/i386-tdep.h                     |  7 +++-
>  gdb/nat/x86-linux-tdesc.c           | 18 +++++----
>  gdb/nat/x86-linux-tdesc.h           |  7 ++--
>  gdb/x86-linux-nat.c                 | 11 ++++--
>  gdbserver/i387-fp.cc                | 40 +++++++++----------
>  gdbserver/linux-amd64-ipa.cc        | 10 +++--
>  gdbserver/linux-i386-ipa.cc         |  6 +--
>  gdbserver/linux-x86-low.cc          |  9 ++---
>  gdbsupport/x86-xstate.h             |  4 +-
>  22 files changed, 198 insertions(+), 156 deletions(-)
>
> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index 82dd1e07cf3..04539dd288a 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -3551,23 +3551,23 @@ amd64_x32_none_init_abi (gdbarch_info info, gdbarch *arch)
>                      amd64_target_description (X86_XSTATE_SSE_MASK, true));
>  }
>
> -/* Return the target description for a specified XSAVE feature mask.  */
> +/* See amd64-tdep.h.  */
>
>  const struct target_desc *
> -amd64_target_description (uint64_t xcr0, bool segments)
> +amd64_target_description (uint64_t xstate_bv_mask, bool segments)

I'd like to ask about the use of 'mask' in this variable name.  We used
to pass in an xcr0 value, which was then combined with various masks to
extract the state bits we were interested in.

You've renamed the new variable as a mask, but continue to combine it
with the same masks (e.g. X86_XSTATE_AVX), which makes me suspect the
variable is not a mask at all.

Now, I can see where the confusion might have come from.  Almost every
call to amd64_target_description passes in a *_MASK constant.  But
that's OK, given these are bit sets, then the MASK constants actually
define valid values.

I am aware that this sounds like a trivial complaint over a variable
name, but I think calling this a mask is pretty confusing (at least to
me), so unless I'm not understanding this, then I think it is worth
renaming this.

The use of `mask` for the value is present throughout this patch, not
just this one function.

Thanks,
Andrew

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

[-- Attachment #2: Type: text/html, Size: 15859 bytes --]

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-14  7:13           ` Luis Machado
@ 2025-07-17 12:01             ` Schimpe, Christina
  2025-07-17 14:59               ` Luis Machado
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-17 12:01 UTC (permalink / raw)
  To: Luis Machado, gdb-patches; +Cc: thiago.bauermann, Andrew Burgess

Hi Luis,

Thank you for the feedback.

> -----Original Message-----
> From: Luis Machado <luis.machado@arm.com>
> Sent: Monday, July 14, 2025 9:13 AM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org
> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
> 
> On 7/13/25 20:05, Luis Machado wrote:
> > Hi,
> >
> > On 7/13/25 15:01, Schimpe, Christina wrote:
> >> Hi Luis,
> >>
> >> Thanks for sharing that info.
> >> I tried to reproduces with the same ubuntu & kernel versions and a very
> similar system but am not successful so far...
> >> Just to be sure, I assume you test with gcc compiler (which version?) and
> compiled GDB with latest upstream master + my cet shadow stack patches?
> >
> > Interesting. GCC says: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
> >
> > I applied the CET shadow stack patches from v5. And yes, gdb is the latest
> tip-of-tree.
> >>
> >> Do you see this for target_board=native-gdbserver and
> target_board=unix?
> >
> > No. native-gdbserver, native-extended-gdbserver and unix work just fine.
> >
> > Let me have another look to see what might be going on.
> 
> Heh, found it. I had a freshly-installed system and was missing libexpat-dev,
> so no XML was being generated.
> 
> With libexpat-dev installed, everything looks fine. So this is good. Sorry for
> the noise.

No worries!

> I spotted two things while building gdb with the patch series applied
> 
> There is a uninitialized usage warning
> 
> gdb/nat/x86-linux.c:151:12: warning: 'ecx' may be used uninitialized [-
> Wmaybe-uninitialized]
>   151 |   if ((ecx & bit_SHSTK) == 0)
>       |       ~~~~~^~~~~~~~~~~~
> ../../../repos/binutils-gdb/gdb/nat/x86-linux.c:147:26: note: 'ecx' was declared
> here
>   147 |   unsigned int eax, ebx, ecx, edx;

Yes, thank you for catching this. I'll fix it as follows:

diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index 1756d5441fc..d037992d849 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -145,6 +145,7 @@ x86_check_ssp_support (const int tid)
      stack is not enabled for the current thread, we still want to return
      true.  */
   unsigned int eax, ebx, ecx, edx;
+  eax = ebx = ecx = edx = 0;

> Also, git am seems to complain about some whitespace issues:
> Applying: gdb, gdbserver: Add support of Intel shadow stack pointer register.
> .git/rebase-apply/patch:596: indent with spaces.
>             # Having unavailable registers leads to a fall back to the standard
> .git/rebase-apply/patch:597: indent with spaces.
>             # unwinders.  Don't add unavailable registers to avoid this.
> .git/rebase-apply/patch:598: indent with spaces.
>             if (str (val) == "<unavailable>"):
> .git/rebase-apply/patch:599: indent with spaces.
>                 continue

I would assume that these findings are false positives, since it's a .py file and the
idents are consistently with spaces in that file.
Is that ok?

> .git/rebase-apply/patch:587: new blank line at EOF.
> +

Thanks! Will fix.

I also spotted a few whitespace issues through the series: 

diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
index 782b66f1467..a2b49730e00 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -1981,7 +1981,7 @@ amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
     return;
 
   /* The shadow stack grows downwards.  To push addresses to the stack,
-     we need to decrement SSP.    */
+     we need to decrement SSP.  */
   const int element_size
     = amd64_linux_shadow_stack_element_size_aligned (gdbarch);
   const CORE_ADDR new_ssp = *ssp - element_size;
@@ -2004,7 +2004,7 @@ amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
   regcache_raw_write_unsigned (regcache, tdep->ssp_regnum, new_ssp);
 }
 
-/* Implement shadow stack pointer unwinding. For each new shadow stack
+/* Implement shadow stack pointer unwinding.  For each new shadow stack
    pointer check if its address is still in the shadow stack memory range.
    If it's outside the range set the returned value to unavailable,
    otherwise return a value containing the new shadow stack pointer.  */
diff --git a/gdb/arch/x86-linux-tdesc-features.h b/gdb/arch/x86-linux-tdesc-features.h
index d1d74e70df8..a280bf608d7 100644
--- a/gdb/arch/x86-linux-tdesc-features.h
+++ b/gdb/arch/x86-linux-tdesc-features.h
@@ -58,7 +58,7 @@ extern int x86_linux_i386_tdesc_count ();
    x86_linux_xstate_bv_mask_to_tdesc_idx) into an xstate_bv_mask
    value which can then be used to create a target description.
    The return mask has the same format than the state component bitmap
-   and does include user and supervisor state components.*/
+   and does include user and supervisor state components.  */
 
 extern uint64_t x86_linux_tdesc_idx_to_xstate_bv_mask (int idx);
 
diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp b/gdb/testsuite/gdb.arch/amd64-ssp.exp
index 6ddc875b9a3..18d43065034 100644
--- a/gdb/testsuite/gdb.arch/amd64-ssp.exp
+++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
@@ -47,4 +47,3 @@ save_vars { ::env(GLIBC_TUNABLES) } {
     # configuring the shadow stack pointer.
     gdb_continue_to_end
 }
-
diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
index 1a0393d127c..beb04b7f8f6 100644
--- a/gdbserver/linux-i386-ipa.cc
+++ b/gdbserver/linux-i386-ipa.cc
@@ -174,7 +174,7 @@ initialize_fast_tracepoint_trampoline_buffer (void)
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask  (idx);
+  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask (idx); 

> warning: 5 lines add whitespace errors.

which I plan to fix locally, too.

> 
> Could you please address those locally?
> 
> Otherwise...
> 
> Approved-By: Luis Machado <luis.machado@arm.com>
> 
> I'd give this another week in case someone has any more comments, and
> then push it.
> 
> Thanks for the series.

My plan is to address your feedback as described here and also Andrew's feedback for the v5.
So far I haven't received any feedback that would block merging this or requires a new version think,
but my plan would be to wait until middle next week. I also plan to include parts of Thiago's patch
"gdb, testsuite: Extend core_find procedure to save program output" (first patch of v5), if his series
will not be merged before mine.

Please let me know if you have any concerns with that plan.

Thanks,
Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save program output.
  2025-07-14 12:21   ` Andrew Burgess
@ 2025-07-17 13:37     ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-17 13:37 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi Andrew,

Thanks for the review.

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Monday, July 14, 2025 2:22 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save
> program output.
> 
> Christina Schimpe <christina.schimpe@intel.com> writes:
> 
> > From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> >
> > The change comes from ARM's GCS series:
> >
> > [PATCH v3 5/9] GDB, gdbserver: aarch64-linux: Initial Guarded Control Stack
> support.
> >
> > We need it for testing coredmp files, too.  So include it in this
> > patch series.
> 
> Typo: "corecmp"

I assume you mean changing coredmp to core dump or coredump?

> With that fixed:
> 
> Approved-By: Andrew Burgess <aburgess@redhat.com>
> 
> Thanks,
> Andrew

Otherwise my plan for this patch would be to merge this middle next week
together with the whole series with some local nits fixed, if there are no further
bigger comments for this series.

Further nits to be fixed locally are described here:

https://sourceware.org/pipermail/gdb-patches/2025-July/219292.html

If there are some more major comments I'll of course post a v6.

Thanks,
Christina

> >
> > Abridged-by: Christina Schimpe <christina.schimpe@intel.com>
> > ---
> > This is the patch mentioned above:
> >
> > https://sourceware.org/pipermail/gdb-patches/2025-June/218892.html
> >
> > Minus everything except for the change in gdb.exp's corefind procedure.
> > ---
> >  gdb/testsuite/lib/gdb.exp | 10 ++++++++--
> >  1 file changed, 8 insertions(+), 2 deletions(-)
> >
> > diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> > index 3f1cd55d727..bb17c4e91a5 100644
> > --- a/gdb/testsuite/lib/gdb.exp
> > +++ b/gdb/testsuite/lib/gdb.exp
> > @@ -9352,7 +9352,13 @@ proc remove_core {pid {test ""}} {
> >      }
> >  }
> >
> > -proc core_find {binfile {deletefiles {}} {arg ""}} {
> > +# Runs ${binfile} expecting it to crash and generate a core file.
> > +# If DELETEFILES is provided, remove these files after running the program.
> > +# If ARG is provided, pass it as a command line argument to the program.
> > +# If OUTPUT_FILE is provided, save the program output to it.
> > +# Returns the name of the core dump, or empty string if not found.
> > +
> > +proc core_find {binfile {deletefiles {}} {arg ""} {output_file
> > +"/dev/null"}} {
> >      global objdir subdir
> >
> >      set destcore "$binfile.core"
> > @@ -9374,7 +9380,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}"
> >      set binfile_basename [file tail $binfile]
> >      foreach i [list \
> > --
> > 2.43.0

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-17 12:01             ` Schimpe, Christina
@ 2025-07-17 14:59               ` Luis Machado
  2025-07-23 12:45                 ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Luis Machado @ 2025-07-17 14:59 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches; +Cc: thiago.bauermann, Andrew Burgess

Hi,

On 7/17/25 13:01, Schimpe, Christina wrote:
> Hi Luis,
> 
> Thank you for the feedback.
> 
>> -----Original Message-----
>> From: Luis Machado <luis.machado@arm.com>
>> Sent: Monday, July 14, 2025 9:13 AM
>> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
>> patches@sourceware.org
>> Cc: thiago.bauermann@linaro.org
>> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
>>
>> On 7/13/25 20:05, Luis Machado wrote:
>>> Hi,
>>>
>>> On 7/13/25 15:01, Schimpe, Christina wrote:
>>>> Hi Luis,
>>>>
>>>> Thanks for sharing that info.
>>>> I tried to reproduces with the same ubuntu & kernel versions and a very
>> similar system but am not successful so far...
>>>> Just to be sure, I assume you test with gcc compiler (which version?) and
>> compiled GDB with latest upstream master + my cet shadow stack patches?
>>>
>>> Interesting. GCC says: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
>>>
>>> I applied the CET shadow stack patches from v5. And yes, gdb is the latest
>> tip-of-tree.
>>>>
>>>> Do you see this for target_board=native-gdbserver and
>> target_board=unix?
>>>
>>> No. native-gdbserver, native-extended-gdbserver and unix work just fine.
>>>
>>> Let me have another look to see what might be going on.
>>
>> Heh, found it. I had a freshly-installed system and was missing libexpat-dev,
>> so no XML was being generated.
>>
>> With libexpat-dev installed, everything looks fine. So this is good. Sorry for
>> the noise.
> 
> No worries!
> 
>> I spotted two things while building gdb with the patch series applied
>>
>> There is a uninitialized usage warning
>>
>> gdb/nat/x86-linux.c:151:12: warning: 'ecx' may be used uninitialized [-
>> Wmaybe-uninitialized]
>>   151 |   if ((ecx & bit_SHSTK) == 0)
>>       |       ~~~~~^~~~~~~~~~~~
>> ../../../repos/binutils-gdb/gdb/nat/x86-linux.c:147:26: note: 'ecx' was declared
>> here
>>   147 |   unsigned int eax, ebx, ecx, edx;
> 
> Yes, thank you for catching this. I'll fix it as follows:
> 
> diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
> index 1756d5441fc..d037992d849 100644
> --- a/gdb/nat/x86-linux.c
> +++ b/gdb/nat/x86-linux.c
> @@ -145,6 +145,7 @@ x86_check_ssp_support (const int tid)
>       stack is not enabled for the current thread, we still want to return
>       true.  */
>    unsigned int eax, ebx, ecx, edx;
> +  eax = ebx = ecx = edx = 0;
> 
>> Also, git am seems to complain about some whitespace issues:
>> Applying: gdb, gdbserver: Add support of Intel shadow stack pointer register.
>> .git/rebase-apply/patch:596: indent with spaces.
>>             # Having unavailable registers leads to a fall back to the standard
>> .git/rebase-apply/patch:597: indent with spaces.
>>             # unwinders.  Don't add unavailable registers to avoid this.
>> .git/rebase-apply/patch:598: indent with spaces.
>>             if (str (val) == "<unavailable>"):
>> .git/rebase-apply/patch:599: indent with spaces.
>>                 continue
> 
> I would assume that these findings are false positives, since it's a .py file and the
> idents are consistently with spaces in that file.
> Is that ok?

It might be. If a false positive, feel free to ignore it.

> 
>> .git/rebase-apply/patch:587: new blank line at EOF.
>> +
> 
> Thanks! Will fix.
> 
> I also spotted a few whitespace issues through the series: 
> 
> diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
> index 782b66f1467..a2b49730e00 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -1981,7 +1981,7 @@ amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
>      return;
>  
>    /* The shadow stack grows downwards.  To push addresses to the stack,
> -     we need to decrement SSP.    */
> +     we need to decrement SSP.  */
>    const int element_size
>      = amd64_linux_shadow_stack_element_size_aligned (gdbarch);
>    const CORE_ADDR new_ssp = *ssp - element_size;
> @@ -2004,7 +2004,7 @@ amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
>    regcache_raw_write_unsigned (regcache, tdep->ssp_regnum, new_ssp);
>  }
>  
> -/* Implement shadow stack pointer unwinding. For each new shadow stack
> +/* Implement shadow stack pointer unwinding.  For each new shadow stack
>     pointer check if its address is still in the shadow stack memory range.
>     If it's outside the range set the returned value to unavailable,
>     otherwise return a value containing the new shadow stack pointer.  */
> diff --git a/gdb/arch/x86-linux-tdesc-features.h b/gdb/arch/x86-linux-tdesc-features.h
> index d1d74e70df8..a280bf608d7 100644
> --- a/gdb/arch/x86-linux-tdesc-features.h
> +++ b/gdb/arch/x86-linux-tdesc-features.h
> @@ -58,7 +58,7 @@ extern int x86_linux_i386_tdesc_count ();
>     x86_linux_xstate_bv_mask_to_tdesc_idx) into an xstate_bv_mask
>     value which can then be used to create a target description.
>     The return mask has the same format than the state component bitmap
> -   and does include user and supervisor state components.*/
> +   and does include user and supervisor state components.  */
>  
>  extern uint64_t x86_linux_tdesc_idx_to_xstate_bv_mask (int idx);
>  
> diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> index 6ddc875b9a3..18d43065034 100644
> --- a/gdb/testsuite/gdb.arch/amd64-ssp.exp
> +++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> @@ -47,4 +47,3 @@ save_vars { ::env(GLIBC_TUNABLES) } {
>      # configuring the shadow stack pointer.
>      gdb_continue_to_end
>  }
> -
> diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
> index 1a0393d127c..beb04b7f8f6 100644
> --- a/gdbserver/linux-i386-ipa.cc
> +++ b/gdbserver/linux-i386-ipa.cc
> @@ -174,7 +174,7 @@ initialize_fast_tracepoint_trampoline_buffer (void)
>  const struct target_desc *
>  get_ipa_tdesc (int idx)
>  {
> -  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask  (idx);
> +  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask (idx); 
> 
>> warning: 5 lines add whitespace errors.
> 
> which I plan to fix locally, too.
> 
>>
>> Could you please address those locally?
>>
>> Otherwise...
>>
>> Approved-By: Luis Machado <luis.machado@arm.com>
>>
>> I'd give this another week in case someone has any more comments, and
>> then push it.
>>
>> Thanks for the series.
> 
> My plan is to address your feedback as described here and also Andrew's feedback for the v5.
> So far I haven't received any feedback that would block merging this or requires a new version think,
> but my plan would be to wait until middle next week. I also plan to include parts of Thiago's patch
> "gdb, testsuite: Extend core_find procedure to save program output" (first patch of v5), if his series
> will not be merged before mine.
> 
> Please let me know if you have any concerns with that plan.
That sounds reasonable to me.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-17 14:59               ` Luis Machado
@ 2025-07-23 12:45                 ` Schimpe, Christina
  2025-07-28 17:05                   ` Luis Machado
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-23 12:45 UTC (permalink / raw)
  To: Luis Machado, gdb-patches; +Cc: thiago.bauermann, Andrew Burgess

Hi Luis,

I finally also tested x32 and it looks good, there was only one issue
with the new coredump test due to RDSSPD/RDSSPQ instructions
for reading the shadow stack pointer:
amd64-shadow-stack-corefile.c:26: Error: operand size mismatch for `rdsspq'

So I have to adapt the instruction for x32 to "rdsspd " to copy low 32 bits only.

I suggest to fix the testfile as follows:
~~~
diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
index f078e33810d..5e84793ccb1 100644
--- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
+++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
@@ -23,7 +23,11 @@ void
 function ()
 {
   unsigned long ssp;
-  asm volatile("xor %0, %0; rdsspq %0" : "=r" (ssp));
+  #ifndef __ILP32__
+    asm volatile ("xor %0, %0; rdsspq %0" : "=r" (ssp));
+  #else
+    asm volatile ("xor %0, %0; rdsspd %0" : "=r" (ssp));
+  #endif
~~~

Would that be fine ore should I repost the patch?

Other than that I'll retest again with latest upstream master and push this tomorrow.

Thanks,
Christina

> -----Original Message-----
> From: Luis Machado <luis.machado@arm.com>
> Sent: Thursday, July 17, 2025 5:00 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; Andrew Burgess <aburgess@redhat.com>
> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
> 
> Hi,
> 
> On 7/17/25 13:01, Schimpe, Christina wrote:
> > Hi Luis,
> >
> > Thank you for the feedback.
> >
> >> -----Original Message-----
> >> From: Luis Machado <luis.machado@arm.com>
> >> Sent: Monday, July 14, 2025 9:13 AM
> >> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> >> patches@sourceware.org
> >> Cc: thiago.bauermann@linaro.org
> >> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
> >>
> >> On 7/13/25 20:05, Luis Machado wrote:
> >>> Hi,
> >>>
> >>> On 7/13/25 15:01, Schimpe, Christina wrote:
> >>>> Hi Luis,
> >>>>
> >>>> Thanks for sharing that info.
> >>>> I tried to reproduces with the same ubuntu & kernel versions and a
> >>>> very
> >> similar system but am not successful so far...
> >>>> Just to be sure, I assume you test with gcc compiler (which
> >>>> version?) and
> >> compiled GDB with latest upstream master + my cet shadow stack
> patches?
> >>>
> >>> Interesting. GCC says: gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
> >>>
> >>> I applied the CET shadow stack patches from v5. And yes, gdb is the
> >>> latest
> >> tip-of-tree.
> >>>>
> >>>> Do you see this for target_board=native-gdbserver and
> >> target_board=unix?
> >>>
> >>> No. native-gdbserver, native-extended-gdbserver and unix work just fine.
> >>>
> >>> Let me have another look to see what might be going on.
> >>
> >> Heh, found it. I had a freshly-installed system and was missing
> >> libexpat-dev, so no XML was being generated.
> >>
> >> With libexpat-dev installed, everything looks fine. So this is good.
> >> Sorry for the noise.
> >
> > No worries!
> >
> >> I spotted two things while building gdb with the patch series applied
> >>
> >> There is a uninitialized usage warning
> >>
> >> gdb/nat/x86-linux.c:151:12: warning: 'ecx' may be used uninitialized
> >> [- Wmaybe-uninitialized]
> >>   151 |   if ((ecx & bit_SHSTK) == 0)
> >>       |       ~~~~~^~~~~~~~~~~~
> >> ../../../repos/binutils-gdb/gdb/nat/x86-linux.c:147:26: note: 'ecx'
> >> was declared here
> >>   147 |   unsigned int eax, ebx, ecx, edx;
> >
> > Yes, thank you for catching this. I'll fix it as follows:
> >
> > diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c index
> > 1756d5441fc..d037992d849 100644
> > --- a/gdb/nat/x86-linux.c
> > +++ b/gdb/nat/x86-linux.c
> > @@ -145,6 +145,7 @@ x86_check_ssp_support (const int tid)
> >       stack is not enabled for the current thread, we still want to return
> >       true.  */
> >    unsigned int eax, ebx, ecx, edx;
> > +  eax = ebx = ecx = edx = 0;
> >
> >> Also, git am seems to complain about some whitespace issues:
> >> Applying: gdb, gdbserver: Add support of Intel shadow stack pointer
> register.
> >> .git/rebase-apply/patch:596: indent with spaces.
> >>             # Having unavailable registers leads to a fall back to
> >> the standard
> >> .git/rebase-apply/patch:597: indent with spaces.
> >>             # unwinders.  Don't add unavailable registers to avoid this.
> >> .git/rebase-apply/patch:598: indent with spaces.
> >>             if (str (val) == "<unavailable>"):
> >> .git/rebase-apply/patch:599: indent with spaces.
> >>                 continue
> >
> > I would assume that these findings are false positives, since it's a
> > .py file and the idents are consistently with spaces in that file.
> > Is that ok?
> 
> It might be. If a false positive, feel free to ignore it.
> 
> >
> >> .git/rebase-apply/patch:587: new blank line at EOF.
> >> +
> >
> > Thanks! Will fix.
> >
> > I also spotted a few whitespace issues through the series:
> >
> > diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index
> > 782b66f1467..a2b49730e00 100644
> > --- a/gdb/amd64-linux-tdep.c
> > +++ b/gdb/amd64-linux-tdep.c
> > @@ -1981,7 +1981,7 @@ amd64_linux_shadow_stack_push (gdbarch
> *gdbarch, CORE_ADDR new_addr,
> >      return;
> >
> >    /* The shadow stack grows downwards.  To push addresses to the stack,
> > -     we need to decrement SSP.    */
> > +     we need to decrement SSP.  */
> >    const int element_size
> >      = amd64_linux_shadow_stack_element_size_aligned (gdbarch);
> >    const CORE_ADDR new_ssp = *ssp - element_size; @@ -2004,7 +2004,7
> > @@ amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR
> new_addr,
> >    regcache_raw_write_unsigned (regcache, tdep->ssp_regnum, new_ssp);
> > }
> >
> > -/* Implement shadow stack pointer unwinding. For each new shadow
> > stack
> > +/* Implement shadow stack pointer unwinding.  For each new shadow
> > +stack
> >     pointer check if its address is still in the shadow stack memory range.
> >     If it's outside the range set the returned value to unavailable,
> >     otherwise return a value containing the new shadow stack pointer.
> > */ diff --git a/gdb/arch/x86-linux-tdesc-features.h
> > b/gdb/arch/x86-linux-tdesc-features.h
> > index d1d74e70df8..a280bf608d7 100644
> > --- a/gdb/arch/x86-linux-tdesc-features.h
> > +++ b/gdb/arch/x86-linux-tdesc-features.h
> > @@ -58,7 +58,7 @@ extern int x86_linux_i386_tdesc_count ();
> >     x86_linux_xstate_bv_mask_to_tdesc_idx) into an xstate_bv_mask
> >     value which can then be used to create a target description.
> >     The return mask has the same format than the state component bitmap
> > -   and does include user and supervisor state components.*/
> > +   and does include user and supervisor state components.  */
> >
> >  extern uint64_t x86_linux_tdesc_idx_to_xstate_bv_mask (int idx);
> >
> > diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp
> > b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> > index 6ddc875b9a3..18d43065034 100644
> > --- a/gdb/testsuite/gdb.arch/amd64-ssp.exp
> > +++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> > @@ -47,4 +47,3 @@ save_vars { ::env(GLIBC_TUNABLES) } {
> >      # configuring the shadow stack pointer.
> >      gdb_continue_to_end
> >  }
> > -
> > diff --git a/gdbserver/linux-i386-ipa.cc b/gdbserver/linux-i386-ipa.cc
> > index 1a0393d127c..beb04b7f8f6 100644
> > --- a/gdbserver/linux-i386-ipa.cc
> > +++ b/gdbserver/linux-i386-ipa.cc
> > @@ -174,7 +174,7 @@ initialize_fast_tracepoint_trampoline_buffer
> > (void)  const struct target_desc *  get_ipa_tdesc (int idx)  {
> > -  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask
> > (idx);
> > +  uint64_t xstate_bv_mask = x86_linux_tdesc_idx_to_xstate_bv_mask
> > + (idx);
> >
> >> warning: 5 lines add whitespace errors.
> >
> > which I plan to fix locally, too.
> >
> >>
> >> Could you please address those locally?
> >>
> >> Otherwise...
> >>
> >> Approved-By: Luis Machado <luis.machado@arm.com>
> >>
> >> I'd give this another week in case someone has any more comments, and
> >> then push it.
> >>
> >> Thanks for the series.
> >
> > My plan is to address your feedback as described here and also Andrew's
> feedback for the v5.
> > So far I haven't received any feedback that would block merging this
> > or requires a new version think, but my plan would be to wait until
> > middle next week. I also plan to include parts of Thiago's patch "gdb,
> > testsuite: Extend core_find procedure to save program output" (first patch
> of v5), if his series will not be merged before mine.
> >
> > Please let me know if you have any concerns with that plan.
> That sounds reasonable to me.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.
  2025-07-15 10:28     ` Schimpe, Christina
@ 2025-07-23 12:47       ` Schimpe, Christina
  2025-08-05 13:47         ` Andrew Burgess
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-23 12:47 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

[-- Attachment #1: Type: text/plain, Size: 8587 bytes --]

Hi Andrew,

Do you have any final comments ?
My plan would be to fix your comments locally (as described below) and push the series by tomorrow.
Or should I repost this patch to be sure?

Thanks,
Christina

From: Schimpe, Christina <christina.schimpe@intel.com>
Sent: Tuesday, July 15, 2025 12:28 PM
To: Andrew Burgess <aburgess@redhat.com>; gdb-patches@sourceware.org
Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
Subject: Re: [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.

Hi Andrew,

Thanks a lot for the review.

>> The XSAVE features set is organized in state components, which are a set
>> of or parts of registers.  So-called XSAVE-supported features are

> Maybe here you mean: "..., which are a set of, or parts of, registers."
> If not, then this sentence doesn't make sense to me

I agree, this sentence is a bit confusing. I think I would prefer to write it out actually.
Is the following more understandable?
"The XSAVE function set is organized in state components, which are a set of registers
or parts of registers."

>> The SDM uses the term xstate_bv for a state-component bitmap, which is

> Would it be possible to define what SDM it please.

Do you mean writing it out to "Intel Software Developer's Manual" ?

> I'd like to ask about the use of 'mask' in this variable name.  We used
> to pass in an xcr0 value, which was then combined with various masks to
> extract the state bits we were interested in.

> You've renamed the new variable as a mask, but continue to combine it
> with the same masks (e.g. X86_XSTATE_AVX), which makes me suspect the
> variable is not a mask at all.

It's not a mask at all. I honestly cannot tell why I decided to call this xstate_bv
instead of xstate_bv_mask when I wrote this patch...

> Now, I can see where the confusion might have come from.  Almost every
> call to amd64_target_description passes in a *_MASK constant.  But
> that's OK, given these are bit sets, then the MASK constants actually
> define valid values.

> I am aware that this sounds like a trivial complaint over a variable
> name, but I think calling this a mask is pretty confusing (at least to
> me), so unless I'm not understanding this, then I think it is worth
> renaming this.

> The use of `mask` for the value is present throughout this patch, not
> just this one function.

I agree and am glad for your feedback here.
The current naming is confusing and I will rename it to xstate_bv.

Christina

________________________________
From: Andrew Burgess <aburgess@redhat.com<mailto:aburgess@redhat.com>>
Sent: Monday, July 14, 2025 3:52 PM
To: Schimpe, Christina <christina.schimpe@intel.com<mailto:christina.schimpe@intel.com>>; gdb-patches@sourceware.org<mailto:gdb-patches@sourceware.org> <gdb-patches@sourceware.org<mailto:gdb-patches@sourceware.org>>
Cc: thiago.bauermann@linaro.org<mailto:thiago.bauermann@linaro.org> <thiago.bauermann@linaro.org<mailto:thiago.bauermann@linaro.org>>; luis.machado@arm.com<mailto:luis.machado@arm.com> <luis.machado@arm.com<mailto:luis.machado@arm.com>>
Subject: Re: [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.

Christina Schimpe <christina.schimpe@intel.com<mailto:christina.schimpe@intel.com>> writes:

> The XSAVE features set is organized in state components, which are a set
> of or parts of registers.  So-called XSAVE-supported features are

Maybe here you mean: "..., which are a set of, or parts of, registers."
If not, then this sentence doesn't make sense to me.

> organized using state-component bitmaps, each bit corresponding to a
> single state component.
>
> The SDM uses the term xstate_bv for a state-component bitmap, which is

Would it be possible to define what SDM it please.

> defined as XCR0 | IA32_XSS.  The control register XCR0 only contains a
> state-component bitmap that specifies user state components, while IA32_XSS
> contains a state-component bitmap that specifies supervisor state components.
>
> Until now, XCR0 is used as input for target description creation in GDB.
> However, a following patch will add userspace support for the CET shadow
> stack feature by Intel.  The CET state is configured in IA32_XSS and consists
> of 2 state components:
> - State component 11 used for the 2 MSRs controlling user-mode
>   functionality for CET (CET_U state)
> - State component 12 used for the 3 MSRs containing shadow-stack pointers
>   for privilege levels 0-2 (CET_S state).
>
> Reading the CET shadow stack pointer register on linux requires a separate
> ptrace call using NT_X86_SHSTK.  To pass the CET shadow stack enablement
> state we would like to pass the xstate_bv value instead of xcr0 for target
> description creation.  To prepare for that, we rename the xcr0 mask
> values for target description creation to xstate_bv.  However, this
> patch doesn't add any functional changes in GDB.
>
> Future states specified in IA32_XSS such as CET will create a combined
> xstate_bv_mask including xcr0 register value and its corresponding bit in
> the state component bitmap.  This combined mask will then be used to create
> the target descriptions.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org<mailto:thiago.bauermann@linaro.org>>
> Reviewed-By: Luis Machado <luis.machado@arm.com<mailto:luis.machado@arm.com>>
> ---
>  gdb/amd64-tdep.c                    | 14 +++----
>  gdb/amd64-tdep.h                    |  8 +++-
>  gdb/arch/amd64-linux-tdesc.c        | 33 ++++++++--------
>  gdb/arch/amd64-linux-tdesc.h        |  7 ++--
>  gdb/arch/amd64.c                    | 15 +++-----
>  gdb/arch/amd64.h                    | 10 ++++-
>  gdb/arch/i386-linux-tdesc.c         | 29 +++++++-------
>  gdb/arch/i386-linux-tdesc.h         |  5 ++-
>  gdb/arch/i386.c                     | 15 ++++----
>  gdb/arch/i386.h                     |  8 +++-
>  gdb/arch/x86-linux-tdesc-features.c | 59 +++++++++++++++--------------
>  gdb/arch/x86-linux-tdesc-features.h | 25 +++++++-----
>  gdb/i386-tdep.c                     | 14 +++----
>  gdb/i386-tdep.h                     |  7 +++-
>  gdb/nat/x86-linux-tdesc.c           | 18 +++++----
>  gdb/nat/x86-linux-tdesc.h           |  7 ++--
>  gdb/x86-linux-nat.c                 | 11 ++++--
>  gdbserver/i387-fp.cc                | 40 +++++++++----------
>  gdbserver/linux-amd64-ipa.cc        | 10 +++--
>  gdbserver/linux-i386-ipa.cc         |  6 +--
>  gdbserver/linux-x86-low.cc          |  9 ++---
>  gdbsupport/x86-xstate.h             |  4 +-
>  22 files changed, 198 insertions(+), 156 deletions(-)
>
> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index 82dd1e07cf3..04539dd288a 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -3551,23 +3551,23 @@ amd64_x32_none_init_abi (gdbarch_info info, gdbarch *arch)
>                      amd64_target_description (X86_XSTATE_SSE_MASK, true));
>  }
>
> -/* Return the target description for a specified XSAVE feature mask.  */
> +/* See amd64-tdep.h.  */
>
>  const struct target_desc *
> -amd64_target_description (uint64_t xcr0, bool segments)
> +amd64_target_description (uint64_t xstate_bv_mask, bool segments)

I'd like to ask about the use of 'mask' in this variable name.  We used
to pass in an xcr0 value, which was then combined with various masks to
extract the state bits we were interested in.

You've renamed the new variable as a mask, but continue to combine it
with the same masks (e.g. X86_XSTATE_AVX), which makes me suspect the
variable is not a mask at all.

Now, I can see where the confusion might have come from.  Almost every
call to amd64_target_description passes in a *_MASK constant.  But
that's OK, given these are bit sets, then the MASK constants actually
define valid values.

I am aware that this sounds like a trivial complaint over a variable
name, but I think calling this a mask is pretty confusing (at least to
me), so unless I'm not understanding this, then I think it is worth
renaming this.

The use of `mask` for the value is present throughout this patch, not
just this one function.

Thanks,
Andrew
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

[-- Attachment #2: Type: text/html, Size: 18066 bytes --]

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-06-28  8:28 ` [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register Christina Schimpe
@ 2025-07-25 12:49   ` Andrew Burgess
  2025-07-25 15:03     ` Schimpe, Christina
  2025-07-29 13:51   ` Andrew Burgess
  2025-08-10 19:01   ` H.J. Lu
  2 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-07-25 12:49 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> This patch adds the user mode register PL3_SSP which is part of the
> Intel(R) Control-Flow Enforcement Technology (CET) feature for support
> of shadow stack.
> For now, only native and remote debugging support for shadow stack
> userspace on amd64 linux are covered by this patch including 64 bit and
> x32 support.  32 bit support is not covered due to missing Linux kernel
> support.
>
> This patch requires fixing the test gdb.base/inline-frame-cycle-unwind
> which is failing in case the shadow stack pointer is unavailable.
> Such a state is possible if shadow stack is disabled for the current thread
> but supported by HW.
>
> This test uses the Python unwinder inline-frame-cycle-unwind.py which fakes
> the cyclic stack cycle by reading the pending frame's registers and adding
> them to the unwinder:
>
> ~~~
> for reg in pending_frame.architecture().registers("general"):
>      val = pending_frame.read_register(reg)
>      unwinder.add_saved_register(reg, val)
>      return unwinder
> ~~~
>
> However, in case the python unwinder is used we add a register (pl3_ssp) that is
> unavailable.  This leads to a NOT_AVAILABLE_ERROR caught in
> gdb/frame-unwind.c:frame_unwind_try_unwinder and it is continued with standard
> unwinders.  This destroys the faked cyclic behavior and the stack is
> further unwinded after frame 5.
>
> In the working scenario an error should be triggered:
> ~~~
> bt
> 0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
> 1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> 2  0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> 3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> 4  0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> 5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> Backtrace stopped: previous frame identical to this frame (corrupt stack?)
> (gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5: backtrace when the unwind is broken at frame 5
> ~~~
>
> To fix the Python unwinder, we simply skip the unavailable registers.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> Reviewed-By: Luis Machado <luis.machado@arm.com>
> ---
>  gdb/NEWS                                      |  3 +
>  gdb/amd64-linux-nat.c                         | 17 +++++
>  gdb/amd64-linux-tdep.c                        |  1 +
>  gdb/amd64-tdep.c                              |  6 +-
>  gdb/amd64-tdep.h                              |  1 +
>  gdb/arch/amd64.c                              | 10 +++
>  gdb/arch/i386.c                               |  4 ++
>  gdb/arch/x86-linux-tdesc-features.c           |  1 +
>  gdb/doc/gdb.texinfo                           |  4 ++
>  gdb/features/Makefile                         |  2 +
>  gdb/features/i386/32bit-ssp.c                 | 14 ++++
>  gdb/features/i386/32bit-ssp.xml               | 11 +++
>  gdb/features/i386/64bit-ssp.c                 | 14 ++++
>  gdb/features/i386/64bit-ssp.xml               | 11 +++
>  gdb/i386-tdep.c                               | 22 +++++-
>  gdb/i386-tdep.h                               |  4 ++
>  gdb/nat/x86-linux-tdesc.c                     |  2 +
>  gdb/nat/x86-linux.c                           | 57 +++++++++++++++
>  gdb/nat/x86-linux.h                           |  4 ++
>  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
>  gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
>  .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
>  gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
>  gdb/x86-linux-nat.c                           | 49 +++++++++++--
>  gdb/x86-linux-nat.h                           | 11 +++
>  gdb/x86-tdep.c                                | 21 ++++++
>  gdb/x86-tdep.h                                |  9 +++
>  gdbserver/linux-x86-low.cc                    | 28 +++++++-
>  gdbsupport/x86-xstate.h                       |  5 +-
>  29 files changed, 447 insertions(+), 10 deletions(-)
>  create mode 100644 gdb/features/i386/32bit-ssp.c
>  create mode 100644 gdb/features/i386/32bit-ssp.xml
>  create mode 100644 gdb/features/i386/64bit-ssp.c
>  create mode 100644 gdb/features/i386/64bit-ssp.xml
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
>


> diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
> index dbb9b3223cb..4df99ccca54 100644
> --- a/gdb/amd64-linux-nat.c
> +++ b/gdb/amd64-linux-nat.c
> @@ -32,6 +32,7 @@
>  #include "amd64-tdep.h"
>  #include "amd64-linux-tdep.h"
>  #include "i386-linux-tdep.h"
> +#include "x86-tdep.h"
>  #include "gdbsupport/x86-xstate.h"
>  
>  #include "x86-linux-nat.h"
> @@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
>  
>        if (have_ptrace_getregset == TRIBOOL_TRUE)
>  	{
> +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
> +	      || (regnum != -1 && regnum == tdep->ssp_regnum))

It's really nit-picking, but I don't think the '>' here check is
correct.  You're checking that tdep->ssp_regnum has been assigned a
value, right?  And it's default value is -1, so, shouldn't the check
really be '>= 0' or '!= -1' maybe?

The same pattern is repeated below in ::store_registers.

Ahh, reading further on, I see that (not just you), but all the *_regnum
fields in i386_gdbarch_tdep start set to 0 (which is a valid register
number), but are set to -1 if the feature is not found.  Maybe I'm
missing something incredibly clever about this ... but I suspect this
code is just showing its age a little.  I still think the validity check
should be against -1.

> +	    {
> +	      x86_linux_fetch_ssp (regcache, tid);
> +	      if (regnum != -1)
> +		return;
> +	    }
> +
>  	  /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
>  	     "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure on
>  	     Intel Skylake CPUs") that sometimes causes the mxcsr location in
> @@ -302,6 +311,14 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
>        if (have_ptrace_getregset == TRIBOOL_TRUE)
>  	{
>  	  gdb::byte_vector xstateregs (tdep->xsave_layout.sizeof_xsave);
> +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
> +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
> +	    {
> +	      x86_linux_store_ssp (regcache, tid);
> +	      if (regnum != -1)
> +		return;
> +	    }
> +
>  	  struct iovec iov;
>  
>  	  iov.iov_base = xstateregs.data ();


> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 4ef640698bd..0881ac4aee5 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -49959,6 +49959,10 @@ The @samp{org.gnu.gdb.i386.pkeys} feature is optional.  It should
>  describe a single register, @samp{pkru}.  It is a 32-bit register
>  valid for i386 and amd64.
>  
> +The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should describe the
> +user mode register @samp{pl3_ssp} which has 64 bits on amd64.  Following the
> +restriction of the Linux kernel, only amd64 is supported for now.

You mention a couple of times in this patch that the shadow stack is
only supported (for now) on amd64 (& x32), but you have added an i386
version of the org.gnu.gdb.i386.pl3_ssp feature with a 32-bit pl3_ssp
register.

This feature is (as far as I can tell) instantiated for 32-bit targets,
which means gdbserver will send out target descriptions containing this
feature.

I think either (a) you should drop the i386 xml file and feature, or (b)
my preferred choice, document the i386 version here.  It's still fine to
say that the 32-bit (i386) register is ignored by GDB for now though.  I
had a go at rewording the paragraph, but I'm not 100% sure its OK yet:

  The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
  describe the user mode register @samp{pl3_ssp} which has 64 bits on
  amd64 and 32 bits on i386.  Following the restriction of the Linux
  kernel, only GDB for amd64 targets makes use of this feature for now.


> +
>  @node LoongArch Features
>  @subsection LoongArch Features
>  @cindex target descriptions, LoongArch Features
> diff --git a/gdb/features/Makefile b/gdb/features/Makefile
> index 7a8c7999733..2afda1ccd00 100644
> --- a/gdb/features/Makefile
> +++ b/gdb/features/Makefile
> @@ -225,6 +225,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
>  	i386/32bit-avx.xml \
>  	i386/32bit-avx512.xml \
>  	i386/32bit-segments.xml \
> +	i386/32bit-ssp.xml \
>  	i386/64bit-avx512.xml \
>  	i386/64bit-core.xml \
>  	i386/64bit-segments.xml \
> @@ -232,6 +233,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
>  	i386/64bit-linux.xml \
>  	i386/64bit-sse.xml \
>  	i386/pkeys.xml \
> +	i386/64bit-ssp.xml \
>  	i386/x32-core.xml \
>  	loongarch/base32.xml \
>  	loongarch/base64.xml \
> diff --git a/gdb/features/i386/32bit-ssp.c b/gdb/features/i386/32bit-ssp.c
> new file mode 100644
> index 00000000000..991bae3c1e6
> --- /dev/null
> +++ b/gdb/features/i386/32bit-ssp.c
> @@ -0,0 +1,14 @@
> +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> +  Original: 32bit-ssp.xml */
> +
> +#include "gdbsupport/tdesc.h"
> +
> +static int
> +create_feature_i386_32bit_ssp (struct target_desc *result, long regnum)
> +{
> +  struct tdesc_feature *feature;
> +
> +  feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pl3_ssp");
> +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 32, "data_ptr");
> +  return regnum;
> +}
> diff --git a/gdb/features/i386/32bit-ssp.xml b/gdb/features/i386/32bit-ssp.xml
> new file mode 100644
> index 00000000000..d17e7004eec
> --- /dev/null
> +++ b/gdb/features/i386/32bit-ssp.xml
> @@ -0,0 +1,11 @@
> +<?xml version="1.0"?>
> +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> +
> +     Copying and distribution of this file, with or without modification,
> +     are permitted in any medium without royalty provided the copyright
> +     notice and this notice are preserved.  -->
> +
> +<!DOCTYPE feature SYSTEM "gdb-target.dtd">
> +<feature name="org.gnu.gdb.i386.pl3_ssp">
> +  <reg name="pl3_ssp" bitsize="32" type="data_ptr"/>
> +</feature>
> diff --git a/gdb/features/i386/64bit-ssp.c b/gdb/features/i386/64bit-ssp.c
> new file mode 100644
> index 00000000000..5468099ddf6
> --- /dev/null
> +++ b/gdb/features/i386/64bit-ssp.c
> @@ -0,0 +1,14 @@
> +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> +  Original: 64bit-ssp.xml */
> +
> +#include "gdbsupport/tdesc.h"
> +
> +static int
> +create_feature_i386_64bit_ssp (struct target_desc *result, long regnum)
> +{
> +  struct tdesc_feature *feature;
> +
> +  feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pl3_ssp");
> +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 64, "data_ptr");
> +  return regnum;
> +}
> diff --git a/gdb/features/i386/64bit-ssp.xml b/gdb/features/i386/64bit-ssp.xml
> new file mode 100644
> index 00000000000..a0688d018a5
> --- /dev/null
> +++ b/gdb/features/i386/64bit-ssp.xml
> @@ -0,0 +1,11 @@
> +<?xml version="1.0"?>
> +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> +
> +     Copying and distribution of this file, with or without modification,
> +     are permitted in any medium without royalty provided the copyright
> +     notice and this notice are preserved.  -->
> +
> +<!DOCTYPE feature SYSTEM "gdb-target.dtd">
> +<feature name="org.gnu.gdb.i386.pl3_ssp">
> +  <reg name="pl3_ssp" bitsize="64" type="data_ptr"/>
> +</feature>
> diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
> index 90ff0c5c706..8eb5b4fac86 100644
> --- a/gdb/i386-tdep.c
> +++ b/gdb/i386-tdep.c
> @@ -8403,7 +8403,8 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>    const struct tdesc_feature *feature_core;
>  
>    const struct tdesc_feature *feature_sse, *feature_avx, *feature_avx512,
> -			     *feature_pkeys, *feature_segments;
> +			     *feature_pkeys, *feature_segments,
> +			     *feature_pl3_ssp;
>    int i, num_regs, valid_p;
>  
>    if (! tdesc_has_registers (tdesc))
> @@ -8429,6 +8430,9 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>    /* Try PKEYS  */
>    feature_pkeys = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys");
>  
> +  /* Try Shadow Stack.  */
> +  feature_pl3_ssp = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pl3_ssp");
> +
>    valid_p = 1;
>  
>    /* The XCR0 bits.  */
> @@ -8544,6 +8548,15 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>  					    tdep->pkeys_register_names[i]);
>      }
>  
> +  if (feature_pl3_ssp != nullptr)
> +    {
> +      if (tdep->ssp_regnum < 0)
> +	tdep->ssp_regnum = I386_PL3_SSP_REGNUM;
> +
> +      valid_p &= tdesc_numbered_register (feature_pl3_ssp, tdesc_data,
> +					  tdep->ssp_regnum, "pl3_ssp");
> +    }
> +
>    return valid_p;
>  }
>  
> @@ -8835,6 +8848,9 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    /* No segment base registers.  */
>    tdep->fsbase_regnum = -1;
>  
> +  /* No shadow stack pointer register.  */
> +  tdep->ssp_regnum = -1;
> +
>    tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
>  
>    set_gdbarch_relocate_instruction (gdbarch, i386_relocate_instruction);
> @@ -8955,13 +8971,15 @@ const struct target_desc *
>  i386_target_description (uint64_t xstate_bv_mask, bool segments)
>  {
>    static target_desc *i386_tdescs \
> -    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
> +    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/] \
> +    [2/*segments*/] = {};
>    target_desc **tdesc;
>  
>    tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
>      [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
>      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
>      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
> +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
>      [segments ? 1 : 0];
>  
>    if (*tdesc == NULL)
> diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
> index 239bc8674e8..60d6f3eb732 100644
> --- a/gdb/i386-tdep.h
> +++ b/gdb/i386-tdep.h
> @@ -191,6 +191,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
>    /* PKEYS register names.  */
>    const char * const *pkeys_register_names = nullptr;
>  
> +  /* Shadow stack pointer register.  */
> +  int ssp_regnum = 0;

I know all the other *_regnum fields start as 0, but I really don't
understand why.  Surely starting as -1 would make more sense?  This
isn't a hard requirement, but maybe you see some reason why these fields
should start at 0 that I'm missing?

> +
>    /* Register number for %fsbase.  Set this to -1 to indicate the
>       absence of segment base registers.  */
>    int fsbase_regnum = 0;
> @@ -293,6 +296,7 @@ enum i386_regnum
>    I386_ZMM0H_REGNUM,		/* %zmm0h */
>    I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
>    I386_PKRU_REGNUM,
> +  I386_PL3_SSP_REGNUM,
>    I386_FSBASE_REGNUM,
>    I386_GSBASE_REGNUM
>  };


> diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> new file mode 100644
> index 00000000000..6ddc875b9a3
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> @@ -0,0 +1,50 @@
> +# Copyright 2018-2024 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 amd64-shadow-stack.c

Test source files should be named to match the .exp file.  But in this
case 'amd64-shadow-stack' seems better than 'amd64-ssp', so I'd renamed
the .exp file to amd64-shadow-stack.exp, then this line can be just:

  standard_testfile


> +
> +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 -1
> +    }
> +
> +    if {![runto_main]} {
> +	return -1
> +    }

The 'return -1' can become just 'return'.  The return value doesn't mean anything.

> +
> +    # Read PL3_SSP register.
> +    set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read pl3_ssp value"]
> +
> +    # Write PL3_SSP register.
> +    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"
> +
> +    # Restore original value.
> +    gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main" "restore original pl3_ssp"
> +
> +    # Potential CET violations often only occur after resuming normal execution.
> +    # Therefore, it is important to test normal program continuation after
> +    # configuring the shadow stack pointer.
> +    gdb_continue_to_end

I assume that if we continue with the bogus value in place the inferior
would either give an error or terminate.  Is it worth trying this and
checking that the inferior behaves as expected?

What if, say, the $pl3_ssp value only ever made it as far as the
register cache, and was never actually written back to the inferior?  I
don't think the above test would actually spot this bug, right?

> +}
> +

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-07-25 12:49   ` Andrew Burgess
@ 2025-07-25 15:03     ` Schimpe, Christina
  2025-08-01 12:54       ` Schimpe, Christina
  2025-08-05 13:57       ` Andrew Burgess
  0 siblings, 2 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-25 15:03 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi Andrew,

Thanks a lot for the review! I have some questions for your feedback, please
find my comments below.

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Friday, July 25, 2025 2:50 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow
> stack pointer register.
> 
> Christina Schimpe <christina.schimpe@intel.com> writes:
> 
> > This patch adds the user mode register PL3_SSP which is part of the
> > Intel(R) Control-Flow Enforcement Technology (CET) feature for support
> > of shadow stack.
> > For now, only native and remote debugging support for shadow stack
> > userspace on amd64 linux are covered by this patch including 64 bit
> > and
> > x32 support.  32 bit support is not covered due to missing Linux
> > kernel support.
> >
> > This patch requires fixing the test gdb.base/inline-frame-cycle-unwind
> > which is failing in case the shadow stack pointer is unavailable.
> > Such a state is possible if shadow stack is disabled for the current
> > thread but supported by HW.
> >
> > This test uses the Python unwinder inline-frame-cycle-unwind.py which
> > fakes the cyclic stack cycle by reading the pending frame's registers
> > and adding them to the unwinder:
> >
> > ~~~
> > for reg in pending_frame.architecture().registers("general"):
> >      val = pending_frame.read_register(reg)
> >      unwinder.add_saved_register(reg, val)
> >      return unwinder
> > ~~~
> >
> > However, in case the python unwinder is used we add a register
> > (pl3_ssp) that is unavailable.  This leads to a NOT_AVAILABLE_ERROR
> > caught in gdb/frame-unwind.c:frame_unwind_try_unwinder and it is
> > continued with standard unwinders.  This destroys the faked cyclic
> > behavior and the stack is further unwinded after frame 5.
> >
> > In the working scenario an error should be triggered:
> > ~~~
> > bt
> > 0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
> > 1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > 2  0x000055555555516e in inline_func () at
> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> > 3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > 4  0x000055555555516e in inline_func () at
> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> > 5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > Backtrace stopped: previous frame identical to this frame (corrupt
> > stack?)
> > (gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5:
> > backtrace when the unwind is broken at frame 5 ~~~
> >
> > To fix the Python unwinder, we simply skip the unavailable registers.
> >
> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> > Reviewed-By: Luis Machado <luis.machado@arm.com>
> > ---
> >  gdb/NEWS                                      |  3 +
> >  gdb/amd64-linux-nat.c                         | 17 +++++
> >  gdb/amd64-linux-tdep.c                        |  1 +
> >  gdb/amd64-tdep.c                              |  6 +-
> >  gdb/amd64-tdep.h                              |  1 +
> >  gdb/arch/amd64.c                              | 10 +++
> >  gdb/arch/i386.c                               |  4 ++
> >  gdb/arch/x86-linux-tdesc-features.c           |  1 +
> >  gdb/doc/gdb.texinfo                           |  4 ++
> >  gdb/features/Makefile                         |  2 +
> >  gdb/features/i386/32bit-ssp.c                 | 14 ++++
> >  gdb/features/i386/32bit-ssp.xml               | 11 +++
> >  gdb/features/i386/64bit-ssp.c                 | 14 ++++
> >  gdb/features/i386/64bit-ssp.xml               | 11 +++
> >  gdb/i386-tdep.c                               | 22 +++++-
> >  gdb/i386-tdep.h                               |  4 ++
> >  gdb/nat/x86-linux-tdesc.c                     |  2 +
> >  gdb/nat/x86-linux.c                           | 57 +++++++++++++++
> >  gdb/nat/x86-linux.h                           |  4 ++
> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
> >  gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
> >  .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
> >  gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
> >  gdb/x86-linux-nat.c                           | 49 +++++++++++--
> >  gdb/x86-linux-nat.h                           | 11 +++
> >  gdb/x86-tdep.c                                | 21 ++++++
> >  gdb/x86-tdep.h                                |  9 +++
> >  gdbserver/linux-x86-low.cc                    | 28 +++++++-
> >  gdbsupport/x86-xstate.h                       |  5 +-
> >  29 files changed, 447 insertions(+), 10 deletions(-)  create mode
> > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
> > gdb/features/i386/32bit-ssp.xml  create mode 100644
> > gdb/features/i386/64bit-ssp.c  create mode 100644
> > gdb/features/i386/64bit-ssp.xml  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack.c
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> >
> 
> 
> > diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index
> > dbb9b3223cb..4df99ccca54 100644
> > --- a/gdb/amd64-linux-nat.c
> > +++ b/gdb/amd64-linux-nat.c
> > @@ -32,6 +32,7 @@
> >  #include "amd64-tdep.h"
> >  #include "amd64-linux-tdep.h"
> >  #include "i386-linux-tdep.h"
> > +#include "x86-tdep.h"
> >  #include "gdbsupport/x86-xstate.h"
> >
> >  #include "x86-linux-nat.h"
> > @@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers (struct
> > regcache *regcache, int regnum)
> >
> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
> >  	{
> > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
> > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
> 
> It's really nit-picking, but I don't think the '>' here check is correct.  You're
> checking that tdep->ssp_regnum has been assigned a value, right?  And it's
> default value is -1, so, shouldn't the check really be '>= 0' or '!= -1' maybe?
>
> The same pattern is repeated below in ::store_registers.
> 
> Ahh, reading further on, I see that (not just you), but all the *_regnum fields
> in i386_gdbarch_tdep start set to 0 (which is a valid register number), but are
> set to -1 if the feature is not found.  Maybe I'm missing something incredibly
> clever about this ... but I suspect this code is just showing its age a little.  I
> still think the validity check should be against -1.

Yes I agree, it seems to me that there is something inconsistent in GDB.

The current default value is 0, we set all regnums to 0 initially in gdb/i386-tdep.h
And we set it to -1 to indicate the absence of that specific register.

But on the other hand the enums (enum amd64_regnum/enum i386_regnum) defining
the register numbers start at 0.

So that seems to be a separate issue one could consider to fix.
Maybe the default should be simply -1, instead of 0 ?

But for my specific patch here the best would be to compare against against -1, I agree and will fix.

> > +	    {
> > +	      x86_linux_fetch_ssp (regcache, tid);
> > +	      if (regnum != -1)
> > +		return;
> > +	    }
> > +
> >  	  /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
> >  	     "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure on
> >  	     Intel Skylake CPUs") that sometimes causes the mxcsr location
> > in @@ -302,6 +311,14 @@ amd64_linux_nat_target::store_registers (struct
> regcache *regcache, int regnum)
> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
> >  	{
> >  	  gdb::byte_vector xstateregs (tdep->xsave_layout.sizeof_xsave);
> > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
> > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
> > +	    {
> > +	      x86_linux_store_ssp (regcache, tid);
> > +	      if (regnum != -1)
> > +		return;
> > +	    }
> > +
> >  	  struct iovec iov;
> >
> >  	  iov.iov_base = xstateregs.data ();
> 
> 
> > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index
> > 4ef640698bd..0881ac4aee5 100644
> > --- a/gdb/doc/gdb.texinfo
> > +++ b/gdb/doc/gdb.texinfo
> > @@ -49959,6 +49959,10 @@ The @samp{org.gnu.gdb.i386.pkeys} feature
> is
> > optional.  It should  describe a single register, @samp{pkru}.  It is
> > a 32-bit register  valid for i386 and amd64.
> >
> > +The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
> > +describe the user mode register @samp{pl3_ssp} which has 64 bits on
> > +amd64.  Following the restriction of the Linux kernel, only amd64 is
> supported for now.
> 
> You mention a couple of times in this patch that the shadow stack is only
> supported (for now) on amd64 (& x32), but you have added an i386 version
> of the org.gnu.gdb.i386.pl3_ssp feature with a 32-bit pl3_ssp register.

We need the 32-bit pl3_ssp register for x32, since for x32 the shadow stack pointer has 32 bits only.

> This feature is (as far as I can tell) instantiated for 32-bit targets, which means
> gdbserver will send out target descriptions containing this feature.
> 
> I think either (a) you should drop the i386 xml file and feature, or (b) my
> preferred choice, document the i386 version here.  It's still fine to say that
> the 32-bit (i386) register is ignored by GDB for now though.  I had a go at
> rewording the paragraph, but I'm not 100% sure its OK yet:
> 
>   The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
>   describe the user mode register @samp{pl3_ssp} which has 64 bits on
>   amd64 and 32 bits on i386.  Following the restriction of the Linux
>   kernel, only GDB for amd64 targets makes use of this feature for now.

I missed to describe x32 here, actually. So I'd write it as follows:

The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
describe the user mode register @samp{pl3_ssp} which has 64 bits on
amd64, 32 bits on amd64 with  32-bit pointer size (X32) and 32 bits on i386. 
Following the restriction of the Linux kernel, only GDB for amd64 targets makes
use of this feature for now.

What do you think?

> 
> > +
> >  @node LoongArch Features
> >  @subsection LoongArch Features
> >  @cindex target descriptions, LoongArch Features diff --git
> > a/gdb/features/Makefile b/gdb/features/Makefile index
> > 7a8c7999733..2afda1ccd00 100644
> > --- a/gdb/features/Makefile
> > +++ b/gdb/features/Makefile
> > @@ -225,6 +225,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
> >  	i386/32bit-avx.xml \
> >  	i386/32bit-avx512.xml \
> >  	i386/32bit-segments.xml \
> > +	i386/32bit-ssp.xml \
> >  	i386/64bit-avx512.xml \
> >  	i386/64bit-core.xml \
> >  	i386/64bit-segments.xml \
> > @@ -232,6 +233,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
> >  	i386/64bit-linux.xml \
> >  	i386/64bit-sse.xml \
> >  	i386/pkeys.xml \
> > +	i386/64bit-ssp.xml \
> >  	i386/x32-core.xml \
> >  	loongarch/base32.xml \
> >  	loongarch/base64.xml \
> > diff --git a/gdb/features/i386/32bit-ssp.c
> > b/gdb/features/i386/32bit-ssp.c new file mode 100644 index
> > 00000000000..991bae3c1e6
> > --- /dev/null
> > +++ b/gdb/features/i386/32bit-ssp.c
> > @@ -0,0 +1,14 @@
> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> > +  Original: 32bit-ssp.xml */
> > +
> > +#include "gdbsupport/tdesc.h"
> > +
> > +static int
> > +create_feature_i386_32bit_ssp (struct target_desc *result, long
> > +regnum) {
> > +  struct tdesc_feature *feature;
> > +
> > +  feature = tdesc_create_feature (result,
> > +"org.gnu.gdb.i386.pl3_ssp");
> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 32,
> > +"data_ptr");
> > +  return regnum;
> > +}
> > diff --git a/gdb/features/i386/32bit-ssp.xml
> > b/gdb/features/i386/32bit-ssp.xml new file mode 100644 index
> > 00000000000..d17e7004eec
> > --- /dev/null
> > +++ b/gdb/features/i386/32bit-ssp.xml
> > @@ -0,0 +1,11 @@
> > +<?xml version="1.0"?>
> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> > +
> > +     Copying and distribution of this file, with or without modification,
> > +     are permitted in any medium without royalty provided the copyright
> > +     notice and this notice are preserved.  -->
> > +
> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
> > +name="org.gnu.gdb.i386.pl3_ssp">
> > +  <reg name="pl3_ssp" bitsize="32" type="data_ptr"/> </feature>
> > diff --git a/gdb/features/i386/64bit-ssp.c
> > b/gdb/features/i386/64bit-ssp.c new file mode 100644 index
> > 00000000000..5468099ddf6
> > --- /dev/null
> > +++ b/gdb/features/i386/64bit-ssp.c
> > @@ -0,0 +1,14 @@
> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> > +  Original: 64bit-ssp.xml */
> > +
> > +#include "gdbsupport/tdesc.h"
> > +
> > +static int
> > +create_feature_i386_64bit_ssp (struct target_desc *result, long
> > +regnum) {
> > +  struct tdesc_feature *feature;
> > +
> > +  feature = tdesc_create_feature (result,
> > +"org.gnu.gdb.i386.pl3_ssp");
> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 64,
> > +"data_ptr");
> > +  return regnum;
> > +}
> > diff --git a/gdb/features/i386/64bit-ssp.xml
> > b/gdb/features/i386/64bit-ssp.xml new file mode 100644 index
> > 00000000000..a0688d018a5
> > --- /dev/null
> > +++ b/gdb/features/i386/64bit-ssp.xml
> > @@ -0,0 +1,11 @@
> > +<?xml version="1.0"?>
> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> > +
> > +     Copying and distribution of this file, with or without modification,
> > +     are permitted in any medium without royalty provided the copyright
> > +     notice and this notice are preserved.  -->
> > +
> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
> > +name="org.gnu.gdb.i386.pl3_ssp">
> > +  <reg name="pl3_ssp" bitsize="64" type="data_ptr"/> </feature>
> > diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index
> > 90ff0c5c706..8eb5b4fac86 100644
> > --- a/gdb/i386-tdep.c
> > +++ b/gdb/i386-tdep.c
> > @@ -8403,7 +8403,8 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
> >    const struct tdesc_feature *feature_core;
> >
> >    const struct tdesc_feature *feature_sse, *feature_avx, *feature_avx512,
> > -			     *feature_pkeys, *feature_segments;
> > +			     *feature_pkeys, *feature_segments,
> > +			     *feature_pl3_ssp;
> >    int i, num_regs, valid_p;
> >
> >    if (! tdesc_has_registers (tdesc))
> > @@ -8429,6 +8430,9 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
> >    /* Try PKEYS  */
> >    feature_pkeys = tdesc_find_feature (tdesc,
> > "org.gnu.gdb.i386.pkeys");
> >
> > +  /* Try Shadow Stack.  */
> > +  feature_pl3_ssp = tdesc_find_feature (tdesc,
> > + "org.gnu.gdb.i386.pl3_ssp");
> > +
> >    valid_p = 1;
> >
> >    /* The XCR0 bits.  */
> > @@ -8544,6 +8548,15 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
> *tdep,
> >  					    tdep->pkeys_register_names[i]);
> >      }
> >
> > +  if (feature_pl3_ssp != nullptr)
> > +    {
> > +      if (tdep->ssp_regnum < 0)
> > +	tdep->ssp_regnum = I386_PL3_SSP_REGNUM;
> > +
> > +      valid_p &= tdesc_numbered_register (feature_pl3_ssp, tdesc_data,
> > +					  tdep->ssp_regnum, "pl3_ssp");
> > +    }
> > +
> >    return valid_p;
> >  }
> >
> > @@ -8835,6 +8848,9 @@ i386_gdbarch_init (struct gdbarch_info info,
> struct gdbarch_list *arches)
> >    /* No segment base registers.  */
> >    tdep->fsbase_regnum = -1;
> >
> > +  /* No shadow stack pointer register.  */  tdep->ssp_regnum = -1;
> > +
> >    tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
> >
> >    set_gdbarch_relocate_instruction (gdbarch,
> > i386_relocate_instruction); @@ -8955,13 +8971,15 @@ const struct
> > target_desc *  i386_target_description (uint64_t xstate_bv_mask, bool
> > segments)  {
> >    static target_desc *i386_tdescs \
> > -    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
> > +    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/] \
> > +    [2/*segments*/] = {};
> >    target_desc **tdesc;
> >
> >    tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
> >      [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
> >      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
> >      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
> > +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
> >      [segments ? 1 : 0];
> >
> >    if (*tdesc == NULL)
> > diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index
> > 239bc8674e8..60d6f3eb732 100644
> > --- a/gdb/i386-tdep.h
> > +++ b/gdb/i386-tdep.h
> > @@ -191,6 +191,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
> >    /* PKEYS register names.  */
> >    const char * const *pkeys_register_names = nullptr;
> >
> > +  /* Shadow stack pointer register.  */  int ssp_regnum = 0;
> 
> I know all the other *_regnum fields start as 0, but I really don't understand
> why.  Surely starting as -1 would make more sense?  This isn't a hard
> requirement, but maybe you see some reason why these fields should start
> at 0 that I'm missing?

I agree. As stated before setting the regnums to -1 here by default would make
more sense to me. So I think I should set ssp_regnum here to -1.
I'd do that, unless I see some issues with that when testing of course.

The other registers set to 0 here could be fixed separately.

Does that sound reasonable?

> > +
> >    /* Register number for %fsbase.  Set this to -1 to indicate the
> >       absence of segment base registers.  */
> >    int fsbase_regnum = 0;
> > @@ -293,6 +296,7 @@ enum i386_regnum
> >    I386_ZMM0H_REGNUM,		/* %zmm0h */
> >    I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
> >    I386_PKRU_REGNUM,
> > +  I386_PL3_SSP_REGNUM,
> >    I386_FSBASE_REGNUM,
> >    I386_GSBASE_REGNUM
> >  };
> 
> 
> > diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp
> > b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> > new file mode 100644
> > index 00000000000..6ddc875b9a3
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> > @@ -0,0 +1,50 @@
> > +# Copyright 2018-2024 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 amd64-shadow-stack.c
> 
> Test source files should be named to match the .exp file.  But in this case
> 'amd64-shadow-stack' seems better than 'amd64-ssp', so I'd renamed the
> .exp file to amd64-shadow-stack.exp, then this line can be just:
> 
>   standard_testfile

Yes, I agree and will fix.

> > +
> > +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 -1
> > +    }
> > +
> > +    if {![runto_main]} {
> > +	return -1
> > +    }
> 
> The 'return -1' can become just 'return'.  The return value doesn't mean
> anything.

True will fix (also in following patches, that have the same for the test files).

> > +
> > +    # Read PL3_SSP register.
> > +    set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read pl3_ssp
> > + value"]
> > +
> > +    # Write PL3_SSP register.
> > +    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"
> > +
> > +    # Restore original value.
> > +    gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main" "restore
> original pl3_ssp"
> > +
> > +    # Potential CET violations often only occur after resuming normal
> execution.
> > +    # Therefore, it is important to test normal program continuation after
> > +    # configuring the shadow stack pointer.
> > +    gdb_continue_to_end
> 
> I assume that if we continue with the bogus value in place the inferior would
> either give an error or terminate.  Is it worth trying this and checking that the
> inferior behaves as expected?

If we don't reset the shadow stack pointer to it's original value we will see a SEGV. 
Dependent on the address of the wrong shadow stack pointer it's either a SEGV
with si code that points to a control flow protection fault or a different si code.

So if I stay in a valid address range for configuring pl3_ssp but don't restore the original value
I'll see a control flow protection exception:

[...]
breakpoint 1, 0x0000555555555148 in main ()^M
(gdb) print /x $pl3_ssp^M
$1 = 0x7ffff7bfffe8^M
(gdb) PASS: gdb.arch/amd64-ssp.exp: get hexadecimal valueof "$pl3_ssp"
print /x $pl3_ssp = 0x7ffff7bfffe0^M
$2 = 0x7ffff7bfffe0^M
(gdb) PASS: gdb.arch/amd64-ssp.exp: set pl3_ssp value
print /x $pl3_ssp^M
$3 = 0x7ffff7bfffe0^M
(gdb) PASS: gdb.arch/amd64-ssp.exp: read pl3_ssp value after setting
continue^M
Continuing.^M
^M
Program received signal SIGSEGV, Segmentation fault.^M
0x0000555555555158 in main ()^M
(gdb) FAIL: gdb.arch/amd64-ssp.exp: continue until exit

Siginfo shows si_code = 10, which indicates a control protection fault.

p $_siginfo^M
$4 = {si_signo = 11, si_errno = 0, si_code = 10, [...]

If I set the value of pl3_ssp as in the current test (0x12345678) I'll see a different SEGV actually

p $_siginfo
$4 = {si_signo = 11, si_errno = 0, si_code = 1, [...]

> 
> What if, say, the $pl3_ssp value only ever made it as far as the register cache,
> and was never actually written back to the inferior?  I don't think the above
> test would actually spot this bug, right?

Hm, if I understand you correctly here and you mean the scenario as shown
above the above test would spot this bug I think (as we saw a fail).

Does my example above show what you described or do you mean a different scenario?

Regards
Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-23 12:45                 ` Schimpe, Christina
@ 2025-07-28 17:05                   ` Luis Machado
  2025-07-28 17:20                     ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Luis Machado @ 2025-07-28 17:05 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches; +Cc: thiago.bauermann, Andrew Burgess

Hi Christina,

Sorry, only spotted this now.

On 7/23/25 13:45, Schimpe, Christina wrote:
> Hi Luis,
> 
> I finally also tested x32 and it looks good, there was only one issue
> with the new coredump test due to RDSSPD/RDSSPQ instructions
> for reading the shadow stack pointer:
> amd64-shadow-stack-corefile.c:26: Error: operand size mismatch for `rdsspq'
> 
> So I have to adapt the instruction for x32 to "rdsspd " to copy low 32 bits only.
> 
> I suggest to fix the testfile as follows:
> ~~~
> diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> index f078e33810d..5e84793ccb1 100644
> --- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> @@ -23,7 +23,11 @@ void
>  function ()
>  {
>    unsigned long ssp;
> -  asm volatile("xor %0, %0; rdsspq %0" : "=r" (ssp));
> +  #ifndef __ILP32__
> +    asm volatile ("xor %0, %0; rdsspq %0" : "=r" (ssp));
> +  #else
> +    asm volatile ("xor %0, %0; rdsspd %0" : "=r" (ssp));
> +  #endif
> ~~~
> 
> Would that be fine ore should I repost the patch?
> 
> Other than that I'll retest again with latest upstream master and push this tomorrow.
> 

That's fine by me. I see Andrew had some comments on your series. Are those addressed locally now or are you still working on them?

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-28 17:05                   ` Luis Machado
@ 2025-07-28 17:20                     ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-28 17:20 UTC (permalink / raw)
  To: Luis Machado, gdb-patches; +Cc: thiago.bauermann, Andrew Burgess

Hi Luis,

> -----Original Message-----
> From: Luis Machado <luis.machado@arm.com>
> Sent: Monday, July 28, 2025 7:06 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; Andrew Burgess <aburgess@redhat.com>
> Subject: Re: [PATCH v5 00/12] Add CET shadow stack support
> 
> Hi Christina,
> 
> Sorry, only spotted this now.
> 
> On 7/23/25 13:45, Schimpe, Christina wrote:
> > Hi Luis,
> >
> > I finally also tested x32 and it looks good, there was only one issue
> > with the new coredump test due to RDSSPD/RDSSPQ instructions for
> > reading the shadow stack pointer:
> > amd64-shadow-stack-corefile.c:26: Error: operand size mismatch for
> `rdsspq'
> >
> > So I have to adapt the instruction for x32 to "rdsspd " to copy low 32 bits
> only.
> >
> > I suggest to fix the testfile as follows:
> > ~~~
> > diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> > b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> > index f078e33810d..5e84793ccb1 100644
> > --- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> > +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> > @@ -23,7 +23,11 @@ void
> >  function ()
> >  {
> >    unsigned long ssp;
> > -  asm volatile("xor %0, %0; rdsspq %0" : "=r" (ssp));
> > +  #ifndef __ILP32__
> > +    asm volatile ("xor %0, %0; rdsspq %0" : "=r" (ssp));  #else
> > +    asm volatile ("xor %0, %0; rdsspd %0" : "=r" (ssp));  #endif
> > ~~~
> >
> > Would that be fine ore should I repost the patch?
> >
> > Other than that I'll retest again with latest upstream master and push this
> tomorrow.
> >
> 
> That's fine by me. I see Andrew had some comments on your series. Are
> those addressed locally now or are you still working on them?

Thank you for the feedback.

I replied and had some questions, so I am waiting for Andrew's feedback. 
Now I think it would make sense to post a new version actually,
as Andrew's feedback addresses not only testcode or comments.
But I thought I'd wait a bit until everything is clear before doing that.

Just FYI, I also had a short discussion with Thiago regarding GDB 17 release:
https://sourceware.org/pipermail/gdb-patches/2025-July/219463.html

Regards,
Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-06-28  8:28 ` [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register Christina Schimpe
  2025-07-25 12:49   ` Andrew Burgess
@ 2025-07-29 13:51   ` Andrew Burgess
  2025-08-01 12:40     ` Schimpe, Christina
  2025-08-10 19:01   ` H.J. Lu
  2 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-07-29 13:51 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> diff --git a/gdb/x86-tdep.c b/gdb/x86-tdep.c
> index 6646b1157c8..06dec4e1f90 100644
> --- a/gdb/x86-tdep.c
> +++ b/gdb/x86-tdep.c
> @@ -17,10 +17,31 @@
>     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 "i386-tdep.h"
>  #include "x86-tdep.h"
>  #include "symtab.h"
>  
>  
> +/* See x86-tdep.h.  */
> +
> +void
> +x86_supply_ssp (regcache *regcache, const uint64_t ssp)
> +{
> +  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (regcache->arch ());
> +  gdb_assert (tdep != nullptr && tdep->ssp_regnum > 0);
> +  regcache->raw_supply (tdep->ssp_regnum, &ssp);
> +}
> +
> +/* See x86-tdep.h.  */
> +
> +void
> +x86_collect_ssp (const regcache *regcache, uint64_t& ssp)

While reviewing the next patch, I spotted a white space issue here, it
should be 'uint64_t &ssp'.  And the same in the declaration too.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-06-28  8:28 ` [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack Christina Schimpe
@ 2025-07-29 14:46   ` Andrew Burgess
  2025-07-30  1:55     ` Thiago Jung Bauermann
  2025-08-04 12:45     ` Schimpe, Christina
  0 siblings, 2 replies; 67+ messages in thread
From: Andrew Burgess @ 2025-07-29 14:46 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> Intel's Control-Flow Enforcement Technology (CET) provides the shadow
> stack feature for the x86 architecture.
>
> This commit adds support to write and read the shadow-stack node in
> corefiles.  This helps debugging return address violations post-mortem.
> The format is synced with the linux kernel commit "x86: Add PTRACE
> interface for shadow stack".  As the linux kernel restricts shadow
> stack support to 64-bit, apply the fix for amd64 only.
>
> Co-Authored-By: Christina Schimpe <christina.schimpe@intel.com>
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> ---
>  gdb/amd64-linux-tdep.c                        |  57 ++++++++-
>  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 +++++++
>  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 ++++++++++++++++++
>  3 files changed, 205 insertions(+), 4 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
>
> diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
> index edb7d8da6ab..9af7a41ea26 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -47,6 +47,7 @@
>  #include "expop.h"
>  #include "arch/amd64-linux-tdesc.h"
>  #include "inferior.h"
> +#include "x86-tdep.h"
>  
>  /* The syscall's XML filename for i386.  */
>  #define XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
> @@ -1593,6 +1594,14 @@ amd64_linux_record_signal (struct gdbarch *gdbarch,
>    return 0;
>  }
>  
> +/* Get shadow stack pointer state from core dump.  */
> +
> +static bool
> +amd64_linux_core_read_ssp_state_p (bfd *abfd)
> +{
> +  return bfd_get_section_by_name (abfd, ".reg-ssp") != nullptr;

I think the comment on this function is not correct.  We're not getting
the shadow stack pointer state, we're:

  /* Return true if the core file ABFD contains shadow stack pointer
     state.  Otherwise, return false.  */


> +}
> +
>  /* Get Linux/x86 target description from core dump.  */
>  
>  static const struct target_desc *
> @@ -1602,11 +1611,14 @@ amd64_linux_core_read_description (struct gdbarch *gdbarch,
>  {
>    /* Linux/x86-64.  */
>    x86_xsave_layout layout;
> -  uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
> -  if (xcr0 == 0)
> -    xcr0 = X86_XSTATE_SSE_MASK;
> +  uint64_t xstate_bv_mask = i386_linux_core_read_xsave_info (abfd, layout);

As with feedback on earlier patches, I don't think the inclusion of
'_mask' is a good one here.  I think what you call xstate_bv_mask is an
actual value, not a mask, and should be named 'xstate_bv'

> +  if (xstate_bv_mask == 0)
> +    xstate_bv_mask = X86_XSTATE_SSE_MASK;
> +
> +  if (amd64_linux_core_read_ssp_state_p (abfd))
> +    xstate_bv_mask |= X86_XSTATE_CET_U;
>  
> -  return amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
> +  return amd64_linux_read_description (xstate_bv_mask & X86_XSTATE_ALL_MASK,
>  				       gdbarch_ptr_bit (gdbarch) == 32);
>  }
>  
> @@ -1637,6 +1649,35 @@ static const struct regset amd64_linux_xstateregset =
>      amd64_linux_collect_xstateregset
>    };
>  
> +/* Supply shadow stack pointer register from SSP to the register cache
> +   REGCACHE.  */
> +
> +static void
> +amd64_linux_supply_ssp (const regset *regset,
> +			regcache *regcache, int regnum,
> +			const void *ssp, size_t len)
> +{
> +  x86_supply_ssp (regcache, *static_cast<const uint64_t *> (ssp));

Before this line I'd like to see:

  gdb_assert (len == sizeof (uint64_t));

> +}
> +
> +/* Collect the shadow stack pointer register from the register cache
> +   REGCACHE and store it in SSP.  */
> +
> +static void
> +amd64_linux_collect_ssp (const regset *regset,
> +			 const regcache *regcache, int regnum,
> +			 void *ssp, size_t len)
> +{
> +  x86_collect_ssp (regcache, *static_cast<uint64_t *> (ssp));

And I think this should get the same 'len == sizeof ...' assert as
above please.

> +}
> +
> +/* Shadow stack pointer register.  */
> +
> +static const struct regset amd64_linux_ssp_register
> +  {
> +    NULL, amd64_linux_supply_ssp, amd64_linux_collect_ssp
> +  };
> +
>  /* Iterate over core file register note sections.  */
>  
>  static void
> @@ -1653,6 +1694,14 @@ amd64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
>      cb (".reg-xstate", tdep->xsave_layout.sizeof_xsave,
>  	tdep->xsave_layout.sizeof_xsave, &amd64_linux_xstateregset,
>  	"XSAVE extended state", cb_data);
> +
> +  /* SSP can be unavailable.  Thus, we need to check the register status
> +     in case we write a core file (regcache != nullptr).  */
> +  if (tdep->ssp_regnum > 0
> +      && (regcache == nullptr

Have you really seen this function called with 'regcache == nullptr' ?
I'm suspect not as e.g. i386_supply_gregset, which is part of
i386_gregset, makes an unchecked dereference of regcache, so if regcache
was nullptr you'd see UB (crash) before getting to this check.

> +	  || REG_VALID == regcache->get_register_status (tdep->ssp_regnum)))
> +    cb (".reg-ssp", 8, 8, &amd64_linux_ssp_register,
> +	"shadow stack pointer", cb_data);
>  }
>  
>  /* The instruction sequences used in x86_64 machines for a
> 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 00000000000..f078e33810d
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> @@ -0,0 +1,42 @@
> +/* 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;
> +  asm volatile("xor %0, %0; rdsspq %0" : "=r" (ssp));
> +
> +  /* 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 00000000000..8784fc3622c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
> @@ -0,0 +1,110 @@
> +# Copyright 2021-2024 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.
> +
> +require allow_ssp_tests
> +
> +standard_testfile
> +
> +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
> +    }
> +
> +    # 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"]
> +
> +    # 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 != ""}]
> +    set os_core_name "${binfile}.core"
> +    remote_exec build "mv $core_filename $os_core_name"
> +    set core_filename $os_core_name

I'm wondering what the point of this core_filename / os_core_name stuff
is?  My reading of `core_find` is that the returned core_filename will
be '${binfile}.core', so this whole thing feels redundant, but maybe I'm
missing something here?

> +
> +    # 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.
> +
> +    if {$gcore_generated} {
> +	clean_restart $binfile
> +
> +	with_test_prefix "gcore corefile" {
> +	    check_core_file $gcore_filename $ssp_in_gcore
> +	}
> +    } else {
> +	fail "gcore corefile not generated"

It's better, where possible, to avoid having pass/fail results that only
show up down some code paths.

In this case it's easy to avoid having a stray 'fail' by restructuring
the code too:

  gdb_assert { $gcore_generated } "gcore corefile created"
  if { $gcore_generated } {
    ... etc ...
  }

Now you'll always have either a pass or fail based on the gcore being
generated.

There is also the helper proc `gcore_cmd_available`.  I'd guess for any
x86 target that supports SSP, gcore will be available, but in theory you
could consider using this to avoid a fail when gcore is not available
maybe?

> +    }
> +
> +    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

I'd move the blank line after the 'close' personally.

Thanks,
Andrew


> +	}
> +    } else {
> +	untested "OS corefile not generated"
> +    }
> +}
> -- 
> 2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-07-29 14:46   ` Andrew Burgess
@ 2025-07-30  1:55     ` Thiago Jung Bauermann
  2025-07-30 11:42       ` Schimpe, Christina
  2025-08-04 12:45     ` Schimpe, Christina
  1 sibling, 1 reply; 67+ messages in thread
From: Thiago Jung Bauermann @ 2025-07-30  1:55 UTC (permalink / raw)
  To: Andrew Burgess; +Cc: Christina Schimpe, gdb-patches, luis.machado

Hello Andrew,

Christina based the testcase in this patch on aarch64-gcs-core.exp from
my GCS patch series and your comments on it also apply to my patch, so
I'm replying on account of that.

Andrew Burgess <aburgess@redhat.com> writes:

> Christina Schimpe <christina.schimpe@intel.com> writes:
>
>> +    # 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.  Save test program output to
>> +    # ${binfile}.out.
>> +    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
>
> I'm wondering what the point of this core_filename / os_core_name stuff
> is?  My reading of `core_find` is that the returned core_filename will
> be '${binfile}.core', so this whole thing feels redundant, but maybe I'm
> missing something here?

I wrote this part. The answer to your question is very simple: I don't
know what I was thinking. I just deleted the last 3 lines from the GCS
testcase.

>> +
>> +    # 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.
>> +
>> +    if {$gcore_generated} {
>> +	clean_restart $binfile
>> +
>> +	with_test_prefix "gcore corefile" {
>> +	    check_core_file $gcore_filename $ssp_in_gcore
>> +	}
>> +    } else {
>> +	fail "gcore corefile not generated"
>
> It's better, where possible, to avoid having pass/fail results that only
> show up down some code paths.
>
> In this case it's easy to avoid having a stray 'fail' by restructuring
> the code too:
>
>   gdb_assert { $gcore_generated } "gcore corefile created"
>   if { $gcore_generated } {
>     ... etc ...
>   }
>
> Now you'll always have either a pass or fail based on the gcore being
> generated.

Good idea. I did that for aarch64-gcs-core.exp.

> There is also the helper proc `gcore_cmd_available`.  I'd guess for any
> x86 target that supports SSP, gcore will be available, but in theory you
> could consider using this to avoid a fail when gcore is not available
> maybe?

In the GCS core testcase, I moved the gcore tests after the OS corefile
ones, with an

  if ![gcore_cmd_available] {
      return
  }

in the middle, and after that I had to redo some GDB setup:

  clean_restart $binfile

  if ![runto $linespec] {
      return
  }

But I agree it does make it more resilient/correct. Thanks for the
suggestion.

>> +    }
>> +
>> +    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
>
> I'd move the blank line after the 'close' personally.

I also had that layout. Changed.

-- 
Thiago

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux.
  2025-06-28  8:28 ` [PATCH v5 08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux Christina Schimpe
@ 2025-07-30  9:58   ` Andrew Burgess
  2025-07-30 12:06     ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-07-30  9:58 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> Unwind the $pl3_ssp register.
> We now have an updated value for the shadow stack pointer when
> moving up or down the frame level.  Note that $pl3_ssp can
> become unavailable when moving to a frame before the shadow
> stack enablement.  In the example below, shadow stack is enabled
> in the function 'call1'.  Thus, when moving to a frame level above
> the function, $pl3_ssp will become unavaiable.
> Following the restriction of the linux kernel, implement the unwinding
> for amd64 linux only.
>
> Before this patch:
> ~~~
> Breakpoint 1, call2 (j=3) at sample.c:44
> 44	  return 42;
> (gdb) p $pl3_ssp
> $1 = (void *) 0x7ffff79ffff8
> (gdb) up
> 55	  call2 (3);
> (gdb) p $pl3_ssp
> $2 = (void *) 0x7ffff79ffff8
> (gdb) up
> 68	  call1 (43);
> (gdb) p $pl3_ssp
> $3 = (void *) 0x7ffff79ffff8
> ~~~
>
> After this patch:
> ~~~
> Breakpoint 1, call2 (j=3) at sample.c:44
> 44	  return 42;
> (gdb) p $pl3_ssp
> $1 = (void *) 0x7ffff79ffff8
> (gdb) up
> 55	  call2 (3);
> (gdb) p $pl3_ssp
> $2 = (void *) 0x7ffff7a00000
> (gdb) up
> 68	  call1 (43i);
> (gdb) p $pl3_ssp
> $3 = <unavailable>
> ~~~
>
> As we now have an updated value for each selected frame, the
> return command is now enabled for shadow stack enabled programs, too.
>
> We therefore add a test for the return command and shadow stack support,
> and for an updated shadow stack pointer after a frame level change.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Reviewed-By: Luis Machado <luis.machado@arm.com>
> ---
>  gdb/amd64-linux-tdep.c                        | 85 ++++++++++++++++++
>  gdb/linux-tdep.c                              | 47 ++++++++++
>  gdb/linux-tdep.h                              |  7 ++
>  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 88 +++++++++++++++++++
>  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 13 +++
>  5 files changed, 240 insertions(+)
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
>

> 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 00000000000..17f32ce3964
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> @@ -0,0 +1,88 @@
> +# Copyright 2018-2024 Free Software Foundation, Inc.

Is this date range correct?  Or a copy & paste error?  The start date
should be when the patches were first posted to the list, or otherwise
made publicly available (e.g. Intel specific GDB release?).  The end
date should be updated to 2025.

With that fixed:

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew

> +
> +# 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.
> +#


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 09/12] gdb, gdbarch: Enable inferior calls for shadow stack support.
  2025-06-28  8:28 ` [PATCH v5 09/12] gdb, gdbarch: Enable inferior calls for shadow stack support Christina Schimpe
@ 2025-07-30 10:42   ` Andrew Burgess
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Burgess @ 2025-07-30 10:42 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> Inferior calls in GDB reset the current PC to the beginning of the function
> that is called.  As no call instruction is executed the new return address
> needs to be pushed to the shadow stack and the shadow stack pointer needs
> to be updated.
>
> This commit adds a new gdbarch method to push an address on the shadow
> stack.  The method is used to adapt the function 'call_function_by_hand_dummy'
> for inferior call shadow stack support.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Reviewed-By: Luis Machado <luis.machado@arm.com>

LGTM.

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew


> ---
>  gdb/gdbarch-gen.c         | 32 ++++++++++++++++++++++++++++++++
>  gdb/gdbarch-gen.h         | 14 ++++++++++++++
>  gdb/gdbarch_components.py | 16 ++++++++++++++++
>  gdb/infcall.c             | 14 ++++++++++----
>  4 files changed, 72 insertions(+), 4 deletions(-)
>
> diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
> index fc570d37a8d..a4b72793fd8 100644
> --- a/gdb/gdbarch-gen.c
> +++ b/gdb/gdbarch-gen.c
> @@ -262,6 +262,7 @@ struct gdbarch
>    gdbarch_read_core_file_mappings_ftype *read_core_file_mappings = default_read_core_file_mappings;
>    gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes;
>    gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context;
> +  gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
>  };
>  
>  /* Create a new ``struct gdbarch'' based on information provided by
> @@ -535,6 +536,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
>    /* Skip verify of read_core_file_mappings, invalid_p == 0.  */
>    /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0.  */
>    /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
> +  /* Skip verify of shadow_stack_push, has predicate.  */
>    if (!log.empty ())
>      internal_error (_("verify_gdbarch: the following are invalid ...%s"),
>  		    log.c_str ());
> @@ -1406,6 +1408,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
>    gdb_printf (file,
>  	      "gdbarch_dump: core_parse_exec_context = <%s>\n",
>  	      host_address_to_string (gdbarch->core_parse_exec_context));
> +  gdb_printf (file,
> +	      "gdbarch_dump: gdbarch_shadow_stack_push_p() = %d\n",
> +	      gdbarch_shadow_stack_push_p (gdbarch));
> +  gdb_printf (file,
> +	      "gdbarch_dump: shadow_stack_push = <%s>\n",
> +	      host_address_to_string (gdbarch->shadow_stack_push));
>    if (gdbarch->dump_tdep != NULL)
>      gdbarch->dump_tdep (gdbarch, file);
>  }
> @@ -5551,3 +5559,27 @@ set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch,
>  {
>    gdbarch->core_parse_exec_context = core_parse_exec_context;
>  }
> +
> +bool
> +gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  return gdbarch->shadow_stack_push != NULL;
> +}
> +
> +void
> +gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  gdb_assert (gdbarch->shadow_stack_push != NULL);
> +  if (gdbarch_debug >= 2)
> +    gdb_printf (gdb_stdlog, "gdbarch_shadow_stack_push called\n");
> +  gdbarch->shadow_stack_push (gdbarch, new_addr, regcache);
> +}
> +
> +void
> +set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
> +			       gdbarch_shadow_stack_push_ftype shadow_stack_push)
> +{
> +  gdbarch->shadow_stack_push = shadow_stack_push;
> +}
> diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
> index 281b97b7aa8..71142332540 100644
> --- a/gdb/gdbarch-gen.h
> +++ b/gdb/gdbarch-gen.h
> @@ -1802,3 +1802,17 @@ extern void set_gdbarch_use_target_description_from_corefile_notes (struct gdbar
>  typedef core_file_exec_context (gdbarch_core_parse_exec_context_ftype) (struct gdbarch *gdbarch, bfd *cbfd);
>  extern core_file_exec_context gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, bfd *cbfd);
>  extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarch_core_parse_exec_context_ftype *core_parse_exec_context);
> +
> +/* Some targets support special hardware-assisted control-flow protection
> +   technologies.  For example, the Intel Control-Flow Enforcement Technology
> +   (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
> +   To enable shadow stack support for inferior calls the shadow_stack_push
> +   gdbarch hook has to be provided.
> +
> +   Push NEW_ADDR to the shadow stack and update the shadow stack pointer. */
> +
> +extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch);
> +
> +typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
> +extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
> +extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push);
> diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
> index 91c867e69bf..abc79588473 100644
> --- a/gdb/gdbarch_components.py
> +++ b/gdb/gdbarch_components.py
> @@ -2848,3 +2848,19 @@ which all assume current_inferior() is the one to read from.
>      predefault="default_core_parse_exec_context",
>      invalid=False,
>  )
> +
> +Method(
> +    comment="""
> +Some targets support special hardware-assisted control-flow protection
> +technologies.  For example, the Intel Control-Flow Enforcement Technology
> +(Intel CET) on x86 provides a shadow stack and indirect branch tracking.
> +To enable shadow stack support for inferior calls the shadow_stack_push
> +gdbarch hook has to be provided.
> +
> +Push NEW_ADDR to the shadow stack and update the shadow stack pointer.
> +""",
> +    type="void",
> +    name="shadow_stack_push",
> +    params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
> +    predicate=True,
> +)
> diff --git a/gdb/infcall.c b/gdb/infcall.c
> index 2b5936d1621..db6d6774367 100644
> --- a/gdb/infcall.c
> +++ b/gdb/infcall.c
> @@ -1448,10 +1448,16 @@ call_function_by_hand_dummy (struct value *function,
>    /* Create the dummy stack frame.  Pass in the call dummy address as,
>       presumably, the ABI code knows where, in the call dummy, the
>       return address should be pointed.  */
> -  sp = gdbarch_push_dummy_call (gdbarch, function,
> -				get_thread_regcache (inferior_thread ()),
> -				bp_addr, args.size (), args.data (),
> -				sp, return_method, struct_addr);
> +  regcache *regcache = get_thread_regcache (inferior_thread ());
> +  sp = gdbarch_push_dummy_call (gdbarch, function, regcache, bp_addr,
> +				args.size (), args.data (), sp,
> +				return_method, struct_addr);
> +
> +  /* Push the return address of the inferior (bp_addr) to the shadow stack
> +     and update the shadow stack pointer.  As we don't execute a call
> +     instruction to call the function we need to handle this manually.  */
> +  if (gdbarch_shadow_stack_push_p (gdbarch))
> +    gdbarch_shadow_stack_push (gdbarch, bp_addr, regcache);
>  
>    /* Set up a frame ID for the dummy frame so we can pass it to
>       set_momentary_breakpoint.  We need to give the breakpoint a frame
> -- 
> 2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-07-30  1:55     ` Thiago Jung Bauermann
@ 2025-07-30 11:42       ` Schimpe, Christina
  2025-08-04 15:28         ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-30 11:42 UTC (permalink / raw)
  To: Thiago Jung Bauermann, Andrew Burgess; +Cc: gdb-patches, luis.machado

Hi Thiago and Andrew, 

Thanks a lot for your feedback. 

I should have mentioned that I created this test based on the test code from Thiago's series GCS
core dump tests. I only mentioned it in the cover letter so far.

I'll do it similar to Thiago when he based his return command testing (aarch64-gcs-return.exp )
on CET shadow stack tests:
https://sourceware.org/pipermail/gdb-patches/2025-June/218895.html

So I'll add a comment in the test description

# Test the shadow stack pointer note in core dumps.
# Based on the corefile tests in gdb.arch/aarch64-gcs-core.exp.

and also a comment in the commit message

    ---
    The code and testcase are lightly adapted from:
    
    [PATCH v3 5/9] GDB, gdbserver: aarch64-linux: Initial Guarded Control Stack support
    
    https://sourceware.org/pipermail/gdb-patches/2025-June/218892.html

in the next version.
Also I'll fix my test based on Andrew's feedback as described by Thiago below.


> -----Original Message-----
> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Sent: Wednesday, July 30, 2025 3:55 AM
> To: Andrew Burgess <aburgess@redhat.com>
> Cc: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with
> shadow stack.
> 
> Hello Andrew,
> 
> Christina based the testcase in this patch on aarch64-gcs-core.exp from my
> GCS patch series and your comments on it also apply to my patch, so I'm
> replying on account of that.
> 
> Andrew Burgess <aburgess@redhat.com> writes:
> 
> > Christina Schimpe <christina.schimpe@intel.com> writes:
> >
> >> +    # 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.  Save test program output to
> >> +    # ${binfile}.out.
> >> +    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
> >
> > I'm wondering what the point of this core_filename / os_core_name
> > stuff is?  My reading of `core_find` is that the returned
> > core_filename will be '${binfile}.core', so this whole thing feels
> > redundant, but maybe I'm missing something here?
> 
> I wrote this part. The answer to your question is very simple: I don't know
> what I was thinking. I just deleted the last 3 lines from the GCS testcase.
> 
> >> +
> >> +    # 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.
> >> +
> >> +    if {$gcore_generated} {
> >> +	clean_restart $binfile
> >> +
> >> +	with_test_prefix "gcore corefile" {
> >> +	    check_core_file $gcore_filename $ssp_in_gcore
> >> +	}
> >> +    } else {
> >> +	fail "gcore corefile not generated"
> >
> > It's better, where possible, to avoid having pass/fail results that
> > only show up down some code paths.
> >
> > In this case it's easy to avoid having a stray 'fail' by restructuring
> > the code too:
> >
> >   gdb_assert { $gcore_generated } "gcore corefile created"
> >   if { $gcore_generated } {
> >     ... etc ...
> >   }
> >
> > Now you'll always have either a pass or fail based on the gcore being
> > generated.
> 
> Good idea. I did that for aarch64-gcs-core.exp.
> 
> > There is also the helper proc `gcore_cmd_available`.  I'd guess for
> > any
> > x86 target that supports SSP, gcore will be available, but in theory
> > you could consider using this to avoid a fail when gcore is not
> > available maybe?
> 
> In the GCS core testcase, I moved the gcore tests after the OS corefile ones,
> with an
> 
>   if ![gcore_cmd_available] {
>       return
>   }
> 
> in the middle, and after that I had to redo some GDB setup:
> 
>   clean_restart $binfile
> 
>   if ![runto $linespec] {
>       return
>   }
> 
> But I agree it does make it more resilient/correct. Thanks for the suggestion.
> 
> >> +    }
> >> +
> >> +    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
> >
> > I'd move the blank line after the 'close' personally.
> 
> I also had that layout. Changed.
> 
> --
> Thiago

Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 10/12] gdb: Implement amd64 linux shadow stack support for inferior calls.
  2025-06-28  8:28 ` [PATCH v5 10/12] gdb: Implement amd64 linux shadow stack support for inferior calls Christina Schimpe
@ 2025-07-30 11:58   ` Andrew Burgess
  2025-07-31 12:32     ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-07-30 11:58 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> This patch enables inferior calls to support Intel's Control-Flow
> Enforcement Technology (CET), which provides the shadow stack feature
> for the x86 architecture.
> Following the restriction of the linux kernel, enable inferior calls
> for amd64 only.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> Reviewed-By: Luis Machado <luis.machado@arm.com>
> ---
>  gdb/amd64-linux-tdep.c                        | 63 +++++++++++++++++++
>  gdb/doc/gdb.texinfo                           | 29 +++++++++
>  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 55 +++++++++++++++-
>  3 files changed, 146 insertions(+), 1 deletion(-)
>
> diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
> index f6ae5395870..899fe2df02c 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -1932,6 +1932,67 @@ amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch)
>    return (binfo->bits_per_word / binfo->bits_per_byte);
>  }
>  
> +/* Read the shadow stack pointer register and return its value, if
> +   possible.  */
> +
> +static std::optional<CORE_ADDR>
> +amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache)
> +{
> +  const i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
> +
> +  if (tdep == nullptr || tdep->ssp_regnum < 0)
> +    return {};

We don't check for 'tdep == nullptr' anywhere else (for x86), so I don't
think this check is needed.  If you _really_ want you could gdb_assert,
but I'm pretty sure you'll have crashed long before now if tdep ==
nullptr.

> +
> +  CORE_ADDR ssp;
> +  if (regcache_raw_read_unsigned (regcache, tdep->ssp_regnum, &ssp)
> +      != REG_VALID)
> +    return {};
> +
> +  /* Dependent on the target in case the shadow stack pointer is
> +     unavailable, the ssp register can be invalid or 0x0 when shadow stack
> +     is supported by HW and the linux kernel but not enabled for the
> +     current thread.  */

Just so I understand, is the 0 coming from actual inferior state?  Or is
the 0 creeping in from somewhere in GDB when we should be creating an
unavailable value, but get it wrong?

Using 0 a magic address value is something I really dislike (having
worked on targets where 0 is a valid address), so they always make me
uncomfortable.  If this is an actual h/w thing then there's nothing we
could or should do about it ... but if this is a GDB thing, then maybe
we can fix that?

> +  if (ssp == 0x0)
> +    return {};
> +
> +  return ssp;
> +}
> +
> +/* If shadow stack is enabled, push the address NEW_ADDR to the shadow
> +   stack and increment the shadow stack pointer accordingly.  */
> +
> +static void
> +amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
> +			       regcache *regcache)
> +{
> +  std::optional<CORE_ADDR> ssp
> +    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache);
> +  if (!ssp.has_value ())
> +    return;
> +
> +  /* The shadow stack grows downwards.  To push addresses to the stack,
> +     we need to decrement SSP.    */
> +  const int element_size
> +    = amd64_linux_shadow_stack_element_size_aligned (gdbarch);
> +  const CORE_ADDR new_ssp = *ssp - element_size;
> +
> +  /* Using /proc/PID/smaps we can only check if NEW_SSP points to shadow
> +     stack memory.  If it doesn't, we assume the stack is full.  */
> +  std::pair<CORE_ADDR, CORE_ADDR> memrange;
> +  if (!linux_address_in_shadow_stack_mem_range (new_ssp, &memrange))
> +    error (_("No space left on the shadow stack."));
> +
> +  /* On x86 there can be a shadow stack token at bit 63.  For x32, the

I don't understand this first sentence and how it relates to either the
rest of the comment, or the following code.

> +     address size is only 32 bit.  Thus, we must use ELEMENT_SIZE (and
> +     not gdbarch_addr_bit) to determine the width of the address to be
> +     written.  */
> +  const bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  write_memory_unsigned_integer (new_ssp, element_size, byte_order,
> +				 (ULONGEST) new_addr);
> +
> +  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
> +  regcache_raw_write_unsigned (regcache, tdep->ssp_regnum, new_ssp);

By this point we know that tdep->ssp_regnum must be a valid regnum.  But
the checks for that are in a separate function, so maybe we should:

  gdb_assert (tdep->ssp_regnum > -1);

> +}
>  
>  /* Implement shadow stack pointer unwinding. For each new shadow stack
>     pointer check if its address is still in the shadow stack memory range.
> @@ -2059,6 +2120,8 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
>  
>    set_gdbarch_remove_non_address_bits_watchpoint
>      (gdbarch, amd64_linux_remove_non_address_bits_watchpoint);
> +
> +  set_gdbarch_shadow_stack_push (gdbarch, amd64_linux_shadow_stack_push);
>    dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);
>  }
>  
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 0881ac4aee5..b5120b78426 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -27037,6 +27037,35 @@ registers
>  
>  @end itemize
>  
> +@subsubsection Intel Control-Flow Enforcement Technology.
> +@cindex Intel Control-Flow Enforcement Technology.
> +
> +The @dfn{Intel Control-Flow Enforcement Technology} (@acronym{Intel CET})
> +provides two capabilities to defend against ``Return-oriented Programming''
> +and ``call/jmp-oriented programming'' style control-flow attacks:
> +
> +@itemize @bullet
> +@item Shadow Stack:
> +A shadow stack is a second stack for a program.  It holds the return
> +addresses pushed by the call instruction.  The @code{RET} instruction pops the
> +return addresses from both call and shadow stack.  If the return addresses from
> +the two stacks do not match, the processor signals a control protection
> +exception.
> +@item Indirect Branch Tracking (IBT):
> +When IBT is enabled, the CPU implements a state machine that tracks indirect
> +@code{JMP} and @code{CALL} instructions.  The state machine can be either IDLE
> +or WAIT_FOR_ENDBRANCH.  In WAIT_FOR_ENDBRANCH state the next instruction in
> +the program stream must be an @code{ENDBR} instruction, otherwise the
> +processor signals a control protection exception.

If I understand it, the IBT doesn't currently have an impact on GDB,
right?  The inferior function being called likely starts with an `endbr`
instruction, but GDB will just leave the IBT mechanism in the IDLE
state, and the endbr will be interpreted as a nop.

I also found this description a little too light on the details.  Just
having the name was enough to go and find the real docs, but I think the
text could be made cleared with two additional sentences:

  @item Indirect Branch Tracking (IBT):
  When IBT is enabled, the CPU implements a state machine that tracks indirect
  @code{JMP} and @code{CALL} instructions.  The state machine can be either IDLE
  or WAIT_FOR_ENDBRANCH.  When a @code{JMP} or @code{CALL} is executed
  the state machine chages to the WAIT_FOR_ENDBRANCH state.  In
  WAIT_FOR_ENDBRANCH state the next instruction in the program stream
  must be an @code{ENDBR} instruction, otherwise the
  processor signals a control protection exception.  After executing a
  @code{ENDBR} instruction the state machine returns to the IDLE state.

This change isn't a hard requirement, but I do think this makes the
description more useful.

> +@end itemize
> +
> +Impact on Call/Print:
> +Inferior calls in @value{GDBN} reset the current PC to the beginning of the
> +function that is called.  No call instruction is executed, but the @code{RET}
> +instruction actually is.  To avoid a control protection exception due to the
> +missing return address on the shadow stack, @value{GDBN} pushes the new return
> +address to the shadow stack and updates the shadow stack pointer.
> +
>  @node Alpha
>  @subsection Alpha
>  
> diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> index 17f32ce3964..622612d2f7d 100644
> --- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> @@ -13,12 +13,29 @@
>  # 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 and the return command.
> +# 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
>  
> +proc restart_and_run_infcall_call2 {} {

There should be a comment before each proc please.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux.
  2025-07-30  9:58   ` Andrew Burgess
@ 2025-07-30 12:06     ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-30 12:06 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi Andrew,

Thanks a lot for the review.

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Wednesday, July 30, 2025 11:59 AM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 08/12] gdb: Handle shadow stack pointer register
> unwinding for amd64 linux.
> 
> Christina Schimpe <christina.schimpe@intel.com> writes:
> 
> > Unwind the $pl3_ssp register.
> > We now have an updated value for the shadow stack pointer when moving
> > up or down the frame level.  Note that $pl3_ssp can become unavailable
> > when moving to a frame before the shadow stack enablement.  In the
> > example below, shadow stack is enabled in the function 'call1'.  Thus,
> > when moving to a frame level above the function, $pl3_ssp will become
> > unavaiable.
> > Following the restriction of the linux kernel, implement the unwinding
> > for amd64 linux only.
> >
> > Before this patch:
> > ~~~
> > Breakpoint 1, call2 (j=3) at sample.c:44
> > 44	  return 42;
> > (gdb) p $pl3_ssp
> > $1 = (void *) 0x7ffff79ffff8
> > (gdb) up
> > 55	  call2 (3);
> > (gdb) p $pl3_ssp
> > $2 = (void *) 0x7ffff79ffff8
> > (gdb) up
> > 68	  call1 (43);
> > (gdb) p $pl3_ssp
> > $3 = (void *) 0x7ffff79ffff8
> > ~~~
> >
> > After this patch:
> > ~~~
> > Breakpoint 1, call2 (j=3) at sample.c:44
> > 44	  return 42;
> > (gdb) p $pl3_ssp
> > $1 = (void *) 0x7ffff79ffff8
> > (gdb) up
> > 55	  call2 (3);
> > (gdb) p $pl3_ssp
> > $2 = (void *) 0x7ffff7a00000
> > (gdb) up
> > 68	  call1 (43i);
> > (gdb) p $pl3_ssp
> > $3 = <unavailable>
> > ~~~
> >
> > As we now have an updated value for each selected frame, the return
> > command is now enabled for shadow stack enabled programs, too.
> >
> > We therefore add a test for the return command and shadow stack
> > support, and for an updated shadow stack pointer after a frame level
> change.
> >
> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> > Reviewed-By: Luis Machado <luis.machado@arm.com>
> > ---
> >  gdb/amd64-linux-tdep.c                        | 85 ++++++++++++++++++
> >  gdb/linux-tdep.c                              | 47 ++++++++++
> >  gdb/linux-tdep.h                              |  7 ++
> >  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 88
> +++++++++++++++++++
> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 13 +++
> >  5 files changed, 240 insertions(+)
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-
> cmds.exp
> >
> 
> > 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 00000000000..17f32ce3964
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> > @@ -0,0 +1,88 @@
> > +# Copyright 2018-2024 Free Software Foundation, Inc.
> 
> Is this date range correct?  Or a copy & paste error?  The start date should
> be when the patches were first posted to the list, or otherwise made
> publicly available (e.g. Intel specific GDB release?).  The end date should be
> updated to 2025.

Yes, it should be 2025 for the end date and I'll use 2024 as start date, as I posted
the series in December 2024 for the first time in this list and the patches made were
 available in the Intel specific GDB release in 2024.

Draft versions were available before 2024, but mostly experimental.

I'll also adapt the copyright in test files of the previous patches in the series, so they'll
all have 2024-2025.
 
> With that fixed:
> 
> Approved-By: Andrew Burgess <aburgess@redhat.com>
> 
> Thanks,
> Andrew
> 
> > +
> > +# 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.
> > +#

Kind Regards,
Christina

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer.
  2025-06-28  8:28 ` [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer Christina Schimpe
@ 2025-07-30 12:22   ` Andrew Burgess
  2025-08-04 13:01     ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-07-30 12:22 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> This patch is required by the following commit
> "gdb: Enable displaced stepping with shadow stack on amd64 linux."
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Reviewed-By: Luis Machado <luis.machado@arm.com>
> ---
>  gdb/arch-utils.c          | 10 ++++++++++
>  gdb/arch-utils.h          |  5 +++++
>  gdb/gdbarch-gen.c         | 22 ++++++++++++++++++++++
>  gdb/gdbarch-gen.h         | 12 +++++++++++-
>  gdb/gdbarch_components.py | 17 ++++++++++++++++-
>  5 files changed, 64 insertions(+), 2 deletions(-)
>
> diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
> index f320d3d7365..c396e9e3840 100644
> --- a/gdb/arch-utils.c
> +++ b/gdb/arch-utils.c
> @@ -1218,6 +1218,16 @@ default_gdbarch_return_value
>  				readbuf, writebuf);
>  }
>  
> +/* See arch-utils.h.  */
> +
> +std::optional<CORE_ADDR>
> +default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
> +				  bool &shadow_stack_enabled)
> +{
> +  shadow_stack_enabled = false;
> +  return {};
> +}
> +
>  obstack *gdbarch_obstack (gdbarch *arch)
>  {
>    return &arch->obstack;
> diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
> index 1509cb7441e..14a84b74733 100644
> --- a/gdb/arch-utils.h
> +++ b/gdb/arch-utils.h
> @@ -414,4 +414,9 @@ extern enum return_value_convention default_gdbarch_return_value
>        struct regcache *regcache, struct value **read_value,
>        const gdb_byte *writebuf);
>  
> +/* Default implementation of gdbarch default_get_shadow_stack_pointer
> +   method.  */
> +extern std::optional<CORE_ADDR> default_get_shadow_stack_pointer
> +  (gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
> +
>  #endif /* GDB_ARCH_UTILS_H */
> diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c
> index a4b72793fd8..caeda3cefae 100644
> --- a/gdb/gdbarch-gen.c
> +++ b/gdb/gdbarch-gen.c
> @@ -263,6 +263,7 @@ struct gdbarch
>    gdbarch_use_target_description_from_corefile_notes_ftype *use_target_description_from_corefile_notes = default_use_target_description_from_corefile_notes;
>    gdbarch_core_parse_exec_context_ftype *core_parse_exec_context = default_core_parse_exec_context;
>    gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
> +  gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer = default_get_shadow_stack_pointer;
>  };
>  
>  /* Create a new ``struct gdbarch'' based on information provided by
> @@ -537,6 +538,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
>    /* Skip verify of use_target_description_from_corefile_notes, invalid_p == 0.  */
>    /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
>    /* Skip verify of shadow_stack_push, has predicate.  */
> +  /* Skip verify of get_shadow_stack_pointer, invalid_p == 0.  */
>    if (!log.empty ())
>      internal_error (_("verify_gdbarch: the following are invalid ...%s"),
>  		    log.c_str ());
> @@ -1414,6 +1416,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
>    gdb_printf (file,
>  	      "gdbarch_dump: shadow_stack_push = <%s>\n",
>  	      host_address_to_string (gdbarch->shadow_stack_push));
> +  gdb_printf (file,
> +	      "gdbarch_dump: get_shadow_stack_pointer = <%s>\n",
> +	      host_address_to_string (gdbarch->get_shadow_stack_pointer));
>    if (gdbarch->dump_tdep != NULL)
>      gdbarch->dump_tdep (gdbarch, file);
>  }
> @@ -5583,3 +5588,20 @@ set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
>  {
>    gdbarch->shadow_stack_push = shadow_stack_push;
>  }
> +
> +std::optional<CORE_ADDR>
> +gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  gdb_assert (gdbarch->get_shadow_stack_pointer != NULL);
> +  if (gdbarch_debug >= 2)
> +    gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer called\n");
> +  return gdbarch->get_shadow_stack_pointer (gdbarch, regcache, shadow_stack_enabled);
> +}
> +
> +void
> +set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
> +				      gdbarch_get_shadow_stack_pointer_ftype get_shadow_stack_pointer)
> +{
> +  gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer;
> +}
> diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
> index 71142332540..c36171b089e 100644
> --- a/gdb/gdbarch-gen.h
> +++ b/gdb/gdbarch-gen.h
> @@ -1807,7 +1807,8 @@ extern void set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc
>     technologies.  For example, the Intel Control-Flow Enforcement Technology
>     (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
>     To enable shadow stack support for inferior calls the shadow_stack_push
> -   gdbarch hook has to be provided.
> +   gdbarch hook has to be provided.  The get_shadow_stack_pointer gdbarch
> +   hook has to be provided to enable displaced stepping.
>  
>     Push NEW_ADDR to the shadow stack and update the shadow stack pointer. */
>  
> @@ -1816,3 +1817,12 @@ extern bool gdbarch_shadow_stack_push_p (struct gdbarch *gdbarch);
>  typedef void (gdbarch_shadow_stack_push_ftype) (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
>  extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
>  extern void set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch, gdbarch_shadow_stack_push_ftype *shadow_stack_push);
> +
> +/* If possible, return the shadow stack pointer.  On some architectures, the
> +   shadow stack pointer is available even if the feature is disabled.  To
> +   return the feature's enablement state configure SHADOW_STACK_ENABLED.
> +   Set it to true in case the shadow stack is enabled. */
> +
> +typedef std::optional<CORE_ADDR> (gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
> +extern std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
> +extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer);
> diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
> index abc79588473..73459064170 100644
> --- a/gdb/gdbarch_components.py
> +++ b/gdb/gdbarch_components.py
> @@ -2855,7 +2855,8 @@ Some targets support special hardware-assisted control-flow protection
>  technologies.  For example, the Intel Control-Flow Enforcement Technology
>  (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
>  To enable shadow stack support for inferior calls the shadow_stack_push
> -gdbarch hook has to be provided.
> +gdbarch hook has to be provided.  The get_shadow_stack_pointer gdbarch
> +hook has to be provided to enable displaced stepping.

I find the addition of this last sentence here a little strange.  While
it's a true statement, wouldn't this be better placed on the comment for
get_shadow_stack_pointer?

>  
>  Push NEW_ADDR to the shadow stack and update the shadow stack pointer.
>  """,
> @@ -2864,3 +2865,17 @@ Push NEW_ADDR to the shadow stack and update the shadow stack pointer.
>      params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
>      predicate=True,
>  )
> +
> +Method(
> +    comment="""
> +If possible, return the shadow stack pointer.  On some architectures, the
> +shadow stack pointer is available even if the feature is disabled.  To
> +return the feature's enablement state configure SHADOW_STACK_ENABLED.
> +Set it to true in case the shadow stack is enabled.

The wording "configure SHADOW_STACK_ENABLED" seems a little strange.
Also, there's a bunch of important detail that this comment doesn't
cover.  Here's what I'd suggest, though it's possible this doesn't match
the implementation (I haven't checked the next patch yet), but this does
match default_get_shadow_stack_pointer.  Feel free to take any of this
that is useful:

  If possible, return the shadow stack pointer.  On some architectures,
  the shadow stack pointer is available even if the feature is disabled.
  If the shadow stack feature is enabled then set SHADOW_STACK_ENABLED
  to true, otherwise set SHADOW_STACK_ENABLED to false.  The
  SHADOW_STACK_ENABLED will always be set if this function returns a
  value.  If the function doesn't return a value then the state of
  SHADOW_STACK_ENABLED is undefined.

Thanks,
Andrew


> +""",
> +    type="std::optional<CORE_ADDR>",
> +    name="get_shadow_stack_pointer",
> +    params=[("regcache *", "regcache"), ("bool &", "shadow_stack_enabled")],
> +    predefault="default_get_shadow_stack_pointer",
> +    invalid=False,
> +)
> -- 
> 2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 12/12] gdb: Enable displaced stepping with shadow stack on amd64 linux.
  2025-06-28  8:28 ` [PATCH v5 12/12] gdb: Enable displaced stepping with shadow stack on amd64 linux Christina Schimpe
@ 2025-07-30 13:59   ` Andrew Burgess
  2025-07-31 17:29     ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-07-30 13:59 UTC (permalink / raw)
  To: Christina Schimpe, gdb-patches; +Cc: thiago.bauermann, luis.machado

Christina Schimpe <christina.schimpe@intel.com> writes:

> Currently, if displaced stepping is active and the single stepped instruction
> is a call instruction, the return address atop the stack is the address
> following the copied instruction.  However, to allow normal program execution
> it has to be the address following the original instruction.  Due to that
> reason, the return address is corrected in amd64_displaced_step_fixup and
> i386_displaced_step_fixup.
>
> For programs that are shadow-stack enabled we see a control-protection
> exception, as the address on the shadow stack does not match the address
> atop the stack.
>
> Fix this by correcting the shadow stack top address as well.
>
> Reviewed-By: Luis Machado <luis.machado@arm.com>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> ---
>  gdb/NEWS                                      |  3 +
>  gdb/amd64-linux-tdep.c                        | 16 +++-
>  gdb/amd64-tdep.c                              | 15 +++
>  gdb/doc/gdb.texinfo                           | 11 ++-
>  gdb/i386-tdep.c                               | 15 +++
>  .../gdb.arch/amd64-shadow-stack-disp-step.exp | 92 +++++++++++++++++++
>  6 files changed, 149 insertions(+), 3 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index ba555f0dea1..60510fefea4 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,9 @@
>  
>  *** Changes since GDB 16
>  
> +* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
> +  size (X32) Shadow Stacks are now supported.
> +
>  * Support for the shadow stack pointer register on x86-64 or x86-64 with
>    32-bit pointer size (X32) GNU/Linux.
>  
> diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
> index 899fe2df02c..782b66f1467 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -1936,8 +1936,10 @@ amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch)
>     possible.  */
>  
>  static std::optional<CORE_ADDR>
> -amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache)
> +amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
> +				      bool &shadow_stack_enabled)
>  {
> +  shadow_stack_enabled = false;
>    const i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch);
>  
>    if (tdep == nullptr || tdep->ssp_regnum < 0)
> @@ -1955,6 +1957,9 @@ amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache)
>    if (ssp == 0x0)
>      return {};
>  
> +  /* In case there is a shadow stack pointer available which is non-null,
> +     the shadow stack feature is enabled.  */
> +  shadow_stack_enabled = true;
>    return ssp;
>  }
>  
> @@ -1965,8 +1970,13 @@ static void
>  amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR new_addr,
>  			       regcache *regcache)
>  {
> +  bool shadow_stack_enabled;
>    std::optional<CORE_ADDR> ssp
> -    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache);
> +    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache,
> +					    shadow_stack_enabled);
> +
> +  /* For amd64/Linux, if SSP has a value that means shadow stack is
> +     enabled.  */

It feels like this statement should be associated with an assert.  I'd
suggest: 

  if (!ssp.has_value ())
    return;
  else
    gdb_assert (shadow_stack_enabled);

>    if (!ssp.has_value ())
>      return;
>  
> @@ -2122,6 +2132,8 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch,
>      (gdbarch, amd64_linux_remove_non_address_bits_watchpoint);
>  
>    set_gdbarch_shadow_stack_push (gdbarch, amd64_linux_shadow_stack_push);
> +  set_gdbarch_get_shadow_stack_pointer (gdbarch,
> +					amd64_linux_get_shadow_stack_pointer);
>    dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);
>  }
>  
> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index 450dbc38047..8afb3a7abba 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -1917,6 +1917,21 @@ amd64_displaced_step_fixup (struct gdbarch *gdbarch,
>        displaced_debug_printf ("relocated return addr at %s to %s",
>  			      paddress (gdbarch, rsp),
>  			      paddress (gdbarch, retaddr));
> +
> +      /* If shadow stack is enabled, we need to correct the return address
> +	 on the shadow stack too.  */
> +      bool shadow_stack_enabled;
> +      std::optional<CORE_ADDR> ssp
> +	= gdbarch_get_shadow_stack_pointer (gdbarch, regs,
> +					    shadow_stack_enabled);
> +      if (ssp.has_value () && shadow_stack_enabled)

Given the strengthening of the comment on
gdbarch_get_shadow_stack_pointer that I suggest in the previous patch, I
think this should become:

  if (shadow_stack_enabled)
    {
      gdb_assert (ssp.has_value ());

      ... etc ...
    }

> +	{
> +	  write_memory_unsigned_integer (*ssp, retaddr_len, byte_order,
> +					 retaddr);
> +	  displaced_debug_printf ("relocated shadow stack return addr at %s "
> +				  "to %s", paddress (gdbarch, *ssp),
> +				  paddress (gdbarch, retaddr));
> +	}
>      }
>  }
>  
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index b5120b78426..488816d5ca2 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -27059,12 +27059,20 @@ the program stream must be an @code{ENDBR} instruction, otherwise the
>  processor signals a control protection exception.
>  @end itemize
>  
> -Impact on Call/Print:
> +Impact on GDB commands:
> +@itemize @bullet
> +@item Call/Print:
>  Inferior calls in @value{GDBN} reset the current PC to the beginning of the
>  function that is called.  No call instruction is executed, but the @code{RET}
>  instruction actually is.  To avoid a control protection exception due to the
>  missing return address on the shadow stack, @value{GDBN} pushes the new return
>  address to the shadow stack and updates the shadow stack pointer.
> +@item Step:
> +With displaced stepping, @value{GDBN} may run an out of line copy of a call
> +instruction.  In this case, the wrong return address is pushed to the shadow
> +stack.  @value{GDBN} corrects this value to avoid a control protection
> +exception.  For more details on displaced stepping, see @ref{displaced-stepping}.
> +@end itemize
>  
>  @node Alpha
>  @subsection Alpha
> @@ -41741,6 +41749,7 @@ GLOBAL              Disassembler_2	(Matches current architecture)
>  @cindex out-of-line single-stepping
>  @item set displaced-stepping
>  @itemx show displaced-stepping
> +@anchor{displaced-stepping}
>  Control whether or not @value{GDBN} will do @dfn{displaced stepping}
>  if the target supports it.  Displaced stepping is a way to single-step
>  over breakpoints without removing them from the inferior, by executing
> diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
> index 8eb5b4fac86..3b05ace2142 100644
> --- a/gdb/i386-tdep.c
> +++ b/gdb/i386-tdep.c
> @@ -899,6 +899,21 @@ i386_displaced_step_fixup (struct gdbarch *gdbarch,
>        displaced_debug_printf ("relocated return addr at %s to %s",
>  			      paddress (gdbarch, esp),
>  			      paddress (gdbarch, retaddr));
> +
> +      /* If shadow stack is enabled, we need to correct the return address
> +	 on the shadow stack too.  */
> +      bool shadow_stack_enabled;
> +      std::optional<CORE_ADDR> ssp
> +	= gdbarch_get_shadow_stack_pointer (gdbarch, regs,
> +					    shadow_stack_enabled);
> +      if (ssp.has_value () && shadow_stack_enabled)

Same comment here as for amd64.

> +	{
> +	  write_memory_unsigned_integer (*ssp, retaddr_len, byte_order,
> +					 retaddr);
> +	  displaced_debug_printf ("relocated shadow stack return addr at %s "
> +				  "to %s", paddress (gdbarch, *ssp),
> +				  paddress (gdbarch, retaddr));
> +	}
>      }
>  }
>  
> 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 00000000000..47bb4df8cfe
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
> @@ -0,0 +1,92 @@
> +# 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 -1

All of the 'return -1' in the global scope should just be 'return'.  The
-1 is not checked or used.

> +    }
> +
> +    # Enable displaced stepping.
> +    gdb_test_no_output "set displaced-stepping on"
> +    gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*"
> +
> +    if { ![runto_main] } {
> +	return -1
> +    }
> +
> +    # Get the address of the call1 instruction.

I think you mean:

  # 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 -1
> +    }
> +
> +    # Get the address of the call2 instruction.

As above:

  # 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 -1
> +    }
> +
> +    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"
> +
> +    # Depending on instruction generation we might end up in the call
> +    # instruction after "runto_main".  Only resume until call1 instruction
> +    # in case the first instruction we're stopped at is not yet the call1
> +    # instruction.

Could you not just add some filler to the test program to avoid needing
to do this?  I don't really object (to this) but using filler might be
simpler.

> +    set stop_addr [get_valueof "/x" "\$pc" "" "value of pc after runto_main"]
> +    if {[eval expr "$stop_addr < $call1_addr"]} {

Is the 'eval expr' really needed here?  There are plenty of the places
in the testsuite where we just write something like:

  if { $stop_addr < $call1_addr } { ... }

so I would have expected that to work.

Thanks,
Andrew

> +	gdb_test "continue" \
> +	    "Breakpoint $decimal, $call1_addr in main ().*" \
> +	    "continue until call1 instruction"
> +    }
> +    gdb_assert {$call1_addr == [get_valueof "/x" "\$pc" ""]}
> +
> +    # 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"
> +}
> -- 
> 2.43.0


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 10/12] gdb: Implement amd64 linux shadow stack support for inferior calls.
  2025-07-30 11:58   ` Andrew Burgess
@ 2025-07-31 12:32     ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-31 12:32 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi Andrew,

Thanks a lot for the review. Please find my comments to your feedback below.

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Wednesday, July 30, 2025 1:58 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 10/12] gdb: Implement amd64 linux shadow stack
> support for inferior calls.
> 
> Christina Schimpe <christina.schimpe@intel.com> writes:
> 
> > This patch enables inferior calls to support Intel's Control-Flow
> > Enforcement Technology (CET), which provides the shadow stack feature
> > for the x86 architecture.
> > Following the restriction of the linux kernel, enable inferior calls
> > for amd64 only.
> >
> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> > Reviewed-By: Luis Machado <luis.machado@arm.com>
> > ---
> >  gdb/amd64-linux-tdep.c                        | 63 +++++++++++++++++++
> >  gdb/doc/gdb.texinfo                           | 29 +++++++++
> >  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 55 +++++++++++++++-
> >  3 files changed, 146 insertions(+), 1 deletion(-)
> >
> > diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index
> > f6ae5395870..899fe2df02c 100644
> > --- a/gdb/amd64-linux-tdep.c
> > +++ b/gdb/amd64-linux-tdep.c
> > @@ -1932,6 +1932,67 @@
> amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch)
> >    return (binfo->bits_per_word / binfo->bits_per_byte);  }
> >
> > +/* Read the shadow stack pointer register and return its value, if
> > +   possible.  */
> > +
> > +static std::optional<CORE_ADDR>
> > +amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache
> > +*regcache) {
> > +  const i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep>
> > +(gdbarch);
> > +
> > +  if (tdep == nullptr || tdep->ssp_regnum < 0)
> > +    return {};
> 
> We don't check for 'tdep == nullptr' anywhere else (for x86), so I don't think
> this check is needed.  If you _really_ want you could gdb_assert, but I'm
> pretty sure you'll have crashed long before now if tdep == nullptr.

I agree and will remove.

> > +
> > +  CORE_ADDR ssp;
> > +  if (regcache_raw_read_unsigned (regcache, tdep->ssp_regnum, &ssp)
> > +      != REG_VALID)
> > +    return {};
> > +
> > +  /* Dependent on the target in case the shadow stack pointer is
> > +     unavailable, the ssp register can be invalid or 0x0 when shadow stack
> > +     is supported by HW and the linux kernel but not enabled for the
> > +     current thread.  */
> 
> Just so I understand, is the 0 coming from actual inferior state?  Or is the 0
> creeping in from somewhere in GDB when we should be creating an
> unavailable value, but get it wrong?
> 
> Using 0 a magic address value is something I really dislike (having worked on
> targets where 0 is a valid address), so they always make me uncomfortable.
> If this is an actual h/w thing then there's nothing we could or should do
> about it ... but if this is a GDB thing, then maybe we can fix that?

We see this dependent on the linux kernel version. So the value 0x0 is set by the kernel I think.
With kernels older than 6.13 we shouldn't see this anymore, due to this patch:
https://github.com/torvalds/linux/commit/a9d9c33132d49329ada647e4514d210d15e31d81

> 
> > +  if (ssp == 0x0)
> > +    return {};
> > +
> > +  return ssp;
> > +}
> > +
> > +/* If shadow stack is enabled, push the address NEW_ADDR to the
> shadow
> > +   stack and increment the shadow stack pointer accordingly.  */
> > +
> > +static void
> > +amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR
> new_addr,
> > +			       regcache *regcache)
> > +{
> > +  std::optional<CORE_ADDR> ssp
> > +    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache);
> > +  if (!ssp.has_value ())
> > +    return;
> > +
> > +  /* The shadow stack grows downwards.  To push addresses to the stack,
> > +     we need to decrement SSP.    */
> > +  const int element_size
> > +    = amd64_linux_shadow_stack_element_size_aligned (gdbarch);  const
> > + CORE_ADDR new_ssp = *ssp - element_size;
> > +
> > +  /* Using /proc/PID/smaps we can only check if NEW_SSP points to
> shadow
> > +     stack memory.  If it doesn't, we assume the stack is full.  */
> > + std::pair<CORE_ADDR, CORE_ADDR> memrange;  if
> > + (!linux_address_in_shadow_stack_mem_range (new_ssp, &memrange))
> > +    error (_("No space left on the shadow stack."));
> > +
> > +  /* On x86 there can be a shadow stack token at bit 63.  For x32,
> > + the
> 
> I don't understand this first sentence and how it relates to either the rest of
> the comment, or the following code.

Yes, this is confusing I agree. I guess all what I wanted to say is that we have to
write the full 8 bytes due to the token even though the actual address of the
shadow stack pointer is only 4 bytes for x32.

Example shadow stack token for x32 in frame #1:
bt shadow^M
#0  0x00000000f7d22040 in __restore_rt from /libx32/libc.so.6^M
#1  0x80000000f7ce6fd8^M
#2  0x00000000f7d21f8b in raise from /libx32/libc.so.6

The output is taken from a patch for the "bt shadow" command,
which is not part of this series. 

How about:

On x86 there can be a shadow stack token at bit 63.  For x32,  the
address size is only 32 bit.   Always write back the full 8 bytes to include
the shadow stack token.

> 
> > +     address size is only 32 bit.  Thus, we must use ELEMENT_SIZE (and
> > +     not gdbarch_addr_bit) to determine the width of the address to be
> > +     written.  */
> > +  const bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> > +  write_memory_unsigned_integer (new_ssp, element_size, byte_order,
> > +				 (ULONGEST) new_addr);
> > +
> > +  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep>
> > + (gdbarch);  regcache_raw_write_unsigned (regcache, tdep->ssp_regnum,
> > + new_ssp);
> 
> By this point we know that tdep->ssp_regnum must be a valid regnum.  But
> the checks for that are in a separate function, so maybe we should:
> 
>   gdb_assert (tdep->ssp_regnum > -1);

Good idea, will add.

> 
> > +}
> >
> >  /* Implement shadow stack pointer unwinding. For each new shadow
> stack
> >     pointer check if its address is still in the shadow stack memory range.
> > @@ -2059,6 +2120,8 @@ amd64_linux_init_abi_common(struct
> gdbarch_info
> > info, struct gdbarch *gdbarch,
> >
> >    set_gdbarch_remove_non_address_bits_watchpoint
> >      (gdbarch, amd64_linux_remove_non_address_bits_watchpoint);
> > +
> > +  set_gdbarch_shadow_stack_push (gdbarch,
> > + amd64_linux_shadow_stack_push);
> >    dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);  }
> >
> > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index
> > 0881ac4aee5..b5120b78426 100644
> > --- a/gdb/doc/gdb.texinfo
> > +++ b/gdb/doc/gdb.texinfo
> > @@ -27037,6 +27037,35 @@ registers
> >
> >  @end itemize
> >
> > +@subsubsection Intel Control-Flow Enforcement Technology.
> > +@cindex Intel Control-Flow Enforcement Technology.
> > +
> > +The @dfn{Intel Control-Flow Enforcement Technology} (@acronym{Intel
> > +CET}) provides two capabilities to defend against ``Return-oriented
> Programming''
> > +and ``call/jmp-oriented programming'' style control-flow attacks:
> > +
> > +@itemize @bullet
> > +@item Shadow Stack:
> > +A shadow stack is a second stack for a program.  It holds the return
> > +addresses pushed by the call instruction.  The @code{RET} instruction
> > +pops the return addresses from both call and shadow stack.  If the
> > +return addresses from the two stacks do not match, the processor
> > +signals a control protection exception.
> > +@item Indirect Branch Tracking (IBT):
> > +When IBT is enabled, the CPU implements a state machine that tracks
> > +indirect @code{JMP} and @code{CALL} instructions.  The state machine
> > +can be either IDLE or WAIT_FOR_ENDBRANCH.  In
> WAIT_FOR_ENDBRANCH
> > +state the next instruction in the program stream must be an
> > +@code{ENDBR} instruction, otherwise the processor signals a control
> protection exception.
> 
> If I understand it, the IBT doesn't currently have an impact on GDB, right?
> The inferior function being called likely starts with an `endbr` instruction, but
> GDB will just leave the IBT mechanism in the IDLE state, and the endbr will
> be interpreted as a nop.

There is no linux kernel support for IBT in userspace, yet.

> 
> I also found this description a little too light on the details.  Just having the
> name was enough to go and find the real docs, but I think the text could be
> made cleared with two additional sentences:
> 
>   @item Indirect Branch Tracking (IBT):
>   When IBT is enabled, the CPU implements a state machine that tracks
> indirect
>   @code{JMP} and @code{CALL} instructions.  The state machine can be
> either IDLE
>   or WAIT_FOR_ENDBRANCH.  When a @code{JMP} or @code{CALL} is
> executed
>   the state machine chages to the WAIT_FOR_ENDBRANCH state.  In
>   WAIT_FOR_ENDBRANCH state the next instruction in the program stream
>   must be an @code{ENDBR} instruction, otherwise the
>   processor signals a control protection exception.  After executing a
>   @code{ENDBR} instruction the state machine returns to the IDLE state.
> 
> This change isn't a hard requirement, but I do think this makes the
> description more useful.

I agree, your suggestion improves the description, thank you. Will improve the docs here.

> > +@end itemize
> > +
> > +Impact on Call/Print:
> > +Inferior calls in @value{GDBN} reset the current PC to the beginning
> > +of the function that is called.  No call instruction is executed, but
> > +the @code{RET} instruction actually is.  To avoid a control
> > +protection exception due to the missing return address on the shadow
> > +stack, @value{GDBN} pushes the new return address to the shadow stack
> and updates the shadow stack pointer.
> > +
> >  @node Alpha
> >  @subsection Alpha
> >
> > diff --git a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> > b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> > index 17f32ce3964..622612d2f7d 100644
> > --- a/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> > +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> > @@ -13,12 +13,29 @@
> >  # 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 and the return
> command.
> > +# 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
> >
> > +proc restart_and_run_infcall_call2 {} {
> 
> There should be a comment before each proc please.

True, will add.

Thanks!
Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 12/12] gdb: Enable displaced stepping with shadow stack on amd64 linux.
  2025-07-30 13:59   ` Andrew Burgess
@ 2025-07-31 17:29     ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-07-31 17:29 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi Andrew, 

Thank you for the review. 

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Wednesday, July 30, 2025 4:00 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 12/12] gdb: Enable displaced stepping with shadow
> stack on amd64 linux.
> 
> Christina Schimpe <christina.schimpe@intel.com> writes:
> 
> > Currently, if displaced stepping is active and the single stepped
> > instruction is a call instruction, the return address atop the stack
> > is the address following the copied instruction.  However, to allow
> > normal program execution it has to be the address following the
> > original instruction.  Due to that reason, the return address is
> > corrected in amd64_displaced_step_fixup and i386_displaced_step_fixup.
> >
> > For programs that are shadow-stack enabled we see a control-protection
> > exception, as the address on the shadow stack does not match the
> > address atop the stack.
> >
> > Fix this by correcting the shadow stack top address as well.
> >
> > Reviewed-By: Luis Machado <luis.machado@arm.com>
> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> > ---
> >  gdb/NEWS                                      |  3 +
> >  gdb/amd64-linux-tdep.c                        | 16 +++-
> >  gdb/amd64-tdep.c                              | 15 +++
> >  gdb/doc/gdb.texinfo                           | 11 ++-
> >  gdb/i386-tdep.c                               | 15 +++
> >  .../gdb.arch/amd64-shadow-stack-disp-step.exp | 92
> > +++++++++++++++++++
> >  6 files changed, 149 insertions(+), 3 deletions(-)  create mode
> > 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
> >
> > diff --git a/gdb/NEWS b/gdb/NEWS
> > index ba555f0dea1..60510fefea4 100644
> > --- a/gdb/NEWS
> > +++ b/gdb/NEWS
> > @@ -3,6 +3,9 @@
> >
> >  *** Changes since GDB 16
> >
> > +* Debugging Linux programs that use x86-64 or x86-64 with 32-bit
> > +pointer
> > +  size (X32) Shadow Stacks are now supported.
> > +
> >  * Support for the shadow stack pointer register on x86-64 or x86-64 with
> >    32-bit pointer size (X32) GNU/Linux.
> >
> > diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index
> > 899fe2df02c..782b66f1467 100644
> > --- a/gdb/amd64-linux-tdep.c
> > +++ b/gdb/amd64-linux-tdep.c
> > @@ -1936,8 +1936,10 @@
> amd64_linux_shadow_stack_element_size_aligned (gdbarch *gdbarch)
> >     possible.  */
> >
> >  static std::optional<CORE_ADDR>
> > -amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache
> > *regcache)
> > +amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache
> *regcache,
> > +				      bool &shadow_stack_enabled)
> >  {
> > +  shadow_stack_enabled = false;
> >    const i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep>
> > (gdbarch);
> >
> >    if (tdep == nullptr || tdep->ssp_regnum < 0) @@ -1955,6 +1957,9 @@
> > amd64_linux_get_shadow_stack_pointer (gdbarch *gdbarch, regcache
> *regcache)
> >    if (ssp == 0x0)
> >      return {};
> >
> > +  /* In case there is a shadow stack pointer available which is non-null,
> > +     the shadow stack feature is enabled.  */  shadow_stack_enabled =
> > + true;
> >    return ssp;
> >  }
> >
> > @@ -1965,8 +1970,13 @@ static void
> >  amd64_linux_shadow_stack_push (gdbarch *gdbarch, CORE_ADDR
> new_addr,
> >  			       regcache *regcache)
> >  {
> > +  bool shadow_stack_enabled;
> >    std::optional<CORE_ADDR> ssp
> > -    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache);
> > +    = amd64_linux_get_shadow_stack_pointer (gdbarch, regcache,
> > +					    shadow_stack_enabled);
> > +
> > +  /* For amd64/Linux, if SSP has a value that means shadow stack is
> > +     enabled.  */
> 
> It feels like this statement should be associated with an assert.  I'd
> suggest:
> 
>   if (!ssp.has_value ())
>     return;
>   else
>     gdb_assert (shadow_stack_enabled);

Agree, will add.

> >    if (!ssp.has_value ())
> >      return;
> >
> > @@ -2122,6 +2132,8 @@ amd64_linux_init_abi_common(struct
> gdbarch_info info, struct gdbarch *gdbarch,
> >      (gdbarch, amd64_linux_remove_non_address_bits_watchpoint);
> >
> >    set_gdbarch_shadow_stack_push (gdbarch,
> > amd64_linux_shadow_stack_push);
> > +  set_gdbarch_get_shadow_stack_pointer (gdbarch,
> > +
> 	amd64_linux_get_shadow_stack_pointer);
> >    dwarf2_frame_set_init_reg (gdbarch, amd64_init_reg);  }
> >
> > diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index
> > 450dbc38047..8afb3a7abba 100644
> > --- a/gdb/amd64-tdep.c
> > +++ b/gdb/amd64-tdep.c
> > @@ -1917,6 +1917,21 @@ amd64_displaced_step_fixup (struct gdbarch
> *gdbarch,
> >        displaced_debug_printf ("relocated return addr at %s to %s",
> >  			      paddress (gdbarch, rsp),
> >  			      paddress (gdbarch, retaddr));
> > +
> > +      /* If shadow stack is enabled, we need to correct the return address
> > +	 on the shadow stack too.  */
> > +      bool shadow_stack_enabled;
> > +      std::optional<CORE_ADDR> ssp
> > +	= gdbarch_get_shadow_stack_pointer (gdbarch, regs,
> > +					    shadow_stack_enabled);
> > +      if (ssp.has_value () && shadow_stack_enabled)
> 
> Given the strengthening of the comment on
> gdbarch_get_shadow_stack_pointer that I suggest in the previous patch, I
> think this should become:
> 
>   if (shadow_stack_enabled)
>     {
>       gdb_assert (ssp.has_value ());
> 
>       ... etc ...
>     }

Agree, will add.

> > +	{
> > +	  write_memory_unsigned_integer (*ssp, retaddr_len, byte_order,
> > +					 retaddr);
> > +	  displaced_debug_printf ("relocated shadow stack return addr at %s
> "
> > +				  "to %s", paddress (gdbarch, *ssp),
> > +				  paddress (gdbarch, retaddr));
> > +	}
> >      }
> >  }
> >
> > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index
> > b5120b78426..488816d5ca2 100644
> > --- a/gdb/doc/gdb.texinfo
> > +++ b/gdb/doc/gdb.texinfo
> > @@ -27059,12 +27059,20 @@ the program stream must be an
> @code{ENDBR}
> > instruction, otherwise the  processor signals a control protection
> exception.
> >  @end itemize
> >
> > -Impact on Call/Print:
> > +Impact on GDB commands:
> > +@itemize @bullet
> > +@item Call/Print:
> >  Inferior calls in @value{GDBN} reset the current PC to the beginning
> > of the  function that is called.  No call instruction is executed, but
> > the @code{RET}  instruction actually is.  To avoid a control
> > protection exception due to the  missing return address on the shadow
> > stack, @value{GDBN} pushes the new return  address to the shadow stack
> and updates the shadow stack pointer.
> > +@item Step:
> > +With displaced stepping, @value{GDBN} may run an out of line copy of
> > +a call instruction.  In this case, the wrong return address is pushed
> > +to the shadow stack.  @value{GDBN} corrects this value to avoid a
> > +control protection exception.  For more details on displaced stepping, see
> @ref{displaced-stepping}.
> > +@end itemize
> >
> >  @node Alpha
> >  @subsection Alpha
> > @@ -41741,6 +41749,7 @@ GLOBAL              Disassembler_2
> 	(Matches current architecture)
> >  @cindex out-of-line single-stepping
> >  @item set displaced-stepping
> >  @itemx show displaced-stepping
> > +@anchor{displaced-stepping}
> >  Control whether or not @value{GDBN} will do @dfn{displaced stepping}
> > if the target supports it.  Displaced stepping is a way to single-step
> > over breakpoints without removing them from the inferior, by executing
> > diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index
> > 8eb5b4fac86..3b05ace2142 100644
> > --- a/gdb/i386-tdep.c
> > +++ b/gdb/i386-tdep.c
> > @@ -899,6 +899,21 @@ i386_displaced_step_fixup (struct gdbarch
> *gdbarch,
> >        displaced_debug_printf ("relocated return addr at %s to %s",
> >  			      paddress (gdbarch, esp),
> >  			      paddress (gdbarch, retaddr));
> > +
> > +      /* If shadow stack is enabled, we need to correct the return address
> > +	 on the shadow stack too.  */
> > +      bool shadow_stack_enabled;
> > +      std::optional<CORE_ADDR> ssp
> > +	= gdbarch_get_shadow_stack_pointer (gdbarch, regs,
> > +					    shadow_stack_enabled);
> > +      if (ssp.has_value () && shadow_stack_enabled)
> 
> Same comment here as for amd64.

Yes, will fix.

> > +	{
> > +	  write_memory_unsigned_integer (*ssp, retaddr_len, byte_order,
> > +					 retaddr);
> > +	  displaced_debug_printf ("relocated shadow stack return addr at %s
> "
> > +				  "to %s", paddress (gdbarch, *ssp),
> > +				  paddress (gdbarch, retaddr));
> > +	}
> >      }
> >  }
> >
> > 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 00000000000..47bb4df8cfe
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-step.exp
> > @@ -0,0 +1,92 @@
> > +# 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 -1
> 
> All of the 'return -1' in the global scope should just be 'return'.  The
> -1 is not checked or used.

Yes, will fix.

> > +    }
> > +
> > +    # Enable displaced stepping.
> > +    gdb_test_no_output "set displaced-stepping on"
> > +    gdb_test "show displaced-stepping" ".* displaced stepping .* is on.*"
> > +
> > +    if { ![runto_main] } {
> > +	return -1
> > +    }
> > +
> > +    # Get the address of the call1 instruction.
> 
> I think you mean:
> 
>   # Get the address of the call to the call1 function.

Yes, thank you.

> > +    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 -1
> > +    }
> > +
> > +    # Get the address of the call2 instruction.
> 
> As above:
> 
>   # Get the address of the call to the call2 function.

Will fix.

> > +    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 -1
> > +    }
> > +
> > +    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"
> > +
> > +    # Depending on instruction generation we might end up in the call
> > +    # instruction after "runto_main".  Only resume until call1 instruction
> > +    # in case the first instruction we're stopped at is not yet the call1
> > +    # instruction.
> 
> Could you not just add some filler to the test program to avoid needing to
> do this?  I don't really object (to this) but using filler might be simpler.

Indeed, simply adding a nop instruction will fix this:
~~~
  /* 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");
~~~
 Thank you!


> > +    set stop_addr [get_valueof "/x" "\$pc" "" "value of pc after
> runto_main"]
> > +    if {[eval expr "$stop_addr < $call1_addr"]} {
> 
> Is the 'eval expr' really needed here?  There are plenty of the places in the
> testsuite where we just write something like:
> 
>   if { $stop_addr < $call1_addr } { ... }
> 
> so I would have expected that to work.

I see:
ERROR: tcl error sourcing /tmp/gdb.arch/amd64-shadow-stack-disp-step.exp.
ERROR: tcl error code TCL LOOKUP COMMAND {0x555555555148 < 0x000055555555514d}
ERROR: invalid command name "0x555555555148 < 0x000055555555514d"
    while executing
"::gdb_tcl_unknown {0x555555555148 < 0x000055555555514d}"

I think the examples that are available in the testsuite for comparing addresses are
always inside gdb_assert.

The gdb_assert proc then again uses "expr". But I can omit "eval" at least. 

Kind Regards,
Christina

> Thanks,
> Andrew
> 
> > +	gdb_test "continue" \
> > +	    "Breakpoint $decimal, $call1_addr in main ().*" \
> > +	    "continue until call1 instruction"
> > +    }
> > +    gdb_assert {$call1_addr == [get_valueof "/x" "\$pc" ""]}
> > +
> > +    # 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"
> > +}
> > --
> > 2.43.0

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-07-29 13:51   ` Andrew Burgess
@ 2025-08-01 12:40     ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-01 12:40 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Tuesday, July 29, 2025 3:51 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow
> stack pointer register.
> 
> Christina Schimpe <christina.schimpe@intel.com> writes:
> 
> > diff --git a/gdb/x86-tdep.c b/gdb/x86-tdep.c index
> > 6646b1157c8..06dec4e1f90 100644
> > --- a/gdb/x86-tdep.c
> > +++ b/gdb/x86-tdep.c
> > @@ -17,10 +17,31 @@
> >     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 "i386-tdep.h"
> >  #include "x86-tdep.h"
> >  #include "symtab.h"
> >
> >
> > +/* See x86-tdep.h.  */
> > +
> > +void
> > +x86_supply_ssp (regcache *regcache, const uint64_t ssp) {
> > +  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep>
> > +(regcache->arch ());
> > +  gdb_assert (tdep != nullptr && tdep->ssp_regnum > 0);
> > +  regcache->raw_supply (tdep->ssp_regnum, &ssp); }
> > +
> > +/* See x86-tdep.h.  */
> > +
> > +void
> > +x86_collect_ssp (const regcache *regcache, uint64_t& ssp)
> 
> While reviewing the next patch, I spotted a white space issue here, it should
> be 'uint64_t &ssp'.  And the same in the declaration too.
> 
> Thanks,
> Andrew

Yes, will fix.
 
Thank you!
Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-07-25 15:03     ` Schimpe, Christina
@ 2025-08-01 12:54       ` Schimpe, Christina
  2025-08-05 13:57       ` Andrew Burgess
  1 sibling, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-01 12:54 UTC (permalink / raw)
  To: Schimpe, Christina, Andrew Burgess, gdb-patches
  Cc: thiago.bauermann, luis.machado

Hi Andrew,

> -----Original Message-----
> From: Schimpe, Christina <christina.schimpe@intel.com>
> Sent: Friday, July 25, 2025 5:04 PM
> To: Andrew Burgess <aburgess@redhat.com>; gdb-patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow
> stack pointer register.
> 
> Hi Andrew,
> 
> Thanks a lot for the review! I have some questions for your feedback, please
> find my comments below.
> 
> > -----Original Message-----
> > From: Andrew Burgess <aburgess@redhat.com>
> > Sent: Friday, July 25, 2025 2:50 PM
> > To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> > patches@sourceware.org
> > Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> > Subject: Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel
> > shadow stack pointer register.
> >
> > Christina Schimpe <christina.schimpe@intel.com> writes:
> >
> > > This patch adds the user mode register PL3_SSP which is part of the
> > > Intel(R) Control-Flow Enforcement Technology (CET) feature for
> > > support of shadow stack.
> > > For now, only native and remote debugging support for shadow stack
> > > userspace on amd64 linux are covered by this patch including 64 bit
> > > and
> > > x32 support.  32 bit support is not covered due to missing Linux
> > > kernel support.
> > >
> > > This patch requires fixing the test
> > > gdb.base/inline-frame-cycle-unwind
> > > which is failing in case the shadow stack pointer is unavailable.
> > > Such a state is possible if shadow stack is disabled for the current
> > > thread but supported by HW.
> > >
> > > This test uses the Python unwinder inline-frame-cycle-unwind.py
> > > which fakes the cyclic stack cycle by reading the pending frame's
> > > registers and adding them to the unwinder:
> > >
> > > ~~~
> > > for reg in pending_frame.architecture().registers("general"):
> > >      val = pending_frame.read_register(reg)
> > >      unwinder.add_saved_register(reg, val)
> > >      return unwinder
> > > ~~~
> > >
> > > However, in case the python unwinder is used we add a register
> > > (pl3_ssp) that is unavailable.  This leads to a NOT_AVAILABLE_ERROR
> > > caught in gdb/frame-unwind.c:frame_unwind_try_unwinder and it is
> > > continued with standard unwinders.  This destroys the faked cyclic
> > > behavior and the stack is further unwinded after frame 5.
> > >
> > > In the working scenario an error should be triggered:
> > > ~~~
> > > bt
> > > 0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
> > > 1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > > 2  0x000055555555516e in inline_func () at
> > > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> > > 3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > > 4  0x000055555555516e in inline_func () at
> > > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> > > 5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > > Backtrace stopped: previous frame identical to this frame (corrupt
> > > stack?)
> > > (gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5:
> > > backtrace when the unwind is broken at frame 5 ~~~
> > >
> > > To fix the Python unwinder, we simply skip the unavailable registers.
> > >
> > > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> > > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> > > Reviewed-By: Luis Machado <luis.machado@arm.com>
> > > ---
> > >  gdb/NEWS                                      |  3 +
> > >  gdb/amd64-linux-nat.c                         | 17 +++++
> > >  gdb/amd64-linux-tdep.c                        |  1 +
> > >  gdb/amd64-tdep.c                              |  6 +-
> > >  gdb/amd64-tdep.h                              |  1 +
> > >  gdb/arch/amd64.c                              | 10 +++
> > >  gdb/arch/i386.c                               |  4 ++
> > >  gdb/arch/x86-linux-tdesc-features.c           |  1 +
> > >  gdb/doc/gdb.texinfo                           |  4 ++
> > >  gdb/features/Makefile                         |  2 +
> > >  gdb/features/i386/32bit-ssp.c                 | 14 ++++
> > >  gdb/features/i386/32bit-ssp.xml               | 11 +++
> > >  gdb/features/i386/64bit-ssp.c                 | 14 ++++
> > >  gdb/features/i386/64bit-ssp.xml               | 11 +++
> > >  gdb/i386-tdep.c                               | 22 +++++-
> > >  gdb/i386-tdep.h                               |  4 ++
> > >  gdb/nat/x86-linux-tdesc.c                     |  2 +
> > >  gdb/nat/x86-linux.c                           | 57 +++++++++++++++
> > >  gdb/nat/x86-linux.h                           |  4 ++
> > >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
> > >  gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
> > >  .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
> > >  gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
> > >  gdb/x86-linux-nat.c                           | 49 +++++++++++--
> > >  gdb/x86-linux-nat.h                           | 11 +++
> > >  gdb/x86-tdep.c                                | 21 ++++++
> > >  gdb/x86-tdep.h                                |  9 +++
> > >  gdbserver/linux-x86-low.cc                    | 28 +++++++-
> > >  gdbsupport/x86-xstate.h                       |  5 +-
> > >  29 files changed, 447 insertions(+), 10 deletions(-)  create mode
> > > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
> > > gdb/features/i386/32bit-ssp.xml  create mode 100644
> > > gdb/features/i386/64bit-ssp.c  create mode 100644
> > > gdb/features/i386/64bit-ssp.xml  create mode 100644
> > > gdb/testsuite/gdb.arch/amd64-shadow-stack.c
> > >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> > >
> >
> >
> > > diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index
> > > dbb9b3223cb..4df99ccca54 100644
> > > --- a/gdb/amd64-linux-nat.c
> > > +++ b/gdb/amd64-linux-nat.c
> > > @@ -32,6 +32,7 @@
> > >  #include "amd64-tdep.h"
> > >  #include "amd64-linux-tdep.h"
> > >  #include "i386-linux-tdep.h"
> > > +#include "x86-tdep.h"
> > >  #include "gdbsupport/x86-xstate.h"
> > >
> > >  #include "x86-linux-nat.h"
> > > @@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers
> (struct
> > > regcache *regcache, int regnum)
> > >
> > >        if (have_ptrace_getregset == TRIBOOL_TRUE)
> > >  	{
> > > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
> > > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
> >
> > It's really nit-picking, but I don't think the '>' here check is
> > correct.  You're checking that tdep->ssp_regnum has been assigned a
> > value, right?  And it's default value is -1, so, shouldn't the check really be
> '>= 0' or '!= -1' maybe?
> >
> > The same pattern is repeated below in ::store_registers.
> >
> > Ahh, reading further on, I see that (not just you), but all the
> > *_regnum fields in i386_gdbarch_tdep start set to 0 (which is a valid
> > register number), but are set to -1 if the feature is not found.
> > Maybe I'm missing something incredibly clever about this ... but I
> > suspect this code is just showing its age a little.  I still think the validity
> check should be against -1.
> 
> Yes I agree, it seems to me that there is something inconsistent in GDB.
> 
> The current default value is 0, we set all regnums to 0 initially in gdb/i386-
> tdep.h And we set it to -1 to indicate the absence of that specific register.
> 
> But on the other hand the enums (enum amd64_regnum/enum
> i386_regnum) defining the register numbers start at 0.
> 
> So that seems to be a separate issue one could consider to fix.
> Maybe the default should be simply -1, instead of 0 ?
> 
> But for my specific patch here the best would be to compare against against
> -1, I agree and will fix.

I created this patch to address the regnum defaults as suggested above:

https://sourceware.org/pipermail/gdb-patches/2025-August/219649.html

Kind Regards,
Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-07-29 14:46   ` Andrew Burgess
  2025-07-30  1:55     ` Thiago Jung Bauermann
@ 2025-08-04 12:45     ` Schimpe, Christina
  1 sibling, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-04 12:45 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi Andrew, 

Thank you for the feedback. Please find my comments to your feedback below.
I'll only comment on the non-test part, since for the test I already replied in:
https://sourceware.org/pipermail/gdb-patches/2025-July/219525.html


> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Tuesday, July 29, 2025 4:47 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with
> shadow stack.
> 
> Christina Schimpe <christina.schimpe@intel.com> writes:
> 
> > Intel's Control-Flow Enforcement Technology (CET) provides the shadow
> > stack feature for the x86 architecture.
> >
> > This commit adds support to write and read the shadow-stack node in
> > corefiles.  This helps debugging return address violations post-mortem.
> > The format is synced with the linux kernel commit "x86: Add PTRACE
> > interface for shadow stack".  As the linux kernel restricts shadow
> > stack support to 64-bit, apply the fix for amd64 only.
> >
> > Co-Authored-By: Christina Schimpe <christina.schimpe@intel.com>
> >
> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> > ---
> >  gdb/amd64-linux-tdep.c                        |  57 ++++++++-
> >  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 +++++++
> >  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110
> > ++++++++++++++++++
> >  3 files changed, 205 insertions(+), 4 deletions(-)  create mode
> > 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> >  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
> >
> > diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index
> > edb7d8da6ab..9af7a41ea26 100644
> > --- a/gdb/amd64-linux-tdep.c
> > +++ b/gdb/amd64-linux-tdep.c
> > @@ -47,6 +47,7 @@
> >  #include "expop.h"
> >  #include "arch/amd64-linux-tdesc.h"
> >  #include "inferior.h"
> > +#include "x86-tdep.h"
> >
> >  /* The syscall's XML filename for i386.  */  #define
> > XML_SYSCALL_FILENAME_AMD64 "syscalls/amd64-linux.xml"
> > @@ -1593,6 +1594,14 @@ amd64_linux_record_signal (struct gdbarch
> *gdbarch,
> >    return 0;
> >  }
> >
> > +/* Get shadow stack pointer state from core dump.  */
> > +
> > +static bool
> > +amd64_linux_core_read_ssp_state_p (bfd *abfd) {
> > +  return bfd_get_section_by_name (abfd, ".reg-ssp") != nullptr;
> 
> I think the comment on this function is not correct.  We're not getting the
> shadow stack pointer state, we're:
> 
>   /* Return true if the core file ABFD contains shadow stack pointer
>      state.  Otherwise, return false.  */

Agree, will add.

> 
> > +}
> > +
> >  /* Get Linux/x86 target description from core dump.  */
> >
> >  static const struct target_desc *
> > @@ -1602,11 +1611,14 @@ amd64_linux_core_read_description (struct
> > gdbarch *gdbarch,  {
> >    /* Linux/x86-64.  */
> >    x86_xsave_layout layout;
> > -  uint64_t xcr0 = i386_linux_core_read_xsave_info (abfd, layout);
> > -  if (xcr0 == 0)
> > -    xcr0 = X86_XSTATE_SSE_MASK;
> > +  uint64_t xstate_bv_mask = i386_linux_core_read_xsave_info (abfd,
> > + layout);
> 
> As with feedback on earlier patches, I don't think the inclusion of '_mask' is
> a good one here.  I think what you call xstate_bv_mask is an actual value,
> not a mask, and should be named 'xstate_bv'

I agree and will fix.

> > +  if (xstate_bv_mask == 0)
> > +    xstate_bv_mask = X86_XSTATE_SSE_MASK;
> > +
> > +  if (amd64_linux_core_read_ssp_state_p (abfd))
> > +    xstate_bv_mask |= X86_XSTATE_CET_U;
> >
> > -  return amd64_linux_read_description (xcr0 & X86_XSTATE_ALL_MASK,
> > +  return amd64_linux_read_description (xstate_bv_mask &
> > + X86_XSTATE_ALL_MASK,
> >  				       gdbarch_ptr_bit (gdbarch) == 32);  }
> >
> > @@ -1637,6 +1649,35 @@ static const struct regset
> amd64_linux_xstateregset =
> >      amd64_linux_collect_xstateregset
> >    };
> >
> > +/* Supply shadow stack pointer register from SSP to the register cache
> > +   REGCACHE.  */
> > +
> > +static void
> > +amd64_linux_supply_ssp (const regset *regset,
> > +			regcache *regcache, int regnum,
> > +			const void *ssp, size_t len)
> > +{
> > +  x86_supply_ssp (regcache, *static_cast<const uint64_t *> (ssp));
> 
> Before this line I'd like to see:
> 
>   gdb_assert (len == sizeof (uint64_t));

Agree, will add.

> > +}
> > +
> > +/* Collect the shadow stack pointer register from the register cache
> > +   REGCACHE and store it in SSP.  */
> > +
> > +static void
> > +amd64_linux_collect_ssp (const regset *regset,
> > +			 const regcache *regcache, int regnum,
> > +			 void *ssp, size_t len)
> > +{
> > +  x86_collect_ssp (regcache, *static_cast<uint64_t *> (ssp));
> 
> And I think this should get the same 'len == sizeof ...' assert as above please.

Agree, will add.

> > +}
> > +
> > +/* Shadow stack pointer register.  */
> > +
> > +static const struct regset amd64_linux_ssp_register
> > +  {
> > +    NULL, amd64_linux_supply_ssp, amd64_linux_collect_ssp
> > +  };
> > +
> >  /* Iterate over core file register note sections.  */
> >
> >  static void
> > @@ -1653,6 +1694,14 @@ amd64_linux_iterate_over_regset_sections
> (struct gdbarch *gdbarch,
> >      cb (".reg-xstate", tdep->xsave_layout.sizeof_xsave,
> >  	tdep->xsave_layout.sizeof_xsave, &amd64_linux_xstateregset,
> >  	"XSAVE extended state", cb_data);
> > +
> > +  /* SSP can be unavailable.  Thus, we need to check the register status
> > +     in case we write a core file (regcache != nullptr).  */  if
> > + (tdep->ssp_regnum > 0
> > +      && (regcache == nullptr

I'll change the 

"tdep->ssp_regnum > 0"

to

"tdep->ssp_regnum != -1"

as discussed already.

> Have you really seen this function called with 'regcache == nullptr' ?
> I'm suspect not as e.g. i386_supply_gregset, which is part of i386_gregset,
> makes an unchecked dereference of regcache, so if regcache was nullptr
> you'd see UB (crash) before getting to this check.


We see this if we load a corefile:
 ~~~
(gdb) core core.3499583
[New LWP 3499583]
Reading symbols from /tmp/main...

Thread 1 "gdb-up" hit Breakpoint 1, amd64_linux_iterate_over_regset_sections (gdbarch=0x5555589cc140, 
    cb=0x555555fdd64f <get_core_registers_cb(char const*, int, int, regset const*, char const*, void*)>, cb_data=0x7fffffffad20, regcache=0x0)
    at /tmp/gdb/amd64-linux-tdep.c:1694
1694	  i386_gdbarch_tdep *tdep = gdbarch_tdep<i386_gdbarch_tdep> (gdbarch)
(gdb) bt
#0  amd64_linux_iterate_over_regset_sections (gdbarch=0x5555589cc140, cb=0x555555fdd64f <get_core_registers_cb(char const*, int, int, regset const*, char const*, void*)>, 
    cb_data=0x7fffffffad20, regcache=0x0) at / tmp/gdb/amd64-linux-tdep.c:1694
#1  0x0000555555e15491 in gdbarch_iterate_over_regset_sections (gdbarch=0x5555589cc140, cb=0x555555fdd64f <get_core_registers_cb(char const*, int, int, regset const*, char const*, void*)>, 
    cb_data=0x7fffffffad20, regcache=0x0) at /tmp/gdb/gdbarch-gen.c:3840
#2  0x0000555555fdd876 in core_target::fetch_registers (this=0x5555588ee4e0, regcache=0x5555589c9120, regno=16) at /tmp /gdb/corelow.c:1425
#3  0x0000555556744d0a in target_fetch_registers (regcache=0x5555589c9120, regno=16) at /tmp/gdb/target.c:3915
[...]
~~~

In corelow.c: core_target::fetch_registers we can also see that that we pass the nullptr:
~~~
  struct gdbarch *gdbarch = regcache->arch ();
  get_core_registers_cb_data data = { this, regcache };
  gdbarch_iterate_over_regset_sections (gdbarch,
					get_core_registers_cb,
					(void *) &data, NULL);
~~~

> > +	  || REG_VALID == regcache->get_register_status (tdep-
> >ssp_regnum)))
> > +    cb (".reg-ssp", 8, 8, &amd64_linux_ssp_register,
> > +	"shadow stack pointer", cb_data);
> >  }
> >  /* The instruction sequences used in x86_64 machines for a 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 00000000000..f078e33810d
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> > @@ -0,0 +1,42 @@
> > +/* 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;
> > +  asm volatile("xor %0, %0; rdsspq %0" : "=r" (ssp));
> > +
> > +  /* 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 00000000000..8784fc3622c
> > --- /dev/null
> > +++ b/gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.exp
> > @@ -0,0 +1,110 @@
> > +# Copyright 2021-2024 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.
> > +
> > +require allow_ssp_tests
> > +
> > +standard_testfile
> > +
> > +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
> > +    }
> > +
> > +    # 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"]
> > +
> > +    # 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 != ""}]
> > +    set os_core_name "${binfile}.core"
> > +    remote_exec build "mv $core_filename $os_core_name"
> > +    set core_filename $os_core_name
> 
> I'm wondering what the point of this core_filename / os_core_name stuff
> is?  My reading of `core_find` is that the returned core_filename will be
> '${binfile}.core', so this whole thing feels redundant, but maybe I'm missing
> something here?
> 
> > +
> > +    # 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.
> > +
> > +    if {$gcore_generated} {
> > +	clean_restart $binfile
> > +
> > +	with_test_prefix "gcore corefile" {
> > +	    check_core_file $gcore_filename $ssp_in_gcore
> > +	}
> > +    } else {
> > +	fail "gcore corefile not generated"
> 
> It's better, where possible, to avoid having pass/fail results that only show
> up down some code paths.
> 
> In this case it's easy to avoid having a stray 'fail' by restructuring the code
> too:
> 
>   gdb_assert { $gcore_generated } "gcore corefile created"
>   if { $gcore_generated } {
>     ... etc ...
>   }
> 
> Now you'll always have either a pass or fail based on the gcore being
> generated.
> 
> There is also the helper proc `gcore_cmd_available`.  I'd guess for any
> x86 target that supports SSP, gcore will be available, but in theory you could
> consider using this to avoid a fail when gcore is not available maybe?
> 
> > +    }
> > +
> > +    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
> 
> I'd move the blank line after the 'close' personally.
> 
> Thanks,
> Andrew
> 
> 
> > +	}
> > +    } else {
> > +	untested "OS corefile not generated"
> > +    }
> > +}
> > --
> > 2.43.0

Kind Regards,
Christina

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer.
  2025-07-30 12:22   ` Andrew Burgess
@ 2025-08-04 13:01     ` Schimpe, Christina
  2025-08-14 15:50       ` Andrew Burgess
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-04 13:01 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi Andrew,

Thanks for the feedback. Please find my comments to your feedback below.

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Wednesday, July 30, 2025 2:22 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to
> get the shadow stack pointer.
> 
> Christina Schimpe <christina.schimpe@intel.com> writes:
> 
> > This patch is required by the following commit
> > "gdb: Enable displaced stepping with shadow stack on amd64 linux."
> >
> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> > Reviewed-By: Luis Machado <luis.machado@arm.com>
> > ---
> >  gdb/arch-utils.c          | 10 ++++++++++
> >  gdb/arch-utils.h          |  5 +++++
> >  gdb/gdbarch-gen.c         | 22 ++++++++++++++++++++++
> >  gdb/gdbarch-gen.h         | 12 +++++++++++-
> >  gdb/gdbarch_components.py | 17 ++++++++++++++++-
> >  5 files changed, 64 insertions(+), 2 deletions(-)
> >
> > diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index
> > f320d3d7365..c396e9e3840 100644
> > --- a/gdb/arch-utils.c
> > +++ b/gdb/arch-utils.c
> > @@ -1218,6 +1218,16 @@ default_gdbarch_return_value
> >  				readbuf, writebuf);
> >  }
> >
> > +/* See arch-utils.h.  */
> > +
> > +std::optional<CORE_ADDR>
> > +default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache
> *regcache,
> > +				  bool &shadow_stack_enabled)
> > +{
> > +  shadow_stack_enabled = false;
> > +  return {};
> > +}
> > +
> >  obstack *gdbarch_obstack (gdbarch *arch)  {
> >    return &arch->obstack;
> > diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index
> > 1509cb7441e..14a84b74733 100644
> > --- a/gdb/arch-utils.h
> > +++ b/gdb/arch-utils.h
> > @@ -414,4 +414,9 @@ extern enum return_value_convention
> default_gdbarch_return_value
> >        struct regcache *regcache, struct value **read_value,
> >        const gdb_byte *writebuf);
> >
> > +/* Default implementation of gdbarch default_get_shadow_stack_pointer
> > +   method.  */
> > +extern std::optional<CORE_ADDR> default_get_shadow_stack_pointer
> > +  (gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
> > +
> >  #endif /* GDB_ARCH_UTILS_H */
> > diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index
> > a4b72793fd8..caeda3cefae 100644
> > --- a/gdb/gdbarch-gen.c
> > +++ b/gdb/gdbarch-gen.c
> > @@ -263,6 +263,7 @@ struct gdbarch
> >    gdbarch_use_target_description_from_corefile_notes_ftype
> *use_target_description_from_corefile_notes =
> default_use_target_description_from_corefile_notes;
> >    gdbarch_core_parse_exec_context_ftype *core_parse_exec_context =
> default_core_parse_exec_context;
> >    gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
> > +  gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer =
> > + default_get_shadow_stack_pointer;
> >  };
> >
> >  /* Create a new ``struct gdbarch'' based on information provided by
> > @@ -537,6 +538,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
> >    /* Skip verify of use_target_description_from_corefile_notes, invalid_p
> == 0.  */
> >    /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
> >    /* Skip verify of shadow_stack_push, has predicate.  */
> > +  /* Skip verify of get_shadow_stack_pointer, invalid_p == 0.  */
> >    if (!log.empty ())
> >      internal_error (_("verify_gdbarch: the following are invalid ...%s"),
> >  		    log.c_str ());
> > @@ -1414,6 +1416,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct
> ui_file *file)
> >    gdb_printf (file,
> >  	      "gdbarch_dump: shadow_stack_push = <%s>\n",
> >  	      host_address_to_string (gdbarch->shadow_stack_push));
> > +  gdb_printf (file,
> > +	      "gdbarch_dump: get_shadow_stack_pointer = <%s>\n",
> > +	      host_address_to_string (gdbarch->get_shadow_stack_pointer));
> >    if (gdbarch->dump_tdep != NULL)
> >      gdbarch->dump_tdep (gdbarch, file);  } @@ -5583,3 +5588,20 @@
> > set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,  {
> >    gdbarch->shadow_stack_push = shadow_stack_push;  }
> > +
> > +std::optional<CORE_ADDR>
> > +gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache
> > +*regcache, bool &shadow_stack_enabled) {
> > +  gdb_assert (gdbarch != NULL);
> > +  gdb_assert (gdbarch->get_shadow_stack_pointer != NULL);
> > +  if (gdbarch_debug >= 2)
> > +    gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer
> > +called\n");
> > +  return gdbarch->get_shadow_stack_pointer (gdbarch, regcache,
> > +shadow_stack_enabled); }
> > +
> > +void
> > +set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
> > +
> gdbarch_get_shadow_stack_pointer_ftype
> > +get_shadow_stack_pointer) {
> > +  gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer; }
> > diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index
> > 71142332540..c36171b089e 100644
> > --- a/gdb/gdbarch-gen.h
> > +++ b/gdb/gdbarch-gen.h
> > @@ -1807,7 +1807,8 @@ extern void
> set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc
> >     technologies.  For example, the Intel Control-Flow Enforcement
> Technology
> >     (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
> >     To enable shadow stack support for inferior calls the
> shadow_stack_push
> > -   gdbarch hook has to be provided.
> > +   gdbarch hook has to be provided.  The get_shadow_stack_pointer
> gdbarch
> > +   hook has to be provided to enable displaced stepping.
> >
> >     Push NEW_ADDR to the shadow stack and update the shadow stack
> > pointer. */
> >
> > @@ -1816,3 +1817,12 @@ extern bool gdbarch_shadow_stack_push_p
> (struct
> > gdbarch *gdbarch);  typedef void (gdbarch_shadow_stack_push_ftype)
> > (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
> > extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
> > CORE_ADDR new_addr, regcache *regcache);  extern void
> > set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
> > gdbarch_shadow_stack_push_ftype *shadow_stack_push);
> > +
> > +/* If possible, return the shadow stack pointer.  On some architectures,
> the
> > +   shadow stack pointer is available even if the feature is disabled.  To
> > +   return the feature's enablement state configure
> SHADOW_STACK_ENABLED.
> > +   Set it to true in case the shadow stack is enabled. */
> > +
> > +typedef std::optional<CORE_ADDR>
> > +(gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch,
> > +regcache *regcache, bool &shadow_stack_enabled); extern
> > +std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct
> > +gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
> > +extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch
> > +*gdbarch, gdbarch_get_shadow_stack_pointer_ftype
> > +*get_shadow_stack_pointer);
> > diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
> > index abc79588473..73459064170 100644
> > --- a/gdb/gdbarch_components.py
> > +++ b/gdb/gdbarch_components.py
> > @@ -2855,7 +2855,8 @@ Some targets support special hardware-
> assisted
> > control-flow protection  technologies.  For example, the Intel
> > Control-Flow Enforcement Technology  (Intel CET) on x86 provides a
> shadow stack and indirect branch tracking.
> >  To enable shadow stack support for inferior calls the
> > shadow_stack_push -gdbarch hook has to be provided.
> > +gdbarch hook has to be provided.  The get_shadow_stack_pointer
> > +gdbarch hook has to be provided to enable displaced stepping.
> 
> I find the addition of this last sentence here a little strange.  While it's a true
> statement, wouldn't this be better placed on the comment for
> get_shadow_stack_pointer?

Mh I see the initial comment section here rather as general overview for the
shadow stack feature in GDB. It explains which features (e.g. infcalls and displaced
stepping) are interacting with shadow stacks.  So in my opinion it's okay to keep it as is.

> >
> >  Push NEW_ADDR to the shadow stack and update the shadow stack
> pointer.
> >  """,
> > @@ -2864,3 +2865,17 @@ Push NEW_ADDR to the shadow stack and
> update the shadow stack pointer.
> >      params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
> >      predicate=True,
> >  )
> > +
> > +Method(
> > +    comment="""
> > +If possible, return the shadow stack pointer.  On some architectures,
> > +the shadow stack pointer is available even if the feature is
> > +disabled.  To return the feature's enablement state configure
> SHADOW_STACK_ENABLED.
> > +Set it to true in case the shadow stack is enabled.
> 
> The wording "configure SHADOW_STACK_ENABLED" seems a little strange.
> Also, there's a bunch of important detail that this comment doesn't cover.
> Here's what I'd suggest, though it's possible this doesn't match the
> implementation (I haven't checked the next patch yet), but this does match
> default_get_shadow_stack_pointer.  Feel free to take any of this that is
> useful:
> 
>   If possible, return the shadow stack pointer.  On some architectures,
>   the shadow stack pointer is available even if the feature is disabled.
>   If the shadow stack feature is enabled then set SHADOW_STACK_ENABLED
>   to true, otherwise set SHADOW_STACK_ENABLED to false.  The
>   SHADOW_STACK_ENABLED will always be set if this function returns a
>   value.  If the function doesn't return a value then the state of
>   SHADOW_STACK_ENABLED is undefined.

Hm, I am not sure if I am missing something here.

We set SHADOW_STACK_ENABLED to false in default_get_shadow_stack_pointer
and *do not*  return a value. 
~~~
std::optional<CORE_ADDR>
default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
				  bool &shadow_stack_enabled)
{
  shadow_stack_enabled = false;
  return {};
}
~~~

What do you think about the following:
If possible, return the shadow stack pointer.  If the shadow stack feature is enabled
then set SHADOW_STACK_ENABLED to true, otherwise set SHADOW_STACK_ENABLED
to false.
On some architectures, the shadow stack pointer is available even if the feature is disabled.
So dependent on the target, an implementation of this function may return a valid shadow
stack pointer, but set  SHADOW_STACK_ENABLED to false.  


> 
> > +""",
> > +    type="std::optional<CORE_ADDR>",
> > +    name="get_shadow_stack_pointer",
> > +    params=[("regcache *", "regcache"), ("bool &",
> "shadow_stack_enabled")],
> > +    predefault="default_get_shadow_stack_pointer",
> > +    invalid=False,
> > +)
> > --
> > 2.43.0

Kind Regards
Christina

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-07-30 11:42       ` Schimpe, Christina
@ 2025-08-04 15:28         ` Schimpe, Christina
  2025-08-05  4:29           ` Thiago Jung Bauermann
  0 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-04 15:28 UTC (permalink / raw)
  To: Thiago Jung Bauermann, Andrew Burgess; +Cc: gdb-patches, luis.machado

Hi Thiago and Andrew,

Now that I have tested this a bit I actually have some more comments on the test.

> -----Original Message-----
> From: Schimpe, Christina
> Sent: Wednesday, July 30, 2025 1:43 PM
> To: Thiago Jung Bauermann <thiago.bauermann@linaro.org>; Andrew
> Burgess <aburgess@redhat.com>
> Cc: gdb-patches@sourceware.org; luis.machado@arm.com
> Subject: RE: [PATCH v5 07/12] gdb: amd64 linux coredump support with
> shadow stack.
> 
> Hi Thiago and Andrew,
> 
> Thanks a lot for your feedback.
> 
> I should have mentioned that I created this test based on the test code from
> Thiago's series GCS core dump tests. I only mentioned it in the cover letter
> so far.
> 
> I'll do it similar to Thiago when he based his return command testing
> (aarch64-gcs-return.exp ) on CET shadow stack tests:
> https://sourceware.org/pipermail/gdb-patches/2025-June/218895.html
> 
> So I'll add a comment in the test description
> 
> # Test the shadow stack pointer note in core dumps.
> # Based on the corefile tests in gdb.arch/aarch64-gcs-core.exp.
> 
> and also a comment in the commit message
> 
>     ---
>     The code and testcase are lightly adapted from:
> 
>     [PATCH v3 5/9] GDB, gdbserver: aarch64-linux: Initial Guarded Control
> Stack support
> 
>     https://sourceware.org/pipermail/gdb-patches/2025-June/218892.html
> 
> in the next version.
> Also I'll fix my test based on Andrew's feedback as described by Thiago
> below.
> 
> 
> > -----Original Message-----
> > From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> > Sent: Wednesday, July 30, 2025 3:55 AM
> > To: Andrew Burgess <aburgess@redhat.com>
> > Cc: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> > patches@sourceware.org; luis.machado@arm.com
> > Subject: Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with
> > shadow stack.
> >
> > Hello Andrew,
> >
> > Christina based the testcase in this patch on aarch64-gcs-core.exp
> > from my GCS patch series and your comments on it also apply to my
> > patch, so I'm replying on account of that.
> >
> > Andrew Burgess <aburgess@redhat.com> writes:
> >
> > > Christina Schimpe <christina.schimpe@intel.com> writes:
> > >
> > >> +    # 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.  Save test program output to
> > >> +    # ${binfile}.out.
> > >> +    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
> > >
> > > I'm wondering what the point of this core_filename / os_core_name
> > > stuff is?  My reading of `core_find` is that the returned
> > > core_filename will be '${binfile}.core', so this whole thing feels
> > > redundant, but maybe I'm missing something here?
> >
> > I wrote this part. The answer to your question is very simple: I don't
> > know what I was thinking. I just deleted the last 3 lines from the GCS
> testcase.
> >
> > >> +
> > >> +    # 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.
> > >> +
> > >> +    if {$gcore_generated} {
> > >> +	clean_restart $binfile
> > >> +
> > >> +	with_test_prefix "gcore corefile" {
> > >> +	    check_core_file $gcore_filename $ssp_in_gcore
> > >> +	}
> > >> +    } else {
> > >> +	fail "gcore corefile not generated"
> > >
> > > It's better, where possible, to avoid having pass/fail results that
> > > only show up down some code paths.
> > >
> > > In this case it's easy to avoid having a stray 'fail' by
> > > restructuring the code too:
> > >
> > >   gdb_assert { $gcore_generated } "gcore corefile created"
> > >   if { $gcore_generated } {
> > >     ... etc ...
> > >   }
> > >
> > > Now you'll always have either a pass or fail based on the gcore
> > > being generated.
> >
> > Good idea. I did that for aarch64-gcs-core.exp.

If no OS corefile is found we will see a FAIL here.
The usual coredump testing doesn't fail in case the coredump file is not found.
So all gdb.base/corefile*.exp tests have sth. like:

set corefile [core_find $binfile {}]
if {$corefile == ""} {
    return
}

This can happen in case corefiles are managed, for instance, by apport on ubuntu.
Do we want a different behaviour ?


> > > There is also the helper proc `gcore_cmd_available`.  I'd guess for
> > > any
> > > x86 target that supports SSP, gcore will be available, but in theory
> > > you could consider using this to avoid a fail when gcore is not
> > > available maybe?
> >
> > In the GCS core testcase, I moved the gcore tests after the OS
> > corefile ones, with an
> >
> >   if ![gcore_cmd_available] {
> >       return
> >   }
> >
> > in the middle, and after that I had to redo some GDB setup:
> >
> >   clean_restart $binfile
> >
> >   if ![runto $linespec] {
> >       return
> >   }

Do you also do the "continue until crash part" again?
It would make sense to move it into a proc then:
~~~
# Continue until the expected crash at the return instruction.

proc continue_until_crash {} {
    global hex decimal

    # 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" \
[...]
~~~

And my gcore testing now looks as follows:

~~~
    if ![gcore_cmd_available] {
	return
    }

    clean_restart $binfile

    if ![runto $linespec] {
	return
    }

    with_test_prefix "gcore corefile" {
	continue_until_crash

	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
	    check_core_file $gcore_filename $ssp_in_gcore
	}
    }
~~~

> > But I agree it does make it more resilient/correct. Thanks for the
> suggestion.
> >
> > >> +    }
> > >> +
> > >> +    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
> > >
> > > I'd move the blank line after the 'close' personally.
> >
> > I also had that layout. Changed.
> >
> > --
> > Thiago
> 
> Christina

Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-08-04 15:28         ` Schimpe, Christina
@ 2025-08-05  4:29           ` Thiago Jung Bauermann
  2025-08-05 15:29             ` Schimpe, Christina
  2025-08-06 20:52             ` Luis
  0 siblings, 2 replies; 67+ messages in thread
From: Thiago Jung Bauermann @ 2025-08-05  4:29 UTC (permalink / raw)
  To: Schimpe, Christina; +Cc: Andrew Burgess, gdb-patches, luis.machado

Hello,

"Schimpe, Christina" <christina.schimpe@intel.com> writes:

>> > >> +
>> > >> +    # 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.
>> > >> +
>> > >> +    if {$gcore_generated} {
>> > >> +	clean_restart $binfile
>> > >> +
>> > >> +	with_test_prefix "gcore corefile" {
>> > >> +	    check_core_file $gcore_filename $ssp_in_gcore
>> > >> +	}
>> > >> +    } else {
>> > >> +	fail "gcore corefile not generated"
>> > >
>> > > It's better, where possible, to avoid having pass/fail results that
>> > > only show up down some code paths.
>> > >
>> > > In this case it's easy to avoid having a stray 'fail' by
>> > > restructuring the code too:
>> > >
>> > >   gdb_assert { $gcore_generated } "gcore corefile created"
>> > >   if { $gcore_generated } {
>> > >     ... etc ...
>> > >   }
>> > >
>> > > Now you'll always have either a pass or fail based on the gcore
>> > > being generated.
>> >
>> > Good idea. I did that for aarch64-gcs-core.exp.
>
> If no OS corefile is found we will see a FAIL here.
> The usual coredump testing doesn't fail in case the coredump file is not found.
> So all gdb.base/corefile*.exp tests have sth. like:
>
> set corefile [core_find $binfile {}]
> if {$corefile == ""} {
>     return
> }
>
> This can happen in case corefiles are managed, for instance, by apport on ubuntu.
> Do we want a different behaviour ?

Interesting point. Perhaps a FAIL Isn't the best result to report in
this case, but IMHO it would be worth reporting an UNTESTED or perhaps
UNSUPPORTED result rather than silently returning.

I don't have a strong opinion on this matter though.

>> > > There is also the helper proc `gcore_cmd_available`.  I'd guess for
>> > > any
>> > > x86 target that supports SSP, gcore will be available, but in theory
>> > > you could consider using this to avoid a fail when gcore is not
>> > > available maybe?
>> >
>> > In the GCS core testcase, I moved the gcore tests after the OS
>> > corefile ones, with an
>> >
>> >   if ![gcore_cmd_available] {
>> >       return
>> >   }
>> >
>> > in the middle, and after that I had to redo some GDB setup:
>> >
>> >   clean_restart $binfile
>> >
>> >   if ![runto $linespec] {
>> >       return
>> >   }
>
> Do you also do the "continue until crash part" again?

No, at least in my version of the test the "continue until crash" part
is only needed once, for the gcore test. The OS corefile test doesn't
need it.

> It would make sense to move it into a proc then:
> ~~~
> # Continue until the expected crash at the return instruction.
>
> proc continue_until_crash {} {
>     global hex decimal
>
>     # 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" \
> [...]
> ~~~
>
> And my gcore testing now looks as follows:
>
> ~~~
>     if ![gcore_cmd_available] {
> 	return

Come to think of it, I think an untested/unsupported would be good here
as well, if I'm going to be consistent in my opinion.

>     }
>
>     clean_restart $binfile
>
>     if ![runto $linespec] {
> 	return
>     }
>
>     with_test_prefix "gcore corefile" {
> 	continue_until_crash
>
> 	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
> 	    check_core_file $gcore_filename $ssp_in_gcore
> 	}
>     }
> ~~~

For reference, my test is here:

https://gitlab.com/bauermann/binutils-gdb/-/blob/guarded-control-stack-patches/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp

-- 
Thiago

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.
  2025-07-23 12:47       ` Schimpe, Christina
@ 2025-08-05 13:47         ` Andrew Burgess
  0 siblings, 0 replies; 67+ messages in thread
From: Andrew Burgess @ 2025-08-05 13:47 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches; +Cc: thiago.bauermann, luis.machado

"Schimpe, Christina" <christina.schimpe@intel.com> writes:

> Hi Andrew,
>
> Do you have any final comments ?
> My plan would be to fix your comments locally (as described below) and push the series by tomorrow.
> Or should I repost this patch to be sure?
>
> Thanks,
> Christina
>
> From: Schimpe, Christina <christina.schimpe@intel.com>
> Sent: Tuesday, July 15, 2025 12:28 PM
> To: Andrew Burgess <aburgess@redhat.com>; gdb-patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86.
>
> Hi Andrew,
>
> Thanks a lot for the review.
>
>>> The XSAVE features set is organized in state components, which are a set
>>> of or parts of registers.  So-called XSAVE-supported features are
>
>> Maybe here you mean: "..., which are a set of, or parts of, registers."
>> If not, then this sentence doesn't make sense to me
>
> I agree, this sentence is a bit confusing. I think I would prefer to write it out actually.
> Is the following more understandable?
> "The XSAVE function set is organized in state components, which are a set of registers
> or parts of registers."
>
>>> The SDM uses the term xstate_bv for a state-component bitmap, which is
>
>> Would it be possible to define what SDM it please.
>
> Do you mean writing it out to "Intel Software Developer's Manual" ?

Yes, just the first time SDM is referenced.  Ideally these commit
messages should be written for a general audience, who might not be
familiar with Intel acronyms.

>> I'd like to ask about the use of 'mask' in this variable name.  We used
>> to pass in an xcr0 value, which was then combined with various masks to
>> extract the state bits we were interested in.
>
>> You've renamed the new variable as a mask, but continue to combine it
>> with the same masks (e.g. X86_XSTATE_AVX), which makes me suspect the
>> variable is not a mask at all.
>
> It's not a mask at all. I honestly cannot tell why I decided to call this xstate_bv
> instead of xstate_bv_mask when I wrote this patch...
>
>> Now, I can see where the confusion might have come from.  Almost every
>> call to amd64_target_description passes in a *_MASK constant.  But
>> that's OK, given these are bit sets, then the MASK constants actually
>> define valid values.
>
>> I am aware that this sounds like a trivial complaint over a variable
>> name, but I think calling this a mask is pretty confusing (at least to
>> me), so unless I'm not understanding this, then I think it is worth
>> renaming this.
>
>> The use of `mask` for the value is present throughout this patch, not
>> just this one function.
>
> I agree and am glad for your feedback here.
> The current naming is confusing and I will rename it to xstate_bv.

Sounds great.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-07-25 15:03     ` Schimpe, Christina
  2025-08-01 12:54       ` Schimpe, Christina
@ 2025-08-05 13:57       ` Andrew Burgess
  2025-08-06 19:53         ` Schimpe, Christina
  1 sibling, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-08-05 13:57 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches; +Cc: thiago.bauermann, luis.machado

"Schimpe, Christina" <christina.schimpe@intel.com> writes:

> Hi Andrew,
>
> Thanks a lot for the review! I have some questions for your feedback, please
> find my comments below.
>
>> -----Original Message-----
>> From: Andrew Burgess <aburgess@redhat.com>
>> Sent: Friday, July 25, 2025 2:50 PM
>> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
>> patches@sourceware.org
>> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
>> Subject: Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow
>> stack pointer register.
>> 
>> Christina Schimpe <christina.schimpe@intel.com> writes:
>> 
>> > This patch adds the user mode register PL3_SSP which is part of the
>> > Intel(R) Control-Flow Enforcement Technology (CET) feature for support
>> > of shadow stack.
>> > For now, only native and remote debugging support for shadow stack
>> > userspace on amd64 linux are covered by this patch including 64 bit
>> > and
>> > x32 support.  32 bit support is not covered due to missing Linux
>> > kernel support.
>> >
>> > This patch requires fixing the test gdb.base/inline-frame-cycle-unwind
>> > which is failing in case the shadow stack pointer is unavailable.
>> > Such a state is possible if shadow stack is disabled for the current
>> > thread but supported by HW.
>> >
>> > This test uses the Python unwinder inline-frame-cycle-unwind.py which
>> > fakes the cyclic stack cycle by reading the pending frame's registers
>> > and adding them to the unwinder:
>> >
>> > ~~~
>> > for reg in pending_frame.architecture().registers("general"):
>> >      val = pending_frame.read_register(reg)
>> >      unwinder.add_saved_register(reg, val)
>> >      return unwinder
>> > ~~~
>> >
>> > However, in case the python unwinder is used we add a register
>> > (pl3_ssp) that is unavailable.  This leads to a NOT_AVAILABLE_ERROR
>> > caught in gdb/frame-unwind.c:frame_unwind_try_unwinder and it is
>> > continued with standard unwinders.  This destroys the faked cyclic
>> > behavior and the stack is further unwinded after frame 5.
>> >
>> > In the working scenario an error should be triggered:
>> > ~~~
>> > bt
>> > 0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
>> > 1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
>> > 2  0x000055555555516e in inline_func () at
>> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
>> > 3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
>> > 4  0x000055555555516e in inline_func () at
>> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
>> > 5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
>> > Backtrace stopped: previous frame identical to this frame (corrupt
>> > stack?)
>> > (gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5:
>> > backtrace when the unwind is broken at frame 5 ~~~
>> >
>> > To fix the Python unwinder, we simply skip the unavailable registers.
>> >
>> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
>> > Reviewed-By: Luis Machado <luis.machado@arm.com>
>> > ---
>> >  gdb/NEWS                                      |  3 +
>> >  gdb/amd64-linux-nat.c                         | 17 +++++
>> >  gdb/amd64-linux-tdep.c                        |  1 +
>> >  gdb/amd64-tdep.c                              |  6 +-
>> >  gdb/amd64-tdep.h                              |  1 +
>> >  gdb/arch/amd64.c                              | 10 +++
>> >  gdb/arch/i386.c                               |  4 ++
>> >  gdb/arch/x86-linux-tdesc-features.c           |  1 +
>> >  gdb/doc/gdb.texinfo                           |  4 ++
>> >  gdb/features/Makefile                         |  2 +
>> >  gdb/features/i386/32bit-ssp.c                 | 14 ++++
>> >  gdb/features/i386/32bit-ssp.xml               | 11 +++
>> >  gdb/features/i386/64bit-ssp.c                 | 14 ++++
>> >  gdb/features/i386/64bit-ssp.xml               | 11 +++
>> >  gdb/i386-tdep.c                               | 22 +++++-
>> >  gdb/i386-tdep.h                               |  4 ++
>> >  gdb/nat/x86-linux-tdesc.c                     |  2 +
>> >  gdb/nat/x86-linux.c                           | 57 +++++++++++++++
>> >  gdb/nat/x86-linux.h                           |  4 ++
>> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
>> >  gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
>> >  .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
>> >  gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
>> >  gdb/x86-linux-nat.c                           | 49 +++++++++++--
>> >  gdb/x86-linux-nat.h                           | 11 +++
>> >  gdb/x86-tdep.c                                | 21 ++++++
>> >  gdb/x86-tdep.h                                |  9 +++
>> >  gdbserver/linux-x86-low.cc                    | 28 +++++++-
>> >  gdbsupport/x86-xstate.h                       |  5 +-
>> >  29 files changed, 447 insertions(+), 10 deletions(-)  create mode
>> > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
>> > gdb/features/i386/32bit-ssp.xml  create mode 100644
>> > gdb/features/i386/64bit-ssp.c  create mode 100644
>> > gdb/features/i386/64bit-ssp.xml  create mode 100644
>> > gdb/testsuite/gdb.arch/amd64-shadow-stack.c
>> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
>> >
>> 
>> 
>> > diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index
>> > dbb9b3223cb..4df99ccca54 100644
>> > --- a/gdb/amd64-linux-nat.c
>> > +++ b/gdb/amd64-linux-nat.c
>> > @@ -32,6 +32,7 @@
>> >  #include "amd64-tdep.h"
>> >  #include "amd64-linux-tdep.h"
>> >  #include "i386-linux-tdep.h"
>> > +#include "x86-tdep.h"
>> >  #include "gdbsupport/x86-xstate.h"
>> >
>> >  #include "x86-linux-nat.h"
>> > @@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers (struct
>> > regcache *regcache, int regnum)
>> >
>> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
>> >  	{
>> > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
>> > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
>> 
>> It's really nit-picking, but I don't think the '>' here check is correct.  You're
>> checking that tdep->ssp_regnum has been assigned a value, right?  And it's
>> default value is -1, so, shouldn't the check really be '>= 0' or '!= -1' maybe?
>>
>> The same pattern is repeated below in ::store_registers.
>> 
>> Ahh, reading further on, I see that (not just you), but all the *_regnum fields
>> in i386_gdbarch_tdep start set to 0 (which is a valid register number), but are
>> set to -1 if the feature is not found.  Maybe I'm missing something incredibly
>> clever about this ... but I suspect this code is just showing its age a little.  I
>> still think the validity check should be against -1.
>
> Yes I agree, it seems to me that there is something inconsistent in GDB.
>
> The current default value is 0, we set all regnums to 0 initially in gdb/i386-tdep.h
> And we set it to -1 to indicate the absence of that specific register.
>
> But on the other hand the enums (enum amd64_regnum/enum i386_regnum) defining
> the register numbers start at 0.
>
> So that seems to be a separate issue one could consider to fix.
> Maybe the default should be simply -1, instead of 0 ?
>
> But for my specific patch here the best would be to compare against against -1, I agree and will fix.
>
>> > +	    {
>> > +	      x86_linux_fetch_ssp (regcache, tid);
>> > +	      if (regnum != -1)
>> > +		return;
>> > +	    }
>> > +
>> >  	  /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
>> >  	     "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure on
>> >  	     Intel Skylake CPUs") that sometimes causes the mxcsr location
>> > in @@ -302,6 +311,14 @@ amd64_linux_nat_target::store_registers (struct
>> regcache *regcache, int regnum)
>> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
>> >  	{
>> >  	  gdb::byte_vector xstateregs (tdep->xsave_layout.sizeof_xsave);
>> > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
>> > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
>> > +	    {
>> > +	      x86_linux_store_ssp (regcache, tid);
>> > +	      if (regnum != -1)
>> > +		return;
>> > +	    }
>> > +
>> >  	  struct iovec iov;
>> >
>> >  	  iov.iov_base = xstateregs.data ();
>> 
>> 
>> > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index
>> > 4ef640698bd..0881ac4aee5 100644
>> > --- a/gdb/doc/gdb.texinfo
>> > +++ b/gdb/doc/gdb.texinfo
>> > @@ -49959,6 +49959,10 @@ The @samp{org.gnu.gdb.i386.pkeys} feature
>> is
>> > optional.  It should  describe a single register, @samp{pkru}.  It is
>> > a 32-bit register  valid for i386 and amd64.
>> >
>> > +The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
>> > +describe the user mode register @samp{pl3_ssp} which has 64 bits on
>> > +amd64.  Following the restriction of the Linux kernel, only amd64 is
>> supported for now.
>> 
>> You mention a couple of times in this patch that the shadow stack is only
>> supported (for now) on amd64 (& x32), but you have added an i386 version
>> of the org.gnu.gdb.i386.pl3_ssp feature with a 32-bit pl3_ssp register.
>
> We need the 32-bit pl3_ssp register for x32, since for x32 the shadow stack pointer has 32 bits only.
>
>> This feature is (as far as I can tell) instantiated for 32-bit targets, which means
>> gdbserver will send out target descriptions containing this feature.
>> 
>> I think either (a) you should drop the i386 xml file and feature, or (b) my
>> preferred choice, document the i386 version here.  It's still fine to say that
>> the 32-bit (i386) register is ignored by GDB for now though.  I had a go at
>> rewording the paragraph, but I'm not 100% sure its OK yet:
>> 
>>   The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
>>   describe the user mode register @samp{pl3_ssp} which has 64 bits on
>>   amd64 and 32 bits on i386.  Following the restriction of the Linux
>>   kernel, only GDB for amd64 targets makes use of this feature for now.
>
> I missed to describe x32 here, actually. So I'd write it as follows:
>
> The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
> describe the user mode register @samp{pl3_ssp} which has 64 bits on
> amd64, 32 bits on amd64 with  32-bit pointer size (X32) and 32 bits on i386. 
> Following the restriction of the Linux kernel, only GDB for amd64 targets makes
> use of this feature for now.
>
> What do you think?

Sounds great.

>
>> 
>> > +
>> >  @node LoongArch Features
>> >  @subsection LoongArch Features
>> >  @cindex target descriptions, LoongArch Features diff --git
>> > a/gdb/features/Makefile b/gdb/features/Makefile index
>> > 7a8c7999733..2afda1ccd00 100644
>> > --- a/gdb/features/Makefile
>> > +++ b/gdb/features/Makefile
>> > @@ -225,6 +225,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
>> >  	i386/32bit-avx.xml \
>> >  	i386/32bit-avx512.xml \
>> >  	i386/32bit-segments.xml \
>> > +	i386/32bit-ssp.xml \
>> >  	i386/64bit-avx512.xml \
>> >  	i386/64bit-core.xml \
>> >  	i386/64bit-segments.xml \
>> > @@ -232,6 +233,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
>> >  	i386/64bit-linux.xml \
>> >  	i386/64bit-sse.xml \
>> >  	i386/pkeys.xml \
>> > +	i386/64bit-ssp.xml \
>> >  	i386/x32-core.xml \
>> >  	loongarch/base32.xml \
>> >  	loongarch/base64.xml \
>> > diff --git a/gdb/features/i386/32bit-ssp.c
>> > b/gdb/features/i386/32bit-ssp.c new file mode 100644 index
>> > 00000000000..991bae3c1e6
>> > --- /dev/null
>> > +++ b/gdb/features/i386/32bit-ssp.c
>> > @@ -0,0 +1,14 @@
>> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
>> > +  Original: 32bit-ssp.xml */
>> > +
>> > +#include "gdbsupport/tdesc.h"
>> > +
>> > +static int
>> > +create_feature_i386_32bit_ssp (struct target_desc *result, long
>> > +regnum) {
>> > +  struct tdesc_feature *feature;
>> > +
>> > +  feature = tdesc_create_feature (result,
>> > +"org.gnu.gdb.i386.pl3_ssp");
>> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 32,
>> > +"data_ptr");
>> > +  return regnum;
>> > +}
>> > diff --git a/gdb/features/i386/32bit-ssp.xml
>> > b/gdb/features/i386/32bit-ssp.xml new file mode 100644 index
>> > 00000000000..d17e7004eec
>> > --- /dev/null
>> > +++ b/gdb/features/i386/32bit-ssp.xml
>> > @@ -0,0 +1,11 @@
>> > +<?xml version="1.0"?>
>> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
>> > +
>> > +     Copying and distribution of this file, with or without modification,
>> > +     are permitted in any medium without royalty provided the copyright
>> > +     notice and this notice are preserved.  -->
>> > +
>> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
>> > +name="org.gnu.gdb.i386.pl3_ssp">
>> > +  <reg name="pl3_ssp" bitsize="32" type="data_ptr"/> </feature>
>> > diff --git a/gdb/features/i386/64bit-ssp.c
>> > b/gdb/features/i386/64bit-ssp.c new file mode 100644 index
>> > 00000000000..5468099ddf6
>> > --- /dev/null
>> > +++ b/gdb/features/i386/64bit-ssp.c
>> > @@ -0,0 +1,14 @@
>> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
>> > +  Original: 64bit-ssp.xml */
>> > +
>> > +#include "gdbsupport/tdesc.h"
>> > +
>> > +static int
>> > +create_feature_i386_64bit_ssp (struct target_desc *result, long
>> > +regnum) {
>> > +  struct tdesc_feature *feature;
>> > +
>> > +  feature = tdesc_create_feature (result,
>> > +"org.gnu.gdb.i386.pl3_ssp");
>> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 64,
>> > +"data_ptr");
>> > +  return regnum;
>> > +}
>> > diff --git a/gdb/features/i386/64bit-ssp.xml
>> > b/gdb/features/i386/64bit-ssp.xml new file mode 100644 index
>> > 00000000000..a0688d018a5
>> > --- /dev/null
>> > +++ b/gdb/features/i386/64bit-ssp.xml
>> > @@ -0,0 +1,11 @@
>> > +<?xml version="1.0"?>
>> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
>> > +
>> > +     Copying and distribution of this file, with or without modification,
>> > +     are permitted in any medium without royalty provided the copyright
>> > +     notice and this notice are preserved.  -->
>> > +
>> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
>> > +name="org.gnu.gdb.i386.pl3_ssp">
>> > +  <reg name="pl3_ssp" bitsize="64" type="data_ptr"/> </feature>
>> > diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index
>> > 90ff0c5c706..8eb5b4fac86 100644
>> > --- a/gdb/i386-tdep.c
>> > +++ b/gdb/i386-tdep.c
>> > @@ -8403,7 +8403,8 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>> >    const struct tdesc_feature *feature_core;
>> >
>> >    const struct tdesc_feature *feature_sse, *feature_avx, *feature_avx512,
>> > -			     *feature_pkeys, *feature_segments;
>> > +			     *feature_pkeys, *feature_segments,
>> > +			     *feature_pl3_ssp;
>> >    int i, num_regs, valid_p;
>> >
>> >    if (! tdesc_has_registers (tdesc))
>> > @@ -8429,6 +8430,9 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>> >    /* Try PKEYS  */
>> >    feature_pkeys = tdesc_find_feature (tdesc,
>> > "org.gnu.gdb.i386.pkeys");
>> >
>> > +  /* Try Shadow Stack.  */
>> > +  feature_pl3_ssp = tdesc_find_feature (tdesc,
>> > + "org.gnu.gdb.i386.pl3_ssp");
>> > +
>> >    valid_p = 1;
>> >
>> >    /* The XCR0 bits.  */
>> > @@ -8544,6 +8548,15 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
>> *tdep,
>> >  					    tdep->pkeys_register_names[i]);
>> >      }
>> >
>> > +  if (feature_pl3_ssp != nullptr)
>> > +    {
>> > +      if (tdep->ssp_regnum < 0)
>> > +	tdep->ssp_regnum = I386_PL3_SSP_REGNUM;
>> > +
>> > +      valid_p &= tdesc_numbered_register (feature_pl3_ssp, tdesc_data,
>> > +					  tdep->ssp_regnum, "pl3_ssp");
>> > +    }
>> > +
>> >    return valid_p;
>> >  }
>> >
>> > @@ -8835,6 +8848,9 @@ i386_gdbarch_init (struct gdbarch_info info,
>> struct gdbarch_list *arches)
>> >    /* No segment base registers.  */
>> >    tdep->fsbase_regnum = -1;
>> >
>> > +  /* No shadow stack pointer register.  */  tdep->ssp_regnum = -1;
>> > +
>> >    tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
>> >
>> >    set_gdbarch_relocate_instruction (gdbarch,
>> > i386_relocate_instruction); @@ -8955,13 +8971,15 @@ const struct
>> > target_desc *  i386_target_description (uint64_t xstate_bv_mask, bool
>> > segments)  {
>> >    static target_desc *i386_tdescs \
>> > -    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
>> > +    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/] \
>> > +    [2/*segments*/] = {};
>> >    target_desc **tdesc;
>> >
>> >    tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
>> >      [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
>> >      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
>> >      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
>> > +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
>> >      [segments ? 1 : 0];
>> >
>> >    if (*tdesc == NULL)
>> > diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index
>> > 239bc8674e8..60d6f3eb732 100644
>> > --- a/gdb/i386-tdep.h
>> > +++ b/gdb/i386-tdep.h
>> > @@ -191,6 +191,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
>> >    /* PKEYS register names.  */
>> >    const char * const *pkeys_register_names = nullptr;
>> >
>> > +  /* Shadow stack pointer register.  */  int ssp_regnum = 0;
>> 
>> I know all the other *_regnum fields start as 0, but I really don't understand
>> why.  Surely starting as -1 would make more sense?  This isn't a hard
>> requirement, but maybe you see some reason why these fields should start
>> at 0 that I'm missing?
>
> I agree. As stated before setting the regnums to -1 here by default would make
> more sense to me. So I think I should set ssp_regnum here to -1.
> I'd do that, unless I see some issues with that when testing of course.
>
> The other registers set to 0 here could be fixed separately.
>
> Does that sound reasonable?

Yes absolutely, I certainly don't want you to change unrelated code as
part of this work, but we might as well improve new code as its added.
Your proposal sounds perfect to me.

>
>> > +
>> >    /* Register number for %fsbase.  Set this to -1 to indicate the
>> >       absence of segment base registers.  */
>> >    int fsbase_regnum = 0;
>> > @@ -293,6 +296,7 @@ enum i386_regnum
>> >    I386_ZMM0H_REGNUM,		/* %zmm0h */
>> >    I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
>> >    I386_PKRU_REGNUM,
>> > +  I386_PL3_SSP_REGNUM,
>> >    I386_FSBASE_REGNUM,
>> >    I386_GSBASE_REGNUM
>> >  };
>> 
>> 
>> > diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp
>> > b/gdb/testsuite/gdb.arch/amd64-ssp.exp
>> > new file mode 100644
>> > index 00000000000..6ddc875b9a3
>> > --- /dev/null
>> > +++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
>> > @@ -0,0 +1,50 @@
>> > +# Copyright 2018-2024 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 amd64-shadow-stack.c
>> 
>> Test source files should be named to match the .exp file.  But in this case
>> 'amd64-shadow-stack' seems better than 'amd64-ssp', so I'd renamed the
>> .exp file to amd64-shadow-stack.exp, then this line can be just:
>> 
>>   standard_testfile
>
> Yes, I agree and will fix.
>
>> > +
>> > +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 -1
>> > +    }
>> > +
>> > +    if {![runto_main]} {
>> > +	return -1
>> > +    }
>> 
>> The 'return -1' can become just 'return'.  The return value doesn't mean
>> anything.
>
> True will fix (also in following patches, that have the same for the test files).
>
>> > +
>> > +    # Read PL3_SSP register.
>> > +    set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read pl3_ssp
>> > + value"]
>> > +
>> > +    # Write PL3_SSP register.
>> > +    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"
>> > +
>> > +    # Restore original value.
>> > +    gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main" "restore
>> original pl3_ssp"
>> > +
>> > +    # Potential CET violations often only occur after resuming normal
>> execution.
>> > +    # Therefore, it is important to test normal program continuation after
>> > +    # configuring the shadow stack pointer.
>> > +    gdb_continue_to_end
>> 
>> I assume that if we continue with the bogus value in place the inferior would
>> either give an error or terminate.  Is it worth trying this and checking that the
>> inferior behaves as expected?
>
> If we don't reset the shadow stack pointer to it's original value we will see a SEGV. 
> Dependent on the address of the wrong shadow stack pointer it's either a SEGV
> with si code that points to a control flow protection fault or a different si code.
>
> So if I stay in a valid address range for configuring pl3_ssp but don't restore the original value
> I'll see a control flow protection exception:
>
> [...]
> breakpoint 1, 0x0000555555555148 in main ()^M
> (gdb) print /x $pl3_ssp^M
> $1 = 0x7ffff7bfffe8^M
> (gdb) PASS: gdb.arch/amd64-ssp.exp: get hexadecimal valueof "$pl3_ssp"
> print /x $pl3_ssp = 0x7ffff7bfffe0^M
> $2 = 0x7ffff7bfffe0^M
> (gdb) PASS: gdb.arch/amd64-ssp.exp: set pl3_ssp value
> print /x $pl3_ssp^M
> $3 = 0x7ffff7bfffe0^M
> (gdb) PASS: gdb.arch/amd64-ssp.exp: read pl3_ssp value after setting
> continue^M
> Continuing.^M
> ^M
> Program received signal SIGSEGV, Segmentation fault.^M
> 0x0000555555555158 in main ()^M
> (gdb) FAIL: gdb.arch/amd64-ssp.exp: continue until exit
>
> Siginfo shows si_code = 10, which indicates a control protection fault.
>
> p $_siginfo^M
> $4 = {si_signo = 11, si_errno = 0, si_code = 10, [...]
>
> If I set the value of pl3_ssp as in the current test (0x12345678) I'll see a different SEGV actually
>
> p $_siginfo
> $4 = {si_signo = 11, si_errno = 0, si_code = 1, [...]
>
>> 
>> What if, say, the $pl3_ssp value only ever made it as far as the register cache,
>> and was never actually written back to the inferior?  I don't think the above
>> test would actually spot this bug, right?
>
> Hm, if I understand you correctly here and you mean the scenario as shown
> above the above test would spot this bug I think (as we saw a fail).
>
> Does my example above show what you described or do you mean a
> different scenario?

Yes, something like the above would check that the register is actually
being written back to the hardware, and is written to the expected
location.

The current test, as written in the patch, writes a bad value to the
shadow stack, then restores the correct value.  What if the bad value
never actually got written back to the hardware at all, and was just
being held in the register cache?

Having a test that writes a bad value, then does 'continue', and expects
to see something like 'Program received signal ...' would be a
reasonable indication that the write to the shadow stack actually made
it to the h/w.

Thanks,
Andrew




>
> Regards
> Christina
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de
> Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-08-05  4:29           ` Thiago Jung Bauermann
@ 2025-08-05 15:29             ` Schimpe, Christina
  2025-08-06 20:52             ` Luis
  1 sibling, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-05 15:29 UTC (permalink / raw)
  To: Thiago Jung Bauermann; +Cc: Andrew Burgess, gdb-patches, luis.machado

Hi Thiago,

> -----Original Message-----
> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Sent: Tuesday, August 5, 2025 6:30 AM
> To: Schimpe, Christina <christina.schimpe@intel.com>
> Cc: Andrew Burgess <aburgess@redhat.com>; gdb-patches@sourceware.org;
> luis.machado@arm.com
> Subject: Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with
> shadow stack.
> 
> Hello,
> 
> "Schimpe, Christina" <christina.schimpe@intel.com> writes:
> 
> >> > >> +
> >> > >> +    # 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.
> >> > >> +
> >> > >> +    if {$gcore_generated} {
> >> > >> +	clean_restart $binfile
> >> > >> +
> >> > >> +	with_test_prefix "gcore corefile" {
> >> > >> +	    check_core_file $gcore_filename $ssp_in_gcore
> >> > >> +	}
> >> > >> +    } else {
> >> > >> +	fail "gcore corefile not generated"
> >> > >
> >> > > It's better, where possible, to avoid having pass/fail results
> >> > > that only show up down some code paths.
> >> > >
> >> > > In this case it's easy to avoid having a stray 'fail' by
> >> > > restructuring the code too:
> >> > >
> >> > >   gdb_assert { $gcore_generated } "gcore corefile created"
> >> > >   if { $gcore_generated } {
> >> > >     ... etc ...
> >> > >   }
> >> > >
> >> > > Now you'll always have either a pass or fail based on the gcore
> >> > > being generated.
> >> >
> >> > Good idea. I did that for aarch64-gcs-core.exp.
> >
> > If no OS corefile is found we will see a FAIL here.
> > The usual coredump testing doesn't fail in case the coredump file is not found.
> > So all gdb.base/corefile*.exp tests have sth. like:
> >
> > set corefile [core_find $binfile {}]
> > if {$corefile == ""} {
> >     return
> > }
> >
> > This can happen in case corefiles are managed, for instance, by apport on
> ubuntu.
> > Do we want a different behaviour ?
> 
> Interesting point. Perhaps a FAIL Isn't the best result to report in this case, but
> IMHO it would be worth reporting an UNTESTED or perhaps UNSUPPORTED
> result rather than silently returning.
> 
> I don't have a strong opinion on this matter though.

I think UNTESTED should be good.
But in GDB we're are actually not consistent with UNTESTED or UNSUPPORTED I think.
In my opinion UNTESTED means that there is something not fine with the setup and one
should investigate, but UNSUPPORTED means it's fine that the test is skipped.

In any case, once we decided for one option it would be good if we'd update the other
corefile tests, too.

> 
> >> > > There is also the helper proc `gcore_cmd_available`.  I'd guess
> >> > > for any
> >> > > x86 target that supports SSP, gcore will be available, but in
> >> > > theory you could consider using this to avoid a fail when gcore
> >> > > is not available maybe?
> >> >
> >> > In the GCS core testcase, I moved the gcore tests after the OS
> >> > corefile ones, with an
> >> >
> >> >   if ![gcore_cmd_available] {
> >> >       return
> >> >   }
> >> >
> >> > in the middle, and after that I had to redo some GDB setup:
> >> >
> >> >   clean_restart $binfile
> >> >
> >> >   if ![runto $linespec] {
> >> >       return
> >> >   }
> >
> > Do you also do the "continue until crash part" again?
> 
> No, at least in my version of the test the "continue until crash" part is only
> needed once, for the gcore test. The OS corefile test doesn't need it.
> 
> > It would make sense to move it into a proc then:
> > ~~~
> > # Continue until the expected crash at the return instruction.
> >
> > proc continue_until_crash {} {
> >     global hex decimal
> >
> >     # 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" \
> > [...]
> > ~~~
> >
> > And my gcore testing now looks as follows:
> >
> > ~~~
> >     if ![gcore_cmd_available] {
> > 	return
> 
> Come to think of it, I think an untested/unsupported would be good here as
> well, if I'm going to be consistent in my opinion.

UNSUPPORTED here then I think, if my above understanding of UNTESTED/UNSUPPORTED is correct.

> >     }
> >
> >     clean_restart $binfile
> >
> >     if ![runto $linespec] {
> > 	return
> >     }
> >
> >     with_test_prefix "gcore corefile" {
> > 	continue_until_crash
> >
> > 	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
> > 	    check_core_file $gcore_filename $ssp_in_gcore
> > 	}
> >     }
> > ~~~
> 
> For reference, my test is here:
> 
> https://gitlab.com/bauermann/binutils-gdb/-/blob/guarded-control-stack-
> patches/gdb/testsuite/gdb.arch/aarch64-gcs-core.exp
> 
> --
> Thiago

Will check thanks!

Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-08-05 13:57       ` Andrew Burgess
@ 2025-08-06 19:53         ` Schimpe, Christina
  2025-08-06 19:54           ` Schimpe, Christina
  2025-08-14 11:39           ` Andrew Burgess
  0 siblings, 2 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-06 19:53 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches, thiago.bauermann; +Cc: luis.machado

Hi Andrew,

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Tuesday, August 5, 2025 3:57 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow
> stack pointer register.
> 
> "Schimpe, Christina" <christina.schimpe@intel.com> writes:
> 
> > Hi Andrew,
> >
> > Thanks a lot for the review! I have some questions for your feedback,
> > please find my comments below.
> >
> >> -----Original Message-----
> >> From: Andrew Burgess <aburgess@redhat.com>
> >> Sent: Friday, July 25, 2025 2:50 PM
> >> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> >> patches@sourceware.org
> >> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> >> Subject: Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel
> >> shadow stack pointer register.
> >>
> >> Christina Schimpe <christina.schimpe@intel.com> writes:
> >>
> >> > This patch adds the user mode register PL3_SSP which is part of the
> >> > Intel(R) Control-Flow Enforcement Technology (CET) feature for
> >> > support of shadow stack.
> >> > For now, only native and remote debugging support for shadow stack
> >> > userspace on amd64 linux are covered by this patch including 64 bit
> >> > and
> >> > x32 support.  32 bit support is not covered due to missing Linux
> >> > kernel support.
> >> >
> >> > This patch requires fixing the test
> >> > gdb.base/inline-frame-cycle-unwind
> >> > which is failing in case the shadow stack pointer is unavailable.
> >> > Such a state is possible if shadow stack is disabled for the
> >> > current thread but supported by HW.
> >> >
> >> > This test uses the Python unwinder inline-frame-cycle-unwind.py
> >> > which fakes the cyclic stack cycle by reading the pending frame's
> >> > registers and adding them to the unwinder:
> >> >
> >> > ~~~
> >> > for reg in pending_frame.architecture().registers("general"):
> >> >      val = pending_frame.read_register(reg)
> >> >      unwinder.add_saved_register(reg, val)
> >> >      return unwinder
> >> > ~~~
> >> >
> >> > However, in case the python unwinder is used we add a register
> >> > (pl3_ssp) that is unavailable.  This leads to a NOT_AVAILABLE_ERROR
> >> > caught in gdb/frame-unwind.c:frame_unwind_try_unwinder and it is
> >> > continued with standard unwinders.  This destroys the faked cyclic
> >> > behavior and the stack is further unwinded after frame 5.
> >> >
> >> > In the working scenario an error should be triggered:
> >> > ~~~
> >> > bt
> >> > 0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
> >> > 1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> >> > 2  0x000055555555516e in inline_func () at
> >> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> >> > 3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> >> > 4  0x000055555555516e in inline_func () at
> >> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> >> > 5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> >> > Backtrace stopped: previous frame identical to this frame (corrupt
> >> > stack?)
> >> > (gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5:
> >> > backtrace when the unwind is broken at frame 5 ~~~
> >> >
> >> > To fix the Python unwinder, we simply skip the unavailable registers.
> >> >
> >> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> >> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> >> > Reviewed-By: Luis Machado <luis.machado@arm.com>
> >> > ---
> >> >  gdb/NEWS                                      |  3 +
> >> >  gdb/amd64-linux-nat.c                         | 17 +++++
> >> >  gdb/amd64-linux-tdep.c                        |  1 +
> >> >  gdb/amd64-tdep.c                              |  6 +-
> >> >  gdb/amd64-tdep.h                              |  1 +
> >> >  gdb/arch/amd64.c                              | 10 +++
> >> >  gdb/arch/i386.c                               |  4 ++
> >> >  gdb/arch/x86-linux-tdesc-features.c           |  1 +
> >> >  gdb/doc/gdb.texinfo                           |  4 ++
> >> >  gdb/features/Makefile                         |  2 +
> >> >  gdb/features/i386/32bit-ssp.c                 | 14 ++++
> >> >  gdb/features/i386/32bit-ssp.xml               | 11 +++
> >> >  gdb/features/i386/64bit-ssp.c                 | 14 ++++
> >> >  gdb/features/i386/64bit-ssp.xml               | 11 +++
> >> >  gdb/i386-tdep.c                               | 22 +++++-
> >> >  gdb/i386-tdep.h                               |  4 ++
> >> >  gdb/nat/x86-linux-tdesc.c                     |  2 +
> >> >  gdb/nat/x86-linux.c                           | 57 +++++++++++++++
> >> >  gdb/nat/x86-linux.h                           |  4 ++
> >> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
> >> >  gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
> >> >  .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
> >> >  gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
> >> >  gdb/x86-linux-nat.c                           | 49 +++++++++++--
> >> >  gdb/x86-linux-nat.h                           | 11 +++
> >> >  gdb/x86-tdep.c                                | 21 ++++++
> >> >  gdb/x86-tdep.h                                |  9 +++
> >> >  gdbserver/linux-x86-low.cc                    | 28 +++++++-
> >> >  gdbsupport/x86-xstate.h                       |  5 +-
> >> >  29 files changed, 447 insertions(+), 10 deletions(-)  create mode
> >> > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
> >> > gdb/features/i386/32bit-ssp.xml  create mode 100644
> >> > gdb/features/i386/64bit-ssp.c  create mode 100644
> >> > gdb/features/i386/64bit-ssp.xml  create mode 100644
> >> > gdb/testsuite/gdb.arch/amd64-shadow-stack.c
> >> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> >> >
> >>
> >>
> >> > diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index
> >> > dbb9b3223cb..4df99ccca54 100644
> >> > --- a/gdb/amd64-linux-nat.c
> >> > +++ b/gdb/amd64-linux-nat.c
> >> > @@ -32,6 +32,7 @@
> >> >  #include "amd64-tdep.h"
> >> >  #include "amd64-linux-tdep.h"
> >> >  #include "i386-linux-tdep.h"
> >> > +#include "x86-tdep.h"
> >> >  #include "gdbsupport/x86-xstate.h"
> >> >
> >> >  #include "x86-linux-nat.h"
> >> > @@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers
> >> > (struct regcache *regcache, int regnum)
> >> >
> >> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
> >> >  	{
> >> > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
> >> > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
> >>
> >> It's really nit-picking, but I don't think the '>' here check is
> >> correct.  You're checking that tdep->ssp_regnum has been assigned a
> >> value, right?  And it's default value is -1, so, shouldn't the check really be
> '>= 0' or '!= -1' maybe?
> >>
> >> The same pattern is repeated below in ::store_registers.
> >>
> >> Ahh, reading further on, I see that (not just you), but all the
> >> *_regnum fields in i386_gdbarch_tdep start set to 0 (which is a valid
> >> register number), but are set to -1 if the feature is not found.
> >> Maybe I'm missing something incredibly clever about this ... but I
> >> suspect this code is just showing its age a little.  I still think the validity
> check should be against -1.
> >
> > Yes I agree, it seems to me that there is something inconsistent in GDB.
> >
> > The current default value is 0, we set all regnums to 0 initially in
> > gdb/i386-tdep.h And we set it to -1 to indicate the absence of that specific
> register.
> >
> > But on the other hand the enums (enum amd64_regnum/enum
> i386_regnum)
> > defining the register numbers start at 0.
> >
> > So that seems to be a separate issue one could consider to fix.
> > Maybe the default should be simply -1, instead of 0 ?
> >
> > But for my specific patch here the best would be to compare against
> against -1, I agree and will fix.
> >
> >> > +	    {
> >> > +	      x86_linux_fetch_ssp (regcache, tid);
> >> > +	      if (regnum != -1)
> >> > +		return;
> >> > +	    }
> >> > +
> >> >  	  /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
> >> >  	     "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure
> on
> >> >  	     Intel Skylake CPUs") that sometimes causes the mxcsr
> >> > location in @@ -302,6 +311,14 @@
> >> > amd64_linux_nat_target::store_registers (struct
> >> regcache *regcache, int regnum)
> >> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
> >> >  	{
> >> >  	  gdb::byte_vector xstateregs (tdep->xsave_layout.sizeof_xsave);
> >> > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
> >> > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
> >> > +	    {
> >> > +	      x86_linux_store_ssp (regcache, tid);
> >> > +	      if (regnum != -1)
> >> > +		return;
> >> > +	    }
> >> > +
> >> >  	  struct iovec iov;
> >> >
> >> >  	  iov.iov_base = xstateregs.data ();
> >>
> >>
> >> > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index
> >> > 4ef640698bd..0881ac4aee5 100644
> >> > --- a/gdb/doc/gdb.texinfo
> >> > +++ b/gdb/doc/gdb.texinfo
> >> > @@ -49959,6 +49959,10 @@ The @samp{org.gnu.gdb.i386.pkeys}
> feature
> >> is
> >> > optional.  It should  describe a single register, @samp{pkru}.  It
> >> > is a 32-bit register  valid for i386 and amd64.
> >> >
> >> > +The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It
> >> > +should describe the user mode register @samp{pl3_ssp} which has 64
> >> > +bits on amd64.  Following the restriction of the Linux kernel,
> >> > +only amd64 is
> >> supported for now.
> >>
> >> You mention a couple of times in this patch that the shadow stack is
> >> only supported (for now) on amd64 (& x32), but you have added an i386
> >> version of the org.gnu.gdb.i386.pl3_ssp feature with a 32-bit pl3_ssp
> register.
> >
> > We need the 32-bit pl3_ssp register for x32, since for x32 the shadow
> stack pointer has 32 bits only.
> >
> >> This feature is (as far as I can tell) instantiated for 32-bit
> >> targets, which means gdbserver will send out target descriptions
> containing this feature.
> >>
> >> I think either (a) you should drop the i386 xml file and feature, or
> >> (b) my preferred choice, document the i386 version here.  It's still
> >> fine to say that the 32-bit (i386) register is ignored by GDB for now
> >> though.  I had a go at rewording the paragraph, but I'm not 100% sure its
> OK yet:
> >>
> >>   The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
> >>   describe the user mode register @samp{pl3_ssp} which has 64 bits on
> >>   amd64 and 32 bits on i386.  Following the restriction of the Linux
> >>   kernel, only GDB for amd64 targets makes use of this feature for now.
> >
> > I missed to describe x32 here, actually. So I'd write it as follows:
> >
> > The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
> > describe the user mode register @samp{pl3_ssp} which has 64 bits on
> > amd64, 32 bits on amd64 with  32-bit pointer size (X32) and 32 bits on
> i386.
> > Following the restriction of the Linux kernel, only GDB for amd64
> > targets makes use of this feature for now.
> >
> > What do you think?
> 
> Sounds great.
> 
> >
> >>
> >> > +
> >> >  @node LoongArch Features
> >> >  @subsection LoongArch Features
> >> >  @cindex target descriptions, LoongArch Features diff --git
> >> > a/gdb/features/Makefile b/gdb/features/Makefile index
> >> > 7a8c7999733..2afda1ccd00 100644
> >> > --- a/gdb/features/Makefile
> >> > +++ b/gdb/features/Makefile
> >> > @@ -225,6 +225,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
> >> >  	i386/32bit-avx.xml \
> >> >  	i386/32bit-avx512.xml \
> >> >  	i386/32bit-segments.xml \
> >> > +	i386/32bit-ssp.xml \
> >> >  	i386/64bit-avx512.xml \
> >> >  	i386/64bit-core.xml \
> >> >  	i386/64bit-segments.xml \
> >> > @@ -232,6 +233,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
> >> >  	i386/64bit-linux.xml \
> >> >  	i386/64bit-sse.xml \
> >> >  	i386/pkeys.xml \
> >> > +	i386/64bit-ssp.xml \
> >> >  	i386/x32-core.xml \
> >> >  	loongarch/base32.xml \
> >> >  	loongarch/base64.xml \
> >> > diff --git a/gdb/features/i386/32bit-ssp.c
> >> > b/gdb/features/i386/32bit-ssp.c new file mode 100644 index
> >> > 00000000000..991bae3c1e6
> >> > --- /dev/null
> >> > +++ b/gdb/features/i386/32bit-ssp.c
> >> > @@ -0,0 +1,14 @@
> >> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> >> > +  Original: 32bit-ssp.xml */
> >> > +
> >> > +#include "gdbsupport/tdesc.h"
> >> > +
> >> > +static int
> >> > +create_feature_i386_32bit_ssp (struct target_desc *result, long
> >> > +regnum) {
> >> > +  struct tdesc_feature *feature;
> >> > +
> >> > +  feature = tdesc_create_feature (result,
> >> > +"org.gnu.gdb.i386.pl3_ssp");
> >> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 32,
> >> > +"data_ptr");
> >> > +  return regnum;
> >> > +}
> >> > diff --git a/gdb/features/i386/32bit-ssp.xml
> >> > b/gdb/features/i386/32bit-ssp.xml new file mode 100644 index
> >> > 00000000000..d17e7004eec
> >> > --- /dev/null
> >> > +++ b/gdb/features/i386/32bit-ssp.xml
> >> > @@ -0,0 +1,11 @@
> >> > +<?xml version="1.0"?>
> >> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> >> > +
> >> > +     Copying and distribution of this file, with or without modification,
> >> > +     are permitted in any medium without royalty provided the
> copyright
> >> > +     notice and this notice are preserved.  -->
> >> > +
> >> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
> >> > +name="org.gnu.gdb.i386.pl3_ssp">
> >> > +  <reg name="pl3_ssp" bitsize="32" type="data_ptr"/> </feature>
> >> > diff --git a/gdb/features/i386/64bit-ssp.c
> >> > b/gdb/features/i386/64bit-ssp.c new file mode 100644 index
> >> > 00000000000..5468099ddf6
> >> > --- /dev/null
> >> > +++ b/gdb/features/i386/64bit-ssp.c
> >> > @@ -0,0 +1,14 @@
> >> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> >> > +  Original: 64bit-ssp.xml */
> >> > +
> >> > +#include "gdbsupport/tdesc.h"
> >> > +
> >> > +static int
> >> > +create_feature_i386_64bit_ssp (struct target_desc *result, long
> >> > +regnum) {
> >> > +  struct tdesc_feature *feature;
> >> > +
> >> > +  feature = tdesc_create_feature (result,
> >> > +"org.gnu.gdb.i386.pl3_ssp");
> >> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 64,
> >> > +"data_ptr");
> >> > +  return regnum;
> >> > +}
> >> > diff --git a/gdb/features/i386/64bit-ssp.xml
> >> > b/gdb/features/i386/64bit-ssp.xml new file mode 100644 index
> >> > 00000000000..a0688d018a5
> >> > --- /dev/null
> >> > +++ b/gdb/features/i386/64bit-ssp.xml
> >> > @@ -0,0 +1,11 @@
> >> > +<?xml version="1.0"?>
> >> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> >> > +
> >> > +     Copying and distribution of this file, with or without modification,
> >> > +     are permitted in any medium without royalty provided the
> copyright
> >> > +     notice and this notice are preserved.  -->
> >> > +
> >> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
> >> > +name="org.gnu.gdb.i386.pl3_ssp">
> >> > +  <reg name="pl3_ssp" bitsize="64" type="data_ptr"/> </feature>
> >> > diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index
> >> > 90ff0c5c706..8eb5b4fac86 100644
> >> > --- a/gdb/i386-tdep.c
> >> > +++ b/gdb/i386-tdep.c
> >> > @@ -8403,7 +8403,8 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
> *tdep,
> >> >    const struct tdesc_feature *feature_core;
> >> >
> >> >    const struct tdesc_feature *feature_sse, *feature_avx,
> *feature_avx512,
> >> > -			     *feature_pkeys, *feature_segments;
> >> > +			     *feature_pkeys, *feature_segments,
> >> > +			     *feature_pl3_ssp;
> >> >    int i, num_regs, valid_p;
> >> >
> >> >    if (! tdesc_has_registers (tdesc)) @@ -8429,6 +8430,9 @@
> >> > i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
> >> >    /* Try PKEYS  */
> >> >    feature_pkeys = tdesc_find_feature (tdesc,
> >> > "org.gnu.gdb.i386.pkeys");
> >> >
> >> > +  /* Try Shadow Stack.  */
> >> > +  feature_pl3_ssp = tdesc_find_feature (tdesc,
> >> > + "org.gnu.gdb.i386.pl3_ssp");
> >> > +
> >> >    valid_p = 1;
> >> >
> >> >    /* The XCR0 bits.  */
> >> > @@ -8544,6 +8548,15 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
> >> *tdep,
> >> >  					    tdep->pkeys_register_names[i]);
> >> >      }
> >> >
> >> > +  if (feature_pl3_ssp != nullptr)
> >> > +    {
> >> > +      if (tdep->ssp_regnum < 0)
> >> > +	tdep->ssp_regnum = I386_PL3_SSP_REGNUM;
> >> > +
> >> > +      valid_p &= tdesc_numbered_register (feature_pl3_ssp, tdesc_data,
> >> > +					  tdep->ssp_regnum, "pl3_ssp");
> >> > +    }
> >> > +
> >> >    return valid_p;
> >> >  }
> >> >
> >> > @@ -8835,6 +8848,9 @@ i386_gdbarch_init (struct gdbarch_info info,
> >> struct gdbarch_list *arches)
> >> >    /* No segment base registers.  */
> >> >    tdep->fsbase_regnum = -1;
> >> >
> >> > +  /* No shadow stack pointer register.  */  tdep->ssp_regnum = -1;
> >> > +
> >> >    tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
> >> >
> >> >    set_gdbarch_relocate_instruction (gdbarch,
> >> > i386_relocate_instruction); @@ -8955,13 +8971,15 @@ const struct
> >> > target_desc *  i386_target_description (uint64_t xstate_bv_mask,
> >> > bool
> >> > segments)  {
> >> >    static target_desc *i386_tdescs \
> >> > -    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] =
> {};
> >> > +    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/] \
> >> > +    [2/*segments*/] = {};
> >> >    target_desc **tdesc;
> >> >
> >> >    tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
> >> >      [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
> >> >      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
> >> >      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
> >> > +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
> >> >      [segments ? 1 : 0];
> >> >
> >> >    if (*tdesc == NULL)
> >> > diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index
> >> > 239bc8674e8..60d6f3eb732 100644
> >> > --- a/gdb/i386-tdep.h
> >> > +++ b/gdb/i386-tdep.h
> >> > @@ -191,6 +191,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
> >> >    /* PKEYS register names.  */
> >> >    const char * const *pkeys_register_names = nullptr;
> >> >
> >> > +  /* Shadow stack pointer register.  */  int ssp_regnum = 0;
> >>
> >> I know all the other *_regnum fields start as 0, but I really don't
> >> understand why.  Surely starting as -1 would make more sense?  This
> >> isn't a hard requirement, but maybe you see some reason why these
> >> fields should start at 0 that I'm missing?
> >
> > I agree. As stated before setting the regnums to -1 here by default
> > would make more sense to me. So I think I should set ssp_regnum here to
> -1.
> > I'd do that, unless I see some issues with that when testing of course.
> >
> > The other registers set to 0 here could be fixed separately.
> >
> > Does that sound reasonable?
> 
> Yes absolutely, I certainly don't want you to change unrelated code as part
> of this work, but we might as well improve new code as its added.
> Your proposal sounds perfect to me.

The patch was pretty straight-forward. 😊
Thanks for providing the feedback here, I think this makes the code much better.

> >
> >> > +
> >> >    /* Register number for %fsbase.  Set this to -1 to indicate the
> >> >       absence of segment base registers.  */
> >> >    int fsbase_regnum = 0;
> >> > @@ -293,6 +296,7 @@ enum i386_regnum
> >> >    I386_ZMM0H_REGNUM,		/* %zmm0h */
> >> >    I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
> >> >    I386_PKRU_REGNUM,
> >> > +  I386_PL3_SSP_REGNUM,
> >> >    I386_FSBASE_REGNUM,
> >> >    I386_GSBASE_REGNUM
> >> >  };
> >>
> >>
> >> > diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp
> >> > b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> >> > new file mode 100644
> >> > index 00000000000..6ddc875b9a3
> >> > --- /dev/null
> >> > +++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
> >> > @@ -0,0 +1,50 @@
> >> > +# Copyright 2018-2024 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 amd64-shadow-stack.c
> >>
> >> Test source files should be named to match the .exp file.  But in
> >> this case 'amd64-shadow-stack' seems better than 'amd64-ssp', so I'd
> >> renamed the .exp file to amd64-shadow-stack.exp, then this line can be
> just:
> >>
> >>   standard_testfile
> >
> > Yes, I agree and will fix.
> >
> >> > +
> >> > +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 -1
> >> > +    }
> >> > +
> >> > +    if {![runto_main]} {
> >> > +	return -1
> >> > +    }
> >>
> >> The 'return -1' can become just 'return'.  The return value doesn't
> >> mean anything.
> >
> > True will fix (also in following patches, that have the same for the test
> files).
> >
> >> > +
> >> > +    # Read PL3_SSP register.
> >> > +    set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read
> >> > + pl3_ssp value"]
> >> > +
> >> > +    # Write PL3_SSP register.
> >> > +    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"
> >> > +
> >> > +    # Restore original value.
> >> > +    gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main"
> >> > + "restore
> >> original pl3_ssp"
> >> > +
> >> > +    # Potential CET violations often only occur after resuming
> >> > + normal
> >> execution.
> >> > +    # Therefore, it is important to test normal program continuation
> after
> >> > +    # configuring the shadow stack pointer.
> >> > +    gdb_continue_to_end
> >>
> >> I assume that if we continue with the bogus value in place the
> >> inferior would either give an error or terminate.  Is it worth trying
> >> this and checking that the inferior behaves as expected?
> >
> > If we don't reset the shadow stack pointer to it's original value we will see
> a SEGV.
> > Dependent on the address of the wrong shadow stack pointer it's either
> > a SEGV with si code that points to a control flow protection fault or a
> different si code.
> >
> > So if I stay in a valid address range for configuring pl3_ssp but
> > don't restore the original value I'll see a control flow protection exception:
> >
> > [...]
> > breakpoint 1, 0x0000555555555148 in main ()^M
> > (gdb) print /x $pl3_ssp^M
> > $1 = 0x7ffff7bfffe8^M
> > (gdb) PASS: gdb.arch/amd64-ssp.exp: get hexadecimal valueof "$pl3_ssp"
> > print /x $pl3_ssp = 0x7ffff7bfffe0^M
> > $2 = 0x7ffff7bfffe0^M
> > (gdb) PASS: gdb.arch/amd64-ssp.exp: set pl3_ssp value print /x
> > $pl3_ssp^M
> > $3 = 0x7ffff7bfffe0^M
> > (gdb) PASS: gdb.arch/amd64-ssp.exp: read pl3_ssp value after setting
> > continue^M Continuing.^M ^M Program received signal SIGSEGV,
> > Segmentation fault.^M
> > 0x0000555555555158 in main ()^M
> > (gdb) FAIL: gdb.arch/amd64-ssp.exp: continue until exit
> >
> > Siginfo shows si_code = 10, which indicates a control protection fault.
> >
> > p $_siginfo^M
> > $4 = {si_signo = 11, si_errno = 0, si_code = 10, [...]
> >
> > If I set the value of pl3_ssp as in the current test (0x12345678) I'll
> > see a different SEGV actually
> >
> > p $_siginfo
> > $4 = {si_signo = 11, si_errno = 0, si_code = 1, [...]
> >
> >>
> >> What if, say, the $pl3_ssp value only ever made it as far as the
> >> register cache, and was never actually written back to the inferior?
> >> I don't think the above test would actually spot this bug, right?
> >
> > Hm, if I understand you correctly here and you mean the scenario as
> > shown above the above test would spot this bug I think (as we saw a fail).
> >
> > Does my example above show what you described or do you mean a
> > different scenario?
> 
> Yes, something like the above would check that the register is actually being
> written back to the hardware, and is written to the expected location.
> 
> The current test, as written in the patch, writes a bad value to the shadow
> stack, then restores the correct value.  What if the bad value never actually
> got written back to the hardware at all, and was just being held in the
> register cache?
> 
> Having a test that writes a bad value, then does 'continue', and expects to
> see something like 'Program received signal ...' would be a reasonable
> indication that the write to the shadow stack actually made it to the h/w.
> 
> Thanks,
> Andrew


Yes, I agree, I'll add:

~~~
    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 -1
    }

    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
    }

~~~

Regards,
Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-08-06 19:53         ` Schimpe, Christina
@ 2025-08-06 19:54           ` Schimpe, Christina
  2025-08-07  3:17             ` Thiago Jung Bauermann
  2025-08-14 11:39           ` Andrew Burgess
  1 sibling, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-06 19:54 UTC (permalink / raw)
  To: gdb-patches, thiago.bauermann; +Cc: luis.machado, Andrew Burgess

HI Thiago,

> > >> > +    # Read PL3_SSP register.
> > >> > +    set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read
> > >> > + pl3_ssp value"]
> > >> > +
> > >> > +    # Write PL3_SSP register.
> > >> > +    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"
> > >> > +
> > >> > +    # Restore original value.
> > >> > +    gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main"
> > >> > + "restore
> > >> original pl3_ssp"
> > >> > +
> > >> > +    # Potential CET violations often only occur after resuming
> > >> > + normal
> > >> execution.
> > >> > +    # Therefore, it is important to test normal program
> > >> > + continuation
> > after
> > >> > +    # configuring the shadow stack pointer.
> > >> > +    gdb_continue_to_end
> > >>
> > >> I assume that if we continue with the bogus value in place the
> > >> inferior would either give an error or terminate.  Is it worth
> > >> trying this and checking that the inferior behaves as expected?
> > >
> > > If we don't reset the shadow stack pointer to it's original value we
> > > will see
> > a SEGV.
> > > Dependent on the address of the wrong shadow stack pointer it's
> > > either a SEGV with si code that points to a control flow protection
> > > fault or a
> > different si code.
> > >
> > > So if I stay in a valid address range for configuring pl3_ssp but
> > > don't restore the original value I'll see a control flow protection
> exception:
> > >
> > > [...]
> > > breakpoint 1, 0x0000555555555148 in main ()^M
> > > (gdb) print /x $pl3_ssp^M
> > > $1 = 0x7ffff7bfffe8^M
> > > (gdb) PASS: gdb.arch/amd64-ssp.exp: get hexadecimal valueof
> "$pl3_ssp"
> > > print /x $pl3_ssp = 0x7ffff7bfffe0^M
> > > $2 = 0x7ffff7bfffe0^M
> > > (gdb) PASS: gdb.arch/amd64-ssp.exp: set pl3_ssp value print /x
> > > $pl3_ssp^M
> > > $3 = 0x7ffff7bfffe0^M
> > > (gdb) PASS: gdb.arch/amd64-ssp.exp: read pl3_ssp value after setting
> > > continue^M Continuing.^M ^M Program received signal SIGSEGV,
> > > Segmentation fault.^M
> > > 0x0000555555555158 in main ()^M
> > > (gdb) FAIL: gdb.arch/amd64-ssp.exp: continue until exit
> > >
> > > Siginfo shows si_code = 10, which indicates a control protection fault.
> > >
> > > p $_siginfo^M
> > > $4 = {si_signo = 11, si_errno = 0, si_code = 10, [...]
> > >
> > > If I set the value of pl3_ssp as in the current test (0x12345678)
> > > I'll see a different SEGV actually
> > >
> > > p $_siginfo
> > > $4 = {si_signo = 11, si_errno = 0, si_code = 1, [...]
> > >
> > >>
> > >> What if, say, the $pl3_ssp value only ever made it as far as the
> > >> register cache, and was never actually written back to the inferior?
> > >> I don't think the above test would actually spot this bug, right?
> > >
> > > Hm, if I understand you correctly here and you mean the scenario as
> > > shown above the above test would spot this bug I think (as we saw a
> fail).
> > >
> > > Does my example above show what you described or do you mean a
> > > different scenario?
> >
> > Yes, something like the above would check that the register is
> > actually being written back to the hardware, and is written to the expected
> location.
> >
> > The current test, as written in the patch, writes a bad value to the
> > shadow stack, then restores the correct value.  What if the bad value
> > never actually got written back to the hardware at all, and was just
> > being held in the register cache?
> >
> > Having a test that writes a bad value, then does 'continue', and
> > expects to see something like 'Program received signal ...' would be a
> > reasonable indication that the write to the shadow stack actually made it
> to the h/w.
> >
> > Thanks,
> > Andrew
> 
> 
> Yes, I agree, I'll add:
> 
> ~~~
>     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 -1
>     }
> 
>     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
>     }
> 
> ~~~
> 
> Regards,
> Christina

Do you have a test for actual write back to HW (as above). If not, it might make sense to add it also for GCS?

Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-08-05  4:29           ` Thiago Jung Bauermann
  2025-08-05 15:29             ` Schimpe, Christina
@ 2025-08-06 20:52             ` Luis
  2025-08-11 11:52               ` Schimpe, Christina
  1 sibling, 1 reply; 67+ messages in thread
From: Luis @ 2025-08-06 20:52 UTC (permalink / raw)
  To: Thiago Jung Bauermann, Schimpe, Christina
  Cc: Andrew Burgess, gdb-patches, luis.machado

On 8/5/25 05:29, Thiago Jung Bauermann wrote:
> Hello,
> 
> "Schimpe, Christina" <christina.schimpe@intel.com> writes:
> 
>>>>>> +
>>>>>> +    # 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.
>>>>>> +
>>>>>> +    if {$gcore_generated} {
>>>>>> +	clean_restart $binfile
>>>>>> +
>>>>>> +	with_test_prefix "gcore corefile" {
>>>>>> +	    check_core_file $gcore_filename $ssp_in_gcore
>>>>>> +	}
>>>>>> +    } else {
>>>>>> +	fail "gcore corefile not generated"
>>>>>
>>>>> It's better, where possible, to avoid having pass/fail results that
>>>>> only show up down some code paths.
>>>>>
>>>>> In this case it's easy to avoid having a stray 'fail' by
>>>>> restructuring the code too:
>>>>>
>>>>>    gdb_assert { $gcore_generated } "gcore corefile created"
>>>>>    if { $gcore_generated } {
>>>>>      ... etc ...
>>>>>    }
>>>>>
>>>>> Now you'll always have either a pass or fail based on the gcore
>>>>> being generated.
>>>>
>>>> Good idea. I did that for aarch64-gcs-core.exp.
>>
>> If no OS corefile is found we will see a FAIL here.
>> The usual coredump testing doesn't fail in case the coredump file is not found.
>> So all gdb.base/corefile*.exp tests have sth. like:
>>
>> set corefile [core_find $binfile {}]
>> if {$corefile == ""} {
>>      return
>> }
>>
>> This can happen in case corefiles are managed, for instance, by apport on ubuntu.
>> Do we want a different behaviour ?
> 
> Interesting point. Perhaps a FAIL Isn't the best result to report in
> this case, but IMHO it would be worth reporting an UNTESTED or perhaps
> UNSUPPORTED result rather than silently returning.
> 
> I don't have a strong opinion on this matter though.
> 

When testing core files, I usually change the core_pattern in 
/proc/sys/kernel/core_pattern so the files are properly generated and 
saved. Personally I find it useful to have a FAIL output when the core 
file generation didn't work.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-08-06 19:54           ` Schimpe, Christina
@ 2025-08-07  3:17             ` Thiago Jung Bauermann
  0 siblings, 0 replies; 67+ messages in thread
From: Thiago Jung Bauermann @ 2025-08-07  3:17 UTC (permalink / raw)
  To: Schimpe, Christina; +Cc: gdb-patches, luis.machado, Andrew Burgess

Hello Christina,

"Schimpe, Christina" <christina.schimpe@intel.com> writes:

>> > >> > +    # Read PL3_SSP register.
>> > >> > +    set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read
>> > >> > + pl3_ssp value"]
>> > >> > +
>> > >> > +    # Write PL3_SSP register.
>> > >> > +    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"
>> > >> > +
>> > >> > +    # Restore original value.
>> > >> > +    gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main"
>> > >> > + "restore
>> > >> original pl3_ssp"
>> > >> > +
>> > >> > +    # Potential CET violations often only occur after resuming
>> > >> > + normal
>> > >> execution.
>> > >> > +    # Therefore, it is important to test normal program
>> > >> > + continuation
>> > after
>> > >> > +    # configuring the shadow stack pointer.
>> > >> > +    gdb_continue_to_end
>> > >>
>> > >> I assume that if we continue with the bogus value in place the
>> > >> inferior would either give an error or terminate.  Is it worth
>> > >> trying this and checking that the inferior behaves as expected?
>> > >
>> > > If we don't reset the shadow stack pointer to it's original value we
>> > > will see
>> > a SEGV.
>> > > Dependent on the address of the wrong shadow stack pointer it's
>> > > either a SEGV with si code that points to a control flow protection
>> > > fault or a
>> > different si code.
>> > >
>> > > So if I stay in a valid address range for configuring pl3_ssp but
>> > > don't restore the original value I'll see a control flow protection
>> exception:
>> > >
>> > > [...]
>> > > breakpoint 1, 0x0000555555555148 in main ()^M
>> > > (gdb) print /x $pl3_ssp^M
>> > > $1 = 0x7ffff7bfffe8^M
>> > > (gdb) PASS: gdb.arch/amd64-ssp.exp: get hexadecimal valueof
>> "$pl3_ssp"
>> > > print /x $pl3_ssp = 0x7ffff7bfffe0^M
>> > > $2 = 0x7ffff7bfffe0^M
>> > > (gdb) PASS: gdb.arch/amd64-ssp.exp: set pl3_ssp value print /x
>> > > $pl3_ssp^M
>> > > $3 = 0x7ffff7bfffe0^M
>> > > (gdb) PASS: gdb.arch/amd64-ssp.exp: read pl3_ssp value after setting
>> > > continue^M Continuing.^M ^M Program received signal SIGSEGV,
>> > > Segmentation fault.^M
>> > > 0x0000555555555158 in main ()^M
>> > > (gdb) FAIL: gdb.arch/amd64-ssp.exp: continue until exit
>> > >
>> > > Siginfo shows si_code = 10, which indicates a control protection fault.
>> > >
>> > > p $_siginfo^M
>> > > $4 = {si_signo = 11, si_errno = 0, si_code = 10, [...]
>> > >
>> > > If I set the value of pl3_ssp as in the current test (0x12345678)
>> > > I'll see a different SEGV actually
>> > >
>> > > p $_siginfo
>> > > $4 = {si_signo = 11, si_errno = 0, si_code = 1, [...]
>> > >
>> > >>
>> > >> What if, say, the $pl3_ssp value only ever made it as far as the
>> > >> register cache, and was never actually written back to the inferior?
>> > >> I don't think the above test would actually spot this bug, right?
>> > >
>> > > Hm, if I understand you correctly here and you mean the scenario as
>> > > shown above the above test would spot this bug I think (as we saw a
>> fail).
>> > >
>> > > Does my example above show what you described or do you mean a
>> > > different scenario?
>> >
>> > Yes, something like the above would check that the register is
>> > actually being written back to the hardware, and is written to the expected
>> location.
>> >
>> > The current test, as written in the patch, writes a bad value to the
>> > shadow stack, then restores the correct value.  What if the bad value
>> > never actually got written back to the hardware at all, and was just
>> > being held in the register cache?
>> >
>> > Having a test that writes a bad value, then does 'continue', and
>> > expects to see something like 'Program received signal ...' would be a
>> > reasonable indication that the write to the shadow stack actually made it
>> to the h/w.
>> >
>> > Thanks,
>> > Andrew
>> 
>> 
>> Yes, I agree, I'll add:
>> 
>> ~~~
>>     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 -1
>>     }
>> 
>>     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
>>     }
>> 
>> ~~~
>> 
>> Regards,
>> Christina
>
> Do you have a test for actual write back to HW (as above). If not, it
> might make sense to add it also for GCS?

I don't have a test for it, but I agree it's worth adding one. Will
do. Thanks for pointing out.

-- 
Thiago

^ permalink raw reply	[flat|nested] 67+ messages in thread

* Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-06-28  8:28 ` [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register Christina Schimpe
  2025-07-25 12:49   ` Andrew Burgess
  2025-07-29 13:51   ` Andrew Burgess
@ 2025-08-10 19:01   ` H.J. Lu
  2025-08-10 20:07     ` Schimpe, Christina
  2 siblings, 1 reply; 67+ messages in thread
From: H.J. Lu @ 2025-08-10 19:01 UTC (permalink / raw)
  To: Christina Schimpe; +Cc: gdb-patches, thiago.bauermann, luis.machado

On Sat, Jun 28, 2025 at 1:32 AM Christina Schimpe
<christina.schimpe@intel.com> wrote:
>
> This patch adds the user mode register PL3_SSP which is part of the
> Intel(R) Control-Flow Enforcement Technology (CET) feature for support
> of shadow stack.
> For now, only native and remote debugging support for shadow stack
> userspace on amd64 linux are covered by this patch including 64 bit and
> x32 support.  32 bit support is not covered due to missing Linux kernel
> support.
>
> This patch requires fixing the test gdb.base/inline-frame-cycle-unwind
> which is failing in case the shadow stack pointer is unavailable.
> Such a state is possible if shadow stack is disabled for the current thread
> but supported by HW.
>
> This test uses the Python unwinder inline-frame-cycle-unwind.py which fakes
> the cyclic stack cycle by reading the pending frame's registers and adding
> them to the unwinder:
>
> ~~~
> for reg in pending_frame.architecture().registers("general"):
>      val = pending_frame.read_register(reg)
>      unwinder.add_saved_register(reg, val)
>      return unwinder
> ~~~
>
> However, in case the python unwinder is used we add a register (pl3_ssp) that is
> unavailable.  This leads to a NOT_AVAILABLE_ERROR caught in
> gdb/frame-unwind.c:frame_unwind_try_unwinder and it is continued with standard
> unwinders.  This destroys the faked cyclic behavior and the stack is
> further unwinded after frame 5.
>
> In the working scenario an error should be triggered:
> ~~~
> bt
> 0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
> 1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> 2  0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> 3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> 4  0x000055555555516e in inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> 5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> Backtrace stopped: previous frame identical to this frame (corrupt stack?)
> (gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5: backtrace when the unwind is broken at frame 5
> ~~~
>
> To fix the Python unwinder, we simply skip the unavailable registers.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> Reviewed-By: Luis Machado <luis.machado@arm.com>
> ---
>  gdb/NEWS                                      |  3 +
>  gdb/amd64-linux-nat.c                         | 17 +++++
>  gdb/amd64-linux-tdep.c                        |  1 +
>  gdb/amd64-tdep.c                              |  6 +-
>  gdb/amd64-tdep.h                              |  1 +
>  gdb/arch/amd64.c                              | 10 +++
>  gdb/arch/i386.c                               |  4 ++
>  gdb/arch/x86-linux-tdesc-features.c           |  1 +
>  gdb/doc/gdb.texinfo                           |  4 ++
>  gdb/features/Makefile                         |  2 +
>  gdb/features/i386/32bit-ssp.c                 | 14 ++++
>  gdb/features/i386/32bit-ssp.xml               | 11 +++
>  gdb/features/i386/64bit-ssp.c                 | 14 ++++
>  gdb/features/i386/64bit-ssp.xml               | 11 +++
>  gdb/i386-tdep.c                               | 22 +++++-
>  gdb/i386-tdep.h                               |  4 ++
>  gdb/nat/x86-linux-tdesc.c                     |  2 +
>  gdb/nat/x86-linux.c                           | 57 +++++++++++++++
>  gdb/nat/x86-linux.h                           |  4 ++
>  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
>  gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
>  .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
>  gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
>  gdb/x86-linux-nat.c                           | 49 +++++++++++--
>  gdb/x86-linux-nat.h                           | 11 +++
>  gdb/x86-tdep.c                                | 21 ++++++
>  gdb/x86-tdep.h                                |  9 +++
>  gdbserver/linux-x86-low.cc                    | 28 +++++++-
>  gdbsupport/x86-xstate.h                       |  5 +-
>  29 files changed, 447 insertions(+), 10 deletions(-)
>  create mode 100644 gdb/features/i386/32bit-ssp.c
>  create mode 100644 gdb/features/i386/32bit-ssp.xml
>  create mode 100644 gdb/features/i386/64bit-ssp.c
>  create mode 100644 gdb/features/i386/64bit-ssp.xml
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 6c8a008d39d..ba555f0dea1 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,9 @@
>
>  *** Changes since GDB 16
>
> +* Support for the shadow stack pointer register on x86-64 or x86-64 with
> +  32-bit pointer size (X32) GNU/Linux.
> +
>  * Debugger Adapter Protocol changes
>
>    ** GDB now supports the "completions" request.
> diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
> index dbb9b3223cb..4df99ccca54 100644
> --- a/gdb/amd64-linux-nat.c
> +++ b/gdb/amd64-linux-nat.c
> @@ -32,6 +32,7 @@
>  #include "amd64-tdep.h"
>  #include "amd64-linux-tdep.h"
>  #include "i386-linux-tdep.h"
> +#include "x86-tdep.h"
>  #include "gdbsupport/x86-xstate.h"
>
>  #include "x86-linux-nat.h"
> @@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
>
>        if (have_ptrace_getregset == TRIBOOL_TRUE)
>         {
> +         if ((regnum == -1 && tdep->ssp_regnum > 0)
> +             || (regnum != -1 && regnum == tdep->ssp_regnum))
> +           {
> +             x86_linux_fetch_ssp (regcache, tid);
> +             if (regnum != -1)
> +               return;
> +           }
> +
>           /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
>              "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure on
>              Intel Skylake CPUs") that sometimes causes the mxcsr location in
> @@ -302,6 +311,14 @@ amd64_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
>        if (have_ptrace_getregset == TRIBOOL_TRUE)
>         {
>           gdb::byte_vector xstateregs (tdep->xsave_layout.sizeof_xsave);
> +         if ((regnum == -1 && tdep->ssp_regnum > 0)
> +             || (regnum != -1 && regnum == tdep->ssp_regnum))
> +           {
> +             x86_linux_store_ssp (regcache, tid);
> +             if (regnum != -1)
> +               return;
> +           }
> +
>           struct iovec iov;
>
>           iov.iov_base = xstateregs.data ();
> diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
> index 13e9c0e86ea..edb7d8da6ab 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -108,6 +108,7 @@ int amd64_linux_gregset_reg_offset[] =
>    -1, -1, -1, -1, -1, -1, -1, -1,
>    -1, -1, -1, -1, -1, -1, -1, -1,
>    -1,                          /* PKEYS register pkru  */
> +  -1,                          /* CET user mode register PL3_SSP.  */
>
>    /* End of hardware registers */
>    21 * 8, 22 * 8,                    /* fs_base and gs_base.  */
> diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c
> index 04539dd288a..450dbc38047 100644
> --- a/gdb/amd64-tdep.c
> +++ b/gdb/amd64-tdep.c
> @@ -3395,6 +3395,9 @@ amd64_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch,
>        tdep->num_pkeys_regs = 1;
>      }
>
> +  if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pl3_ssp") != nullptr)
> +    tdep->ssp_regnum = AMD64_PL3_SSP_REGNUM;
> +
>    tdep->num_byte_regs = 20;
>    tdep->num_word_regs = 16;
>    tdep->num_dword_regs = 16;
> @@ -3557,12 +3560,13 @@ const struct target_desc *
>  amd64_target_description (uint64_t xstate_bv_mask, bool segments)
>  {
>    static target_desc *amd64_tdescs \
> -    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
> +    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/][2/*segments*/] = {};
>    target_desc **tdesc;
>
>    tdesc = &amd64_tdescs[(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
>      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
>      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
> +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
>      [segments ? 1 : 0];
>
>    if (*tdesc == NULL)
> diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h
> index 82f781bf404..eb294f3dfbe 100644
> --- a/gdb/amd64-tdep.h
> +++ b/gdb/amd64-tdep.h
> @@ -81,6 +81,7 @@ enum amd64_regnum
>    AMD64_ZMM0H_REGNUM,
>    AMD64_ZMM31H_REGNUM = AMD64_ZMM0H_REGNUM + 31,
>    AMD64_PKRU_REGNUM,
> +  AMD64_PL3_SSP_REGNUM,
>    AMD64_FSBASE_REGNUM,
>    AMD64_GSBASE_REGNUM
>  };
> diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c
> index 3181f827356..c9925fa6b66 100644
> --- a/gdb/arch/amd64.c
> +++ b/gdb/arch/amd64.c
> @@ -28,6 +28,8 @@
>  #include "../features/i386/64bit-sse.c"
>  #include "../features/i386/pkeys.c"
>
> +#include "../features/i386/64bit-ssp.c"
> +#include "../features/i386/32bit-ssp.c"
>  #include "../features/i386/x32-core.c"
>
>  /* See arch/amd64.h.  */
> @@ -68,5 +70,13 @@ amd64_create_target_description (uint64_t xstate_bv_mask, bool is_x32,
>    if (xstate_bv_mask & X86_XSTATE_PKRU)
>      regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
>
> +  if (xstate_bv_mask & X86_XSTATE_CET_U)
> +    {
> +      if (!is_x32)
> +       regnum = create_feature_i386_64bit_ssp (tdesc.get (), regnum);
> +      else
> +       regnum = create_feature_i386_32bit_ssp (tdesc.get (), regnum);
> +    }
> +
>    return tdesc.release ();
>  }
> diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c
> index e04d8c5dd94..87058e32dcb 100644
> --- a/gdb/arch/i386.c
> +++ b/gdb/arch/i386.c
> @@ -28,6 +28,7 @@
>  #include "../features/i386/32bit-avx512.c"
>  #include "../features/i386/32bit-segments.c"
>  #include "../features/i386/pkeys.c"
> +#include "../features/i386/32bit-ssp.c"
>
>  /* See arch/i386.h.  */
>
> @@ -66,5 +67,8 @@ i386_create_target_description (uint64_t xstate_bv_mask, bool is_linux,
>    if (xstate_bv_mask & X86_XSTATE_PKRU)
>      regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
>
> +  if (xstate_bv_mask & X86_XSTATE_CET_U)
> +    regnum = create_feature_i386_32bit_ssp (tdesc.get (), regnum);
> +
>    return tdesc.release ();
>  }
> diff --git a/gdb/arch/x86-linux-tdesc-features.c b/gdb/arch/x86-linux-tdesc-features.c
> index 68f37fccaef..f288f120cfb 100644
> --- a/gdb/arch/x86-linux-tdesc-features.c
> +++ b/gdb/arch/x86-linux-tdesc-features.c
> @@ -65,6 +65,7 @@ struct x86_xstate_feature {
>
>  static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
>    /* Feature,           i386,  amd64,  x32.  */
> +  { X86_XSTATE_CET_U,  false,  true,   true },
>    { X86_XSTATE_PKRU,   true,   true,   true },
>    { X86_XSTATE_AVX512, true,   true,   true },
>    { X86_XSTATE_AVX,    true,   true,   true },
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 4ef640698bd..0881ac4aee5 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -49959,6 +49959,10 @@ The @samp{org.gnu.gdb.i386.pkeys} feature is optional.  It should
>  describe a single register, @samp{pkru}.  It is a 32-bit register
>  valid for i386 and amd64.
>
> +The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should describe the
> +user mode register @samp{pl3_ssp} which has 64 bits on amd64.  Following the
> +restriction of the Linux kernel, only amd64 is supported for now.
> +
>  @node LoongArch Features
>  @subsection LoongArch Features
>  @cindex target descriptions, LoongArch Features
> diff --git a/gdb/features/Makefile b/gdb/features/Makefile
> index 7a8c7999733..2afda1ccd00 100644
> --- a/gdb/features/Makefile
> +++ b/gdb/features/Makefile
> @@ -225,6 +225,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
>         i386/32bit-avx.xml \
>         i386/32bit-avx512.xml \
>         i386/32bit-segments.xml \
> +       i386/32bit-ssp.xml \
>         i386/64bit-avx512.xml \
>         i386/64bit-core.xml \
>         i386/64bit-segments.xml \
> @@ -232,6 +233,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
>         i386/64bit-linux.xml \
>         i386/64bit-sse.xml \
>         i386/pkeys.xml \
> +       i386/64bit-ssp.xml \
>         i386/x32-core.xml \
>         loongarch/base32.xml \
>         loongarch/base64.xml \
> diff --git a/gdb/features/i386/32bit-ssp.c b/gdb/features/i386/32bit-ssp.c
> new file mode 100644
> index 00000000000..991bae3c1e6
> --- /dev/null
> +++ b/gdb/features/i386/32bit-ssp.c
> @@ -0,0 +1,14 @@
> +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> +  Original: 32bit-ssp.xml */
> +
> +#include "gdbsupport/tdesc.h"
> +
> +static int
> +create_feature_i386_32bit_ssp (struct target_desc *result, long regnum)
> +{
> +  struct tdesc_feature *feature;
> +
> +  feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pl3_ssp");
> +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 32, "data_ptr");
> +  return regnum;
> +}
> diff --git a/gdb/features/i386/32bit-ssp.xml b/gdb/features/i386/32bit-ssp.xml
> new file mode 100644
> index 00000000000..d17e7004eec
> --- /dev/null
> +++ b/gdb/features/i386/32bit-ssp.xml
> @@ -0,0 +1,11 @@
> +<?xml version="1.0"?>
> +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> +
> +     Copying and distribution of this file, with or without modification,
> +     are permitted in any medium without royalty provided the copyright
> +     notice and this notice are preserved.  -->
> +
> +<!DOCTYPE feature SYSTEM "gdb-target.dtd">
> +<feature name="org.gnu.gdb.i386.pl3_ssp">
> +  <reg name="pl3_ssp" bitsize="32" type="data_ptr"/>
> +</feature>
> diff --git a/gdb/features/i386/64bit-ssp.c b/gdb/features/i386/64bit-ssp.c
> new file mode 100644
> index 00000000000..5468099ddf6
> --- /dev/null
> +++ b/gdb/features/i386/64bit-ssp.c
> @@ -0,0 +1,14 @@
> +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> +  Original: 64bit-ssp.xml */
> +
> +#include "gdbsupport/tdesc.h"
> +
> +static int
> +create_feature_i386_64bit_ssp (struct target_desc *result, long regnum)
> +{
> +  struct tdesc_feature *feature;
> +
> +  feature = tdesc_create_feature (result, "org.gnu.gdb.i386.pl3_ssp");
> +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 64, "data_ptr");
> +  return regnum;
> +}
> diff --git a/gdb/features/i386/64bit-ssp.xml b/gdb/features/i386/64bit-ssp.xml
> new file mode 100644
> index 00000000000..a0688d018a5
> --- /dev/null
> +++ b/gdb/features/i386/64bit-ssp.xml
> @@ -0,0 +1,11 @@
> +<?xml version="1.0"?>
> +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> +
> +     Copying and distribution of this file, with or without modification,
> +     are permitted in any medium without royalty provided the copyright
> +     notice and this notice are preserved.  -->
> +
> +<!DOCTYPE feature SYSTEM "gdb-target.dtd">
> +<feature name="org.gnu.gdb.i386.pl3_ssp">
> +  <reg name="pl3_ssp" bitsize="64" type="data_ptr"/>
> +</feature>
> diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c
> index 90ff0c5c706..8eb5b4fac86 100644
> --- a/gdb/i386-tdep.c
> +++ b/gdb/i386-tdep.c
> @@ -8403,7 +8403,8 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>    const struct tdesc_feature *feature_core;
>
>    const struct tdesc_feature *feature_sse, *feature_avx, *feature_avx512,
> -                            *feature_pkeys, *feature_segments;
> +                            *feature_pkeys, *feature_segments,
> +                            *feature_pl3_ssp;
>    int i, num_regs, valid_p;
>
>    if (! tdesc_has_registers (tdesc))
> @@ -8429,6 +8430,9 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>    /* Try PKEYS  */
>    feature_pkeys = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pkeys");
>
> +  /* Try Shadow Stack.  */
> +  feature_pl3_ssp = tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pl3_ssp");
> +
>    valid_p = 1;
>
>    /* The XCR0 bits.  */
> @@ -8544,6 +8548,15 @@ i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>                                             tdep->pkeys_register_names[i]);
>      }
>
> +  if (feature_pl3_ssp != nullptr)
> +    {
> +      if (tdep->ssp_regnum < 0)
> +       tdep->ssp_regnum = I386_PL3_SSP_REGNUM;
> +
> +      valid_p &= tdesc_numbered_register (feature_pl3_ssp, tdesc_data,
> +                                         tdep->ssp_regnum, "pl3_ssp");
> +    }
> +
>    return valid_p;
>  }
>
> @@ -8835,6 +8848,9 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
>    /* No segment base registers.  */
>    tdep->fsbase_regnum = -1;
>
> +  /* No shadow stack pointer register.  */
> +  tdep->ssp_regnum = -1;
> +
>    tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
>
>    set_gdbarch_relocate_instruction (gdbarch, i386_relocate_instruction);
> @@ -8955,13 +8971,15 @@ const struct target_desc *
>  i386_target_description (uint64_t xstate_bv_mask, bool segments)
>  {
>    static target_desc *i386_tdescs \
> -    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
> +    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/] \
> +    [2/*segments*/] = {};
>    target_desc **tdesc;
>
>    tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
>      [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
>      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
>      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
> +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
>      [segments ? 1 : 0];
>
>    if (*tdesc == NULL)
> diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
> index 239bc8674e8..60d6f3eb732 100644
> --- a/gdb/i386-tdep.h
> +++ b/gdb/i386-tdep.h
> @@ -191,6 +191,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
>    /* PKEYS register names.  */
>    const char * const *pkeys_register_names = nullptr;
>
> +  /* Shadow stack pointer register.  */
> +  int ssp_regnum = 0;
> +
>    /* Register number for %fsbase.  Set this to -1 to indicate the
>       absence of segment base registers.  */
>    int fsbase_regnum = 0;
> @@ -293,6 +296,7 @@ enum i386_regnum
>    I386_ZMM0H_REGNUM,           /* %zmm0h */
>    I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
>    I386_PKRU_REGNUM,
> +  I386_PL3_SSP_REGNUM,
>    I386_FSBASE_REGNUM,
>    I386_GSBASE_REGNUM
>  };
> diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
> index e9cf2527c5f..5bc36b6bef2 100644
> --- a/gdb/nat/x86-linux-tdesc.c
> +++ b/gdb/nat/x86-linux-tdesc.c
> @@ -110,6 +110,8 @@ x86_linux_tdesc_for_tid (int tid, uint64_t *xstate_bv_storage,
>             = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
>
>           *xstate_bv_storage = xcr0;
> +         if (x86_check_ssp_support (tid))
> +           *xstate_bv_storage |= X86_XSTATE_CET_U;
>         }
>      }
>
> diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
> index 0bdff736f8a..1756d5441fc 100644
> --- a/gdb/nat/x86-linux.c
> +++ b/gdb/nat/x86-linux.c
> @@ -17,6 +17,12 @@
>     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 "elf/common.h"
> +#include "gdbsupport/common-defs.h"
> +#include "nat/gdb_ptrace.h"
> +#include "nat/linux-ptrace.h"
> +#include "nat/x86-cpuid.h"
> +#include <sys/uio.h>
>  #include "x86-linux.h"
>  #include "x86-linux-dregs.h"
>  #include "nat/gdb_ptrace.h"
> @@ -126,3 +132,54 @@ x86_linux_ptrace_get_arch_size (int tid)
>    return x86_linux_arch_size (false, false);
>  #endif
>  }
> +
> +/* See nat/x86-linux.h.  */
> +
> +bool
> +x86_check_ssp_support (const int tid)
> +{
> +  /* It's not enough to check shadow stack support with the ptrace call
> +     below only, as we cannot distinguish between shadow stack not enabled
> +     for the current thread and shadow stack is not supported by HW.  In
> +     both scenarios the ptrace call fails with ENODEV.  In case shadow
> +     stack is not enabled for the current thread, we still want to return
> +     true.  */
> +  unsigned int eax, ebx, ecx, edx;
> +
> +  __get_cpuid_count (7, 0, &eax, &ebx, &ecx, &edx);

It should be

  if (! __get_cpuid_count (7, 0, &eax, &ebx, &ecx, &edx))
    return false;

> +
> +  if ((ecx & bit_SHSTK) == 0)
> +    return false;
> +


H.J.

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-08-10 19:01   ` H.J. Lu
@ 2025-08-10 20:07     ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-10 20:07 UTC (permalink / raw)
  To: H.J. Lu; +Cc: gdb-patches, thiago.bauermann, luis.machado

Hi HJ,

Thank you for the review.

> -----Original Message-----
> From: H.J. Lu <hjl.tools@gmail.com>
> Sent: Sunday, August 10, 2025 9:01 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>
> Cc: gdb-patches@sourceware.org; thiago.bauermann@linaro.org;
> luis.machado@arm.com
> Subject: Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow
> stack pointer register.
> 
> On Sat, Jun 28, 2025 at 1:32 AM Christina Schimpe
> <christina.schimpe@intel.com> wrote:
> >
> > This patch adds the user mode register PL3_SSP which is part of the
> > Intel(R) Control-Flow Enforcement Technology (CET) feature for support
> > of shadow stack.
> > For now, only native and remote debugging support for shadow stack
> > userspace on amd64 linux are covered by this patch including 64 bit
> > and
> > x32 support.  32 bit support is not covered due to missing Linux
> > kernel support.
> >
> > This patch requires fixing the test gdb.base/inline-frame-cycle-unwind
> > which is failing in case the shadow stack pointer is unavailable.
> > Such a state is possible if shadow stack is disabled for the current
> > thread but supported by HW.
> >
> > This test uses the Python unwinder inline-frame-cycle-unwind.py which
> > fakes the cyclic stack cycle by reading the pending frame's registers
> > and adding them to the unwinder:
> >
> > ~~~
> > for reg in pending_frame.architecture().registers("general"):
> >      val = pending_frame.read_register(reg)
> >      unwinder.add_saved_register(reg, val)
> >      return unwinder
> > ~~~
> >
> > However, in case the python unwinder is used we add a register
> > (pl3_ssp) that is unavailable.  This leads to a NOT_AVAILABLE_ERROR
> > caught in gdb/frame-unwind.c:frame_unwind_try_unwinder and it is
> > continued with standard unwinders.  This destroys the faked cyclic
> > behavior and the stack is further unwinded after frame 5.
> >
> > In the working scenario an error should be triggered:
> > ~~~
> > bt
> > 0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
> > 1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > 2  0x000055555555516e in inline_func () at
> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> > 3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > 4  0x000055555555516e in inline_func () at
> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
> > 5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
> > Backtrace stopped: previous frame identical to this frame (corrupt
> > stack?)
> > (gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5:
> > backtrace when the unwind is broken at frame 5 ~~~
> >
> > To fix the Python unwinder, we simply skip the unavailable registers.
> >
> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
> > Reviewed-By: Luis Machado <luis.machado@arm.com>
> > ---
> >  gdb/NEWS                                      |  3 +
> >  gdb/amd64-linux-nat.c                         | 17 +++++
> >  gdb/amd64-linux-tdep.c                        |  1 +
> >  gdb/amd64-tdep.c                              |  6 +-
> >  gdb/amd64-tdep.h                              |  1 +
> >  gdb/arch/amd64.c                              | 10 +++
> >  gdb/arch/i386.c                               |  4 ++
> >  gdb/arch/x86-linux-tdesc-features.c           |  1 +
> >  gdb/doc/gdb.texinfo                           |  4 ++
> >  gdb/features/Makefile                         |  2 +
> >  gdb/features/i386/32bit-ssp.c                 | 14 ++++
> >  gdb/features/i386/32bit-ssp.xml               | 11 +++
> >  gdb/features/i386/64bit-ssp.c                 | 14 ++++
> >  gdb/features/i386/64bit-ssp.xml               | 11 +++
> >  gdb/i386-tdep.c                               | 22 +++++-
> >  gdb/i386-tdep.h                               |  4 ++
> >  gdb/nat/x86-linux-tdesc.c                     |  2 +
> >  gdb/nat/x86-linux.c                           | 57 +++++++++++++++
> >  gdb/nat/x86-linux.h                           |  4 ++
> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
> >  gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
> >  .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
> >  gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
> >  gdb/x86-linux-nat.c                           | 49 +++++++++++--
> >  gdb/x86-linux-nat.h                           | 11 +++
> >  gdb/x86-tdep.c                                | 21 ++++++
> >  gdb/x86-tdep.h                                |  9 +++
> >  gdbserver/linux-x86-low.cc                    | 28 +++++++-
> >  gdbsupport/x86-xstate.h                       |  5 +-
> >  29 files changed, 447 insertions(+), 10 deletions(-)  create mode
> > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
> > gdb/features/i386/32bit-ssp.xml  create mode 100644
> > gdb/features/i386/64bit-ssp.c  create mode 100644
> > gdb/features/i386/64bit-ssp.xml  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack.c
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> >
> > diff --git a/gdb/NEWS b/gdb/NEWS
> > index 6c8a008d39d..ba555f0dea1 100644
> > --- a/gdb/NEWS
> > +++ b/gdb/NEWS
> > @@ -3,6 +3,9 @@
> >
> >  *** Changes since GDB 16
> >
> > +* Support for the shadow stack pointer register on x86-64 or x86-64
> > +with
> > +  32-bit pointer size (X32) GNU/Linux.
> > +
> >  * Debugger Adapter Protocol changes
> >
> >    ** GDB now supports the "completions" request.
> > diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index
> > dbb9b3223cb..4df99ccca54 100644
> > --- a/gdb/amd64-linux-nat.c
> > +++ b/gdb/amd64-linux-nat.c
> > @@ -32,6 +32,7 @@
> >  #include "amd64-tdep.h"
> >  #include "amd64-linux-tdep.h"
> >  #include "i386-linux-tdep.h"
> > +#include "x86-tdep.h"
> >  #include "gdbsupport/x86-xstate.h"
> >
> >  #include "x86-linux-nat.h"
> > @@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers (struct
> > regcache *regcache, int regnum)
> >
> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
> >         {
> > +         if ((regnum == -1 && tdep->ssp_regnum > 0)
> > +             || (regnum != -1 && regnum == tdep->ssp_regnum))
> > +           {
> > +             x86_linux_fetch_ssp (regcache, tid);
> > +             if (regnum != -1)
> > +               return;
> > +           }
> > +
> >           /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
> >              "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure on
> >              Intel Skylake CPUs") that sometimes causes the mxcsr
> > location in @@ -302,6 +311,14 @@
> amd64_linux_nat_target::store_registers (struct regcache *regcache, int
> regnum)
> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
> >         {
> >           gdb::byte_vector xstateregs
> > (tdep->xsave_layout.sizeof_xsave);
> > +         if ((regnum == -1 && tdep->ssp_regnum > 0)
> > +             || (regnum != -1 && regnum == tdep->ssp_regnum))
> > +           {
> > +             x86_linux_store_ssp (regcache, tid);
> > +             if (regnum != -1)
> > +               return;
> > +           }
> > +
> >           struct iovec iov;
> >
> >           iov.iov_base = xstateregs.data (); diff --git
> > a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index
> > 13e9c0e86ea..edb7d8da6ab 100644
> > --- a/gdb/amd64-linux-tdep.c
> > +++ b/gdb/amd64-linux-tdep.c
> > @@ -108,6 +108,7 @@ int amd64_linux_gregset_reg_offset[] =
> >    -1, -1, -1, -1, -1, -1, -1, -1,
> >    -1, -1, -1, -1, -1, -1, -1, -1,
> >    -1,                          /* PKEYS register pkru  */
> > +  -1,                          /* CET user mode register PL3_SSP.  */
> >
> >    /* End of hardware registers */
> >    21 * 8, 22 * 8,                    /* fs_base and gs_base.  */
> > diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index
> > 04539dd288a..450dbc38047 100644
> > --- a/gdb/amd64-tdep.c
> > +++ b/gdb/amd64-tdep.c
> > @@ -3395,6 +3395,9 @@ amd64_init_abi (struct gdbarch_info info,
> struct gdbarch *gdbarch,
> >        tdep->num_pkeys_regs = 1;
> >      }
> >
> > +  if (tdesc_find_feature (tdesc, "org.gnu.gdb.i386.pl3_ssp") != nullptr)
> > +    tdep->ssp_regnum = AMD64_PL3_SSP_REGNUM;
> > +
> >    tdep->num_byte_regs = 20;
> >    tdep->num_word_regs = 16;
> >    tdep->num_dword_regs = 16;
> > @@ -3557,12 +3560,13 @@ const struct target_desc *
> > amd64_target_description (uint64_t xstate_bv_mask, bool segments)  {
> >    static target_desc *amd64_tdescs \
> > -    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
> > +    [2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/][2/*segments*/] =
> > + {};
> >    target_desc **tdesc;
> >
> >    tdesc = &amd64_tdescs[(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
> >      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
> >      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
> > +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
> >      [segments ? 1 : 0];
> >
> >    if (*tdesc == NULL)
> > diff --git a/gdb/amd64-tdep.h b/gdb/amd64-tdep.h index
> > 82f781bf404..eb294f3dfbe 100644
> > --- a/gdb/amd64-tdep.h
> > +++ b/gdb/amd64-tdep.h
> > @@ -81,6 +81,7 @@ enum amd64_regnum
> >    AMD64_ZMM0H_REGNUM,
> >    AMD64_ZMM31H_REGNUM = AMD64_ZMM0H_REGNUM + 31,
> >    AMD64_PKRU_REGNUM,
> > +  AMD64_PL3_SSP_REGNUM,
> >    AMD64_FSBASE_REGNUM,
> >    AMD64_GSBASE_REGNUM
> >  };
> > diff --git a/gdb/arch/amd64.c b/gdb/arch/amd64.c index
> > 3181f827356..c9925fa6b66 100644
> > --- a/gdb/arch/amd64.c
> > +++ b/gdb/arch/amd64.c
> > @@ -28,6 +28,8 @@
> >  #include "../features/i386/64bit-sse.c"
> >  #include "../features/i386/pkeys.c"
> >
> > +#include "../features/i386/64bit-ssp.c"
> > +#include "../features/i386/32bit-ssp.c"
> >  #include "../features/i386/x32-core.c"
> >
> >  /* See arch/amd64.h.  */
> > @@ -68,5 +70,13 @@ amd64_create_target_description (uint64_t
> xstate_bv_mask, bool is_x32,
> >    if (xstate_bv_mask & X86_XSTATE_PKRU)
> >      regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
> >
> > +  if (xstate_bv_mask & X86_XSTATE_CET_U)
> > +    {
> > +      if (!is_x32)
> > +       regnum = create_feature_i386_64bit_ssp (tdesc.get (), regnum);
> > +      else
> > +       regnum = create_feature_i386_32bit_ssp (tdesc.get (), regnum);
> > +    }
> > +
> >    return tdesc.release ();
> >  }
> > diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c index
> > e04d8c5dd94..87058e32dcb 100644
> > --- a/gdb/arch/i386.c
> > +++ b/gdb/arch/i386.c
> > @@ -28,6 +28,7 @@
> >  #include "../features/i386/32bit-avx512.c"
> >  #include "../features/i386/32bit-segments.c"
> >  #include "../features/i386/pkeys.c"
> > +#include "../features/i386/32bit-ssp.c"
> >
> >  /* See arch/i386.h.  */
> >
> > @@ -66,5 +67,8 @@ i386_create_target_description (uint64_t
> xstate_bv_mask, bool is_linux,
> >    if (xstate_bv_mask & X86_XSTATE_PKRU)
> >      regnum = create_feature_i386_pkeys (tdesc.get (), regnum);
> >
> > +  if (xstate_bv_mask & X86_XSTATE_CET_U)
> > +    regnum = create_feature_i386_32bit_ssp (tdesc.get (), regnum);
> > +
> >    return tdesc.release ();
> >  }
> > diff --git a/gdb/arch/x86-linux-tdesc-features.c
> > b/gdb/arch/x86-linux-tdesc-features.c
> > index 68f37fccaef..f288f120cfb 100644
> > --- a/gdb/arch/x86-linux-tdesc-features.c
> > +++ b/gdb/arch/x86-linux-tdesc-features.c
> > @@ -65,6 +65,7 @@ struct x86_xstate_feature {
> >
> >  static constexpr x86_xstate_feature x86_linux_all_xstate_features[] = {
> >    /* Feature,           i386,  amd64,  x32.  */
> > +  { X86_XSTATE_CET_U,  false,  true,   true },
> >    { X86_XSTATE_PKRU,   true,   true,   true },
> >    { X86_XSTATE_AVX512, true,   true,   true },
> >    { X86_XSTATE_AVX,    true,   true,   true },
> > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index
> > 4ef640698bd..0881ac4aee5 100644
> > --- a/gdb/doc/gdb.texinfo
> > +++ b/gdb/doc/gdb.texinfo
> > @@ -49959,6 +49959,10 @@ The @samp{org.gnu.gdb.i386.pkeys}
> feature is
> > optional.  It should  describe a single register, @samp{pkru}.  It is
> > a 32-bit register  valid for i386 and amd64.
> >
> > +The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
> > +describe the user mode register @samp{pl3_ssp} which has 64 bits on
> > +amd64.  Following the restriction of the Linux kernel, only amd64 is
> supported for now.
> > +
> >  @node LoongArch Features
> >  @subsection LoongArch Features
> >  @cindex target descriptions, LoongArch Features diff --git
> > a/gdb/features/Makefile b/gdb/features/Makefile index
> > 7a8c7999733..2afda1ccd00 100644
> > --- a/gdb/features/Makefile
> > +++ b/gdb/features/Makefile
> > @@ -225,6 +225,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
> >         i386/32bit-avx.xml \
> >         i386/32bit-avx512.xml \
> >         i386/32bit-segments.xml \
> > +       i386/32bit-ssp.xml \
> >         i386/64bit-avx512.xml \
> >         i386/64bit-core.xml \
> >         i386/64bit-segments.xml \
> > @@ -232,6 +233,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
> >         i386/64bit-linux.xml \
> >         i386/64bit-sse.xml \
> >         i386/pkeys.xml \
> > +       i386/64bit-ssp.xml \
> >         i386/x32-core.xml \
> >         loongarch/base32.xml \
> >         loongarch/base64.xml \
> > diff --git a/gdb/features/i386/32bit-ssp.c
> > b/gdb/features/i386/32bit-ssp.c new file mode 100644 index
> > 00000000000..991bae3c1e6
> > --- /dev/null
> > +++ b/gdb/features/i386/32bit-ssp.c
> > @@ -0,0 +1,14 @@
> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> > +  Original: 32bit-ssp.xml */
> > +
> > +#include "gdbsupport/tdesc.h"
> > +
> > +static int
> > +create_feature_i386_32bit_ssp (struct target_desc *result, long
> > +regnum) {
> > +  struct tdesc_feature *feature;
> > +
> > +  feature = tdesc_create_feature (result,
> > +"org.gnu.gdb.i386.pl3_ssp");
> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 32,
> > +"data_ptr");
> > +  return regnum;
> > +}
> > diff --git a/gdb/features/i386/32bit-ssp.xml
> > b/gdb/features/i386/32bit-ssp.xml new file mode 100644 index
> > 00000000000..d17e7004eec
> > --- /dev/null
> > +++ b/gdb/features/i386/32bit-ssp.xml
> > @@ -0,0 +1,11 @@
> > +<?xml version="1.0"?>
> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> > +
> > +     Copying and distribution of this file, with or without modification,
> > +     are permitted in any medium without royalty provided the copyright
> > +     notice and this notice are preserved.  -->
> > +
> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
> > +name="org.gnu.gdb.i386.pl3_ssp">
> > +  <reg name="pl3_ssp" bitsize="32" type="data_ptr"/> </feature>
> > diff --git a/gdb/features/i386/64bit-ssp.c
> > b/gdb/features/i386/64bit-ssp.c new file mode 100644 index
> > 00000000000..5468099ddf6
> > --- /dev/null
> > +++ b/gdb/features/i386/64bit-ssp.c
> > @@ -0,0 +1,14 @@
> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
> > +  Original: 64bit-ssp.xml */
> > +
> > +#include "gdbsupport/tdesc.h"
> > +
> > +static int
> > +create_feature_i386_64bit_ssp (struct target_desc *result, long
> > +regnum) {
> > +  struct tdesc_feature *feature;
> > +
> > +  feature = tdesc_create_feature (result,
> > +"org.gnu.gdb.i386.pl3_ssp");
> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 64,
> > +"data_ptr");
> > +  return regnum;
> > +}
> > diff --git a/gdb/features/i386/64bit-ssp.xml
> > b/gdb/features/i386/64bit-ssp.xml new file mode 100644 index
> > 00000000000..a0688d018a5
> > --- /dev/null
> > +++ b/gdb/features/i386/64bit-ssp.xml
> > @@ -0,0 +1,11 @@
> > +<?xml version="1.0"?>
> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
> > +
> > +     Copying and distribution of this file, with or without modification,
> > +     are permitted in any medium without royalty provided the copyright
> > +     notice and this notice are preserved.  -->
> > +
> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
> > +name="org.gnu.gdb.i386.pl3_ssp">
> > +  <reg name="pl3_ssp" bitsize="64" type="data_ptr"/> </feature>
> > diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index
> > 90ff0c5c706..8eb5b4fac86 100644
> > --- a/gdb/i386-tdep.c
> > +++ b/gdb/i386-tdep.c
> > @@ -8403,7 +8403,8 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
> *tdep,
> >    const struct tdesc_feature *feature_core;
> >
> >    const struct tdesc_feature *feature_sse, *feature_avx, *feature_avx512,
> > -                            *feature_pkeys, *feature_segments;
> > +                            *feature_pkeys, *feature_segments,
> > +                            *feature_pl3_ssp;
> >    int i, num_regs, valid_p;
> >
> >    if (! tdesc_has_registers (tdesc))
> > @@ -8429,6 +8430,9 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
> *tdep,
> >    /* Try PKEYS  */
> >    feature_pkeys = tdesc_find_feature (tdesc,
> > "org.gnu.gdb.i386.pkeys");
> >
> > +  /* Try Shadow Stack.  */
> > +  feature_pl3_ssp = tdesc_find_feature (tdesc,
> > + "org.gnu.gdb.i386.pl3_ssp");
> > +
> >    valid_p = 1;
> >
> >    /* The XCR0 bits.  */
> > @@ -8544,6 +8548,15 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
> *tdep,
> >                                             tdep->pkeys_register_names[i]);
> >      }
> >
> > +  if (feature_pl3_ssp != nullptr)
> > +    {
> > +      if (tdep->ssp_regnum < 0)
> > +       tdep->ssp_regnum = I386_PL3_SSP_REGNUM;
> > +
> > +      valid_p &= tdesc_numbered_register (feature_pl3_ssp, tdesc_data,
> > +                                         tdep->ssp_regnum, "pl3_ssp");
> > +    }
> > +
> >    return valid_p;
> >  }
> >
> > @@ -8835,6 +8848,9 @@ i386_gdbarch_init (struct gdbarch_info info,
> struct gdbarch_list *arches)
> >    /* No segment base registers.  */
> >    tdep->fsbase_regnum = -1;
> >
> > +  /* No shadow stack pointer register.  */  tdep->ssp_regnum = -1;
> > +
> >    tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
> >
> >    set_gdbarch_relocate_instruction (gdbarch,
> > i386_relocate_instruction); @@ -8955,13 +8971,15 @@ const struct
> > target_desc *  i386_target_description (uint64_t xstate_bv_mask, bool
> > segments)  {
> >    static target_desc *i386_tdescs \
> > -    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] = {};
> > +    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/] \
> > +    [2/*segments*/] = {};
> >    target_desc **tdesc;
> >
> >    tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
> >      [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
> >      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
> >      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
> > +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
> >      [segments ? 1 : 0];
> >
> >    if (*tdesc == NULL)
> > diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index
> > 239bc8674e8..60d6f3eb732 100644
> > --- a/gdb/i386-tdep.h
> > +++ b/gdb/i386-tdep.h
> > @@ -191,6 +191,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
> >    /* PKEYS register names.  */
> >    const char * const *pkeys_register_names = nullptr;
> >
> > +  /* Shadow stack pointer register.  */  int ssp_regnum = 0;
> > +
> >    /* Register number for %fsbase.  Set this to -1 to indicate the
> >       absence of segment base registers.  */
> >    int fsbase_regnum = 0;
> > @@ -293,6 +296,7 @@ enum i386_regnum
> >    I386_ZMM0H_REGNUM,           /* %zmm0h */
> >    I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
> >    I386_PKRU_REGNUM,
> > +  I386_PL3_SSP_REGNUM,
> >    I386_FSBASE_REGNUM,
> >    I386_GSBASE_REGNUM
> >  };
> > diff --git a/gdb/nat/x86-linux-tdesc.c b/gdb/nat/x86-linux-tdesc.c
> > index e9cf2527c5f..5bc36b6bef2 100644
> > --- a/gdb/nat/x86-linux-tdesc.c
> > +++ b/gdb/nat/x86-linux-tdesc.c
> > @@ -110,6 +110,8 @@ x86_linux_tdesc_for_tid (int tid, uint64_t
> *xstate_bv_storage,
> >             = x86_fetch_xsave_layout (xcr0, x86_xsave_length ());
> >
> >           *xstate_bv_storage = xcr0;
> > +         if (x86_check_ssp_support (tid))
> > +           *xstate_bv_storage |= X86_XSTATE_CET_U;
> >         }
> >      }
> >
> > diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c index
> > 0bdff736f8a..1756d5441fc 100644
> > --- a/gdb/nat/x86-linux.c
> > +++ b/gdb/nat/x86-linux.c
> > @@ -17,6 +17,12 @@
> >     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 "elf/common.h"
> > +#include "gdbsupport/common-defs.h"
> > +#include "nat/gdb_ptrace.h"
> > +#include "nat/linux-ptrace.h"
> > +#include "nat/x86-cpuid.h"
> > +#include <sys/uio.h>
> >  #include "x86-linux.h"
> >  #include "x86-linux-dregs.h"
> >  #include "nat/gdb_ptrace.h"
> > @@ -126,3 +132,54 @@ x86_linux_ptrace_get_arch_size (int tid)
> >    return x86_linux_arch_size (false, false);  #endif  }
> > +
> > +/* See nat/x86-linux.h.  */
> > +
> > +bool
> > +x86_check_ssp_support (const int tid) {
> > +  /* It's not enough to check shadow stack support with the ptrace call
> > +     below only, as we cannot distinguish between shadow stack not
> enabled
> > +     for the current thread and shadow stack is not supported by HW.  In
> > +     both scenarios the ptrace call fails with ENODEV.  In case shadow
> > +     stack is not enabled for the current thread, we still want to return
> > +     true.  */
> > +  unsigned int eax, ebx, ecx, edx;
> > +
> > +  __get_cpuid_count (7, 0, &eax, &ebx, &ecx, &edx);
> 
> It should be
> 
>   if (! __get_cpuid_count (7, 0, &eax, &ebx, &ecx, &edx))
>     return false;
> 

I agree, we should check the return value. Thanks for catching this.

> > +
> > +  if ((ecx & bit_SHSTK) == 0)
> > +    return false;
> > +
> 
> 
> H.J.

Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack.
  2025-08-06 20:52             ` Luis
@ 2025-08-11 11:52               ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-11 11:52 UTC (permalink / raw)
  To: 'Luis', Thiago Jung Bauermann; +Cc: Andrew Burgess, gdb-patches

Hi Luis and Thiago,

> -----Original Message-----
> From: Luis <luis.machado.foss@gmail.com>
> Sent: Wednesday, August 6, 2025 10:52 PM
> To: Thiago Jung Bauermann <thiago.bauermann@linaro.org>; Schimpe,
> Christina <christina.schimpe@intel.com>
> Cc: Andrew Burgess <aburgess@redhat.com>; gdb-
> patches@sourceware.org; luis.machado@arm.com
> Subject: Re: [PATCH v5 07/12] gdb: amd64 linux coredump support with
> shadow stack.
> 
> On 8/5/25 05:29, Thiago Jung Bauermann wrote:
> > Hello,
> >
> > "Schimpe, Christina" <christina.schimpe@intel.com> writes:
> >
> >>>>>> +
> >>>>>> +    # 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.
> >>>>>> +
> >>>>>> +    if {$gcore_generated} {
> >>>>>> +	clean_restart $binfile
> >>>>>> +
> >>>>>> +	with_test_prefix "gcore corefile" {
> >>>>>> +	    check_core_file $gcore_filename $ssp_in_gcore
> >>>>>> +	}
> >>>>>> +    } else {
> >>>>>> +	fail "gcore corefile not generated"
> >>>>>
> >>>>> It's better, where possible, to avoid having pass/fail results
> >>>>> that only show up down some code paths.
> >>>>>
> >>>>> In this case it's easy to avoid having a stray 'fail' by
> >>>>> restructuring the code too:
> >>>>>
> >>>>>    gdb_assert { $gcore_generated } "gcore corefile created"
> >>>>>    if { $gcore_generated } {
> >>>>>      ... etc ...
> >>>>>    }
> >>>>>
> >>>>> Now you'll always have either a pass or fail based on the gcore
> >>>>> being generated.
> >>>>
> >>>> Good idea. I did that for aarch64-gcs-core.exp.
> >>
> >> If no OS corefile is found we will see a FAIL here.
> >> The usual coredump testing doesn't fail in case the coredump file is not
> found.
> >> So all gdb.base/corefile*.exp tests have sth. like:
> >>
> >> set corefile [core_find $binfile {}]
> >> if {$corefile == ""} {
> >>      return
> >> }
> >>
> >> This can happen in case corefiles are managed, for instance, by apport on
> ubuntu.
> >> Do we want a different behaviour ?
> >
> > Interesting point. Perhaps a FAIL Isn't the best result to report in
> > this case, but IMHO it would be worth reporting an UNTESTED or perhaps
> > UNSUPPORTED result rather than silently returning.
> >
> > I don't have a strong opinion on this matter though.
> >
> 
> When testing core files, I usually change the core_pattern in
> /proc/sys/kernel/core_pattern so the files are properly generated and saved.
> Personally I find it useful to have a FAIL output when the core file generation
> didn't work.

Thank you for the feedback.

I also use /proc/sys/kernel/core_pattern to fix core file testing on my machine. However, by default
corefiles do not work in our testsuite for the recent OS I have been testing (ubuntu 24, fedora 42).

I created this RFC patch to clarify if we should use UNTESTED or FAIL:
https://sourceware.org/pipermail/gdb-patches/2025-August/219880.html

I know it's not the most important topic, but it would be nice if we could agree on a commonly accepted
solution.

Christina
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-07-08 15:18 ` [PATCH v5 00/12] Add CET shadow stack support Schimpe, Christina
@ 2025-08-14  7:52   ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-14  7:52 UTC (permalink / raw)
  To: gdb-patches, Andrew Burgess
  Cc: thiago.bauermann, luis.machado, Tom Tromey, Guinevere Larsen

Hi Andrew,

Thanks a lot for your detailed review of the full series!
I implemented most of your feedback.

I think the most important open is for the description of the gdbarch method, since ARM's GCS series also depends on that patch ("gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer.")

Discussion for this patch: https://sourceware.org/pipermail/gdb-patches/2025-August/219698.html

So I am kindly pinging for your feedback here. 😊

Best Regards,
Christina

> -----Original Message-----
> From: Schimpe, Christina <christina.schimpe@intel.com>
> Sent: Tuesday, July 8, 2025 5:18 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com; Tom Tromey
> <tom@tromey.com>
> Subject: RE: [PATCH v5 00/12] Add CET shadow stack support
> 
> I actually missed to mention Tom's feedback - my apologies for that.
> 
> Tom's review included:
> - Approval for patch #4 " gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc
> 14 branch."
> - Improvements for comments in patch #11 "gdb, gdbarch: Introduce
> gdbarch method to get the shadow stack pointer. ".
> 
> Kind Regards,
> Christina
> 
> > -----Original Message-----
> > From: Christina Schimpe <christina.schimpe@intel.com>
> > Sent: Saturday, June 28, 2025 10:28 AM
> > To: gdb-patches@sourceware.org
> > Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> > Subject: [PATCH v5 00/12] Add CET shadow stack support
> >
> > Hi all,
> >
> > this is my v5 of the series to add amd64 shadow stack support to GDB
> > on linux.
> > It addresses the feedback of Luis.
> >
> > v4 can be found here:
> > https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html
> >
> > Changes since v4:
> > - Improve some comments.
> > - Change the test in "gdb: amd64 linux coredump support with shadow
> >   stack." to also test core file generated by the linux kernel.  This
> >   requires changes for the core_find procedure to save program output,
> >   that have been implemented by Thiago already, so we include this part
> >   of the patch in this series: "gdb, testsuite: Extend core_find procedure
> >   to save program output.".  The test is now very similar to the test
> >   implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
> >   providing the input here!
> >
> > I am looking forward to your feedback!
> >
> > Regards,
> >
> > Christina
> >
> > Christina Schimpe (12):
> >   gdb, testsuite: Extend core_find procedure to save program output.
> >   gdbserver: Add optional runtime register set type.
> >   gdbserver: Add assert in x86_linux_read_description.
> >   gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
> >   gdb, gdbserver: Use xstate_bv for target description creation on x86.
> >   gdb, gdbserver: Add support of Intel shadow stack pointer register.
> >   gdb: amd64 linux coredump support with shadow stack.
> >   gdb: Handle shadow stack pointer register unwinding for amd64 linux.
> >   gdb, gdbarch: Enable inferior calls for shadow stack support.
> >   gdb: Implement amd64 linux shadow stack support for inferior calls.
> >   gdb, gdbarch: Introduce gdbarch method to get the shadow stack
> >     pointer.
> >   gdb: Enable displaced stepping with shadow stack on amd64 linux.
> >
> >  gdb/NEWS                                      |   6 +
> >  gdb/amd64-linux-nat.c                         |  17 ++
> >  gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
> >  gdb/amd64-tdep.c                              |  35 ++-
> >  gdb/amd64-tdep.h                              |   9 +-
> >  gdb/arch-utils.c                              |  10 +
> >  gdb/arch-utils.h                              |   5 +
> >  gdb/arch/amd64-linux-tdesc.c                  |  33 +--
> >  gdb/arch/amd64-linux-tdesc.h                  |   7 +-
> >  gdb/arch/amd64.c                              |  25 +-
> >  gdb/arch/amd64.h                              |  10 +-
> >  gdb/arch/i386-linux-tdesc.c                   |  29 +--
> >  gdb/arch/i386-linux-tdesc.h                   |   5 +-
> >  gdb/arch/i386.c                               |  19 +-
> >  gdb/arch/i386.h                               |   8 +-
> >  gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
> >  gdb/arch/x86-linux-tdesc-features.h           |  25 +-
> >  gdb/doc/gdb.texinfo                           |  42 ++++
> >  gdb/features/Makefile                         |   2 +
> >  gdb/features/i386/32bit-ssp.c                 |  14 ++
> >  gdb/features/i386/32bit-ssp.xml               |  11 +
> >  gdb/features/i386/64bit-ssp.c                 |  14 ++
> >  gdb/features/i386/64bit-ssp.xml               |  11 +
> >  gdb/gdbarch-gen.c                             |  54 +++++
> >  gdb/gdbarch-gen.h                             |  24 ++
> >  gdb/gdbarch_components.py                     |  31 +++
> >  gdb/i386-tdep.c                               |  51 +++-
> >  gdb/i386-tdep.h                               |  11 +-
> >  gdb/infcall.c                                 |  14 +-
> >  gdb/linux-tdep.c                              |  47 ++++
> >  gdb/linux-tdep.h                              |   7 +
> >  gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
> >  gdb/nat/x86-linux-tdesc.c                     |  20 +-
> >  gdb/nat/x86-linux-tdesc.h                     |   7 +-
> >  gdb/nat/x86-linux.c                           |  57 +++++
> >  gdb/nat/x86-linux.h                           |   4 +
> >  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
> >  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
> >  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
> > .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
> >  gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
> >  .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
> >  gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
> >  gdb/x86-linux-nat.c                           |  50 +++-
> >  gdb/x86-linux-nat.h                           |  11 +
> >  gdb/x86-tdep.c                                |  21 ++
> >  gdb/x86-tdep.h                                |   9 +
> >  gdbserver/i387-fp.cc                          |  40 ++--
> >  gdbserver/linux-amd64-ipa.cc                  |  10 +-
> >  gdbserver/linux-i386-ipa.cc                   |   6 +-
> >  gdbserver/linux-low.cc                        |  50 ++--
> >  gdbserver/linux-low.h                         |   7 +-
> >  gdbserver/linux-x86-low.cc                    |  44 +++-
> >  gdbsupport/x86-xstate.h                       |   7 +-
> >  55 files changed, 1687 insertions(+), 217 deletions(-)  create mode
> > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
> > gdb/features/i386/32bit- ssp.xml  create mode 100644
> > gdb/features/i386/64bit-ssp.c  create mode
> > 100644 gdb/features/i386/64bit-ssp.xml  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> >  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack-corefile.c
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-
> > corefile.exp
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-
> > step.exp
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> >
> > --
> > 2.43.0

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.
  2025-08-06 19:53         ` Schimpe, Christina
  2025-08-06 19:54           ` Schimpe, Christina
@ 2025-08-14 11:39           ` Andrew Burgess
  1 sibling, 0 replies; 67+ messages in thread
From: Andrew Burgess @ 2025-08-14 11:39 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches, thiago.bauermann; +Cc: luis.machado

"Schimpe, Christina" <christina.schimpe@intel.com> writes:

> Hi Andrew,
>
>> -----Original Message-----
>> From: Andrew Burgess <aburgess@redhat.com>
>> Sent: Tuesday, August 5, 2025 3:57 PM
>> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
>> patches@sourceware.org
>> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
>> Subject: RE: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow
>> stack pointer register.
>> 
>> "Schimpe, Christina" <christina.schimpe@intel.com> writes:
>> 
>> > Hi Andrew,
>> >
>> > Thanks a lot for the review! I have some questions for your feedback,
>> > please find my comments below.
>> >
>> >> -----Original Message-----
>> >> From: Andrew Burgess <aburgess@redhat.com>
>> >> Sent: Friday, July 25, 2025 2:50 PM
>> >> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
>> >> patches@sourceware.org
>> >> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
>> >> Subject: Re: [PATCH v5 06/12] gdb, gdbserver: Add support of Intel
>> >> shadow stack pointer register.
>> >>
>> >> Christina Schimpe <christina.schimpe@intel.com> writes:
>> >>
>> >> > This patch adds the user mode register PL3_SSP which is part of the
>> >> > Intel(R) Control-Flow Enforcement Technology (CET) feature for
>> >> > support of shadow stack.
>> >> > For now, only native and remote debugging support for shadow stack
>> >> > userspace on amd64 linux are covered by this patch including 64 bit
>> >> > and
>> >> > x32 support.  32 bit support is not covered due to missing Linux
>> >> > kernel support.
>> >> >
>> >> > This patch requires fixing the test
>> >> > gdb.base/inline-frame-cycle-unwind
>> >> > which is failing in case the shadow stack pointer is unavailable.
>> >> > Such a state is possible if shadow stack is disabled for the
>> >> > current thread but supported by HW.
>> >> >
>> >> > This test uses the Python unwinder inline-frame-cycle-unwind.py
>> >> > which fakes the cyclic stack cycle by reading the pending frame's
>> >> > registers and adding them to the unwinder:
>> >> >
>> >> > ~~~
>> >> > for reg in pending_frame.architecture().registers("general"):
>> >> >      val = pending_frame.read_register(reg)
>> >> >      unwinder.add_saved_register(reg, val)
>> >> >      return unwinder
>> >> > ~~~
>> >> >
>> >> > However, in case the python unwinder is used we add a register
>> >> > (pl3_ssp) that is unavailable.  This leads to a NOT_AVAILABLE_ERROR
>> >> > caught in gdb/frame-unwind.c:frame_unwind_try_unwinder and it is
>> >> > continued with standard unwinders.  This destroys the faked cyclic
>> >> > behavior and the stack is further unwinded after frame 5.
>> >> >
>> >> > In the working scenario an error should be triggered:
>> >> > ~~~
>> >> > bt
>> >> > 0  inline_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:49^M
>> >> > 1  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
>> >> > 2  0x000055555555516e in inline_func () at
>> >> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
>> >> > 3  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
>> >> > 4  0x000055555555516e in inline_func () at
>> >> > /tmp/gdb.base/inline-frame-cycle-unwind.c:45^M
>> >> > 5  normal_func () at /tmp/gdb.base/inline-frame-cycle-unwind.c:32^M
>> >> > Backtrace stopped: previous frame identical to this frame (corrupt
>> >> > stack?)
>> >> > (gdb) PASS: gdb.base/inline-frame-cycle-unwind.exp: cycle at level 5:
>> >> > backtrace when the unwind is broken at frame 5 ~~~
>> >> >
>> >> > To fix the Python unwinder, we simply skip the unavailable registers.
>> >> >
>> >> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>> >> > Reviewed-By: Eli Zaretskii <eliz@gnu.org>
>> >> > Reviewed-By: Luis Machado <luis.machado@arm.com>
>> >> > ---
>> >> >  gdb/NEWS                                      |  3 +
>> >> >  gdb/amd64-linux-nat.c                         | 17 +++++
>> >> >  gdb/amd64-linux-tdep.c                        |  1 +
>> >> >  gdb/amd64-tdep.c                              |  6 +-
>> >> >  gdb/amd64-tdep.h                              |  1 +
>> >> >  gdb/arch/amd64.c                              | 10 +++
>> >> >  gdb/arch/i386.c                               |  4 ++
>> >> >  gdb/arch/x86-linux-tdesc-features.c           |  1 +
>> >> >  gdb/doc/gdb.texinfo                           |  4 ++
>> >> >  gdb/features/Makefile                         |  2 +
>> >> >  gdb/features/i386/32bit-ssp.c                 | 14 ++++
>> >> >  gdb/features/i386/32bit-ssp.xml               | 11 +++
>> >> >  gdb/features/i386/64bit-ssp.c                 | 14 ++++
>> >> >  gdb/features/i386/64bit-ssp.xml               | 11 +++
>> >> >  gdb/i386-tdep.c                               | 22 +++++-
>> >> >  gdb/i386-tdep.h                               |  4 ++
>> >> >  gdb/nat/x86-linux-tdesc.c                     |  2 +
>> >> >  gdb/nat/x86-linux.c                           | 57 +++++++++++++++
>> >> >  gdb/nat/x86-linux.h                           |  4 ++
>> >> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   | 22 ++++++
>> >> >  gdb/testsuite/gdb.arch/amd64-ssp.exp          | 50 +++++++++++++
>> >> >  .../gdb.base/inline-frame-cycle-unwind.py     |  4 ++
>> >> >  gdb/testsuite/lib/gdb.exp                     | 70 +++++++++++++++++++
>> >> >  gdb/x86-linux-nat.c                           | 49 +++++++++++--
>> >> >  gdb/x86-linux-nat.h                           | 11 +++
>> >> >  gdb/x86-tdep.c                                | 21 ++++++
>> >> >  gdb/x86-tdep.h                                |  9 +++
>> >> >  gdbserver/linux-x86-low.cc                    | 28 +++++++-
>> >> >  gdbsupport/x86-xstate.h                       |  5 +-
>> >> >  29 files changed, 447 insertions(+), 10 deletions(-)  create mode
>> >> > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
>> >> > gdb/features/i386/32bit-ssp.xml  create mode 100644
>> >> > gdb/features/i386/64bit-ssp.c  create mode 100644
>> >> > gdb/features/i386/64bit-ssp.xml  create mode 100644
>> >> > gdb/testsuite/gdb.arch/amd64-shadow-stack.c
>> >> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
>> >> >
>> >>
>> >>
>> >> > diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c index
>> >> > dbb9b3223cb..4df99ccca54 100644
>> >> > --- a/gdb/amd64-linux-nat.c
>> >> > +++ b/gdb/amd64-linux-nat.c
>> >> > @@ -32,6 +32,7 @@
>> >> >  #include "amd64-tdep.h"
>> >> >  #include "amd64-linux-tdep.h"
>> >> >  #include "i386-linux-tdep.h"
>> >> > +#include "x86-tdep.h"
>> >> >  #include "gdbsupport/x86-xstate.h"
>> >> >
>> >> >  #include "x86-linux-nat.h"
>> >> > @@ -237,6 +238,14 @@ amd64_linux_nat_target::fetch_registers
>> >> > (struct regcache *regcache, int regnum)
>> >> >
>> >> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
>> >> >  	{
>> >> > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
>> >> > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
>> >>
>> >> It's really nit-picking, but I don't think the '>' here check is
>> >> correct.  You're checking that tdep->ssp_regnum has been assigned a
>> >> value, right?  And it's default value is -1, so, shouldn't the check really be
>> '>= 0' or '!= -1' maybe?
>> >>
>> >> The same pattern is repeated below in ::store_registers.
>> >>
>> >> Ahh, reading further on, I see that (not just you), but all the
>> >> *_regnum fields in i386_gdbarch_tdep start set to 0 (which is a valid
>> >> register number), but are set to -1 if the feature is not found.
>> >> Maybe I'm missing something incredibly clever about this ... but I
>> >> suspect this code is just showing its age a little.  I still think the validity
>> check should be against -1.
>> >
>> > Yes I agree, it seems to me that there is something inconsistent in GDB.
>> >
>> > The current default value is 0, we set all regnums to 0 initially in
>> > gdb/i386-tdep.h And we set it to -1 to indicate the absence of that specific
>> register.
>> >
>> > But on the other hand the enums (enum amd64_regnum/enum
>> i386_regnum)
>> > defining the register numbers start at 0.
>> >
>> > So that seems to be a separate issue one could consider to fix.
>> > Maybe the default should be simply -1, instead of 0 ?
>> >
>> > But for my specific patch here the best would be to compare against
>> against -1, I agree and will fix.
>> >
>> >> > +	    {
>> >> > +	      x86_linux_fetch_ssp (regcache, tid);
>> >> > +	      if (regnum != -1)
>> >> > +		return;
>> >> > +	    }
>> >> > +
>> >> >  	  /* Pre-4.14 kernels have a bug (fixed by commit 0852b374173b
>> >> >  	     "x86/fpu: Add FPU state copying quirk to handle XRSTOR failure
>> on
>> >> >  	     Intel Skylake CPUs") that sometimes causes the mxcsr
>> >> > location in @@ -302,6 +311,14 @@
>> >> > amd64_linux_nat_target::store_registers (struct
>> >> regcache *regcache, int regnum)
>> >> >        if (have_ptrace_getregset == TRIBOOL_TRUE)
>> >> >  	{
>> >> >  	  gdb::byte_vector xstateregs (tdep->xsave_layout.sizeof_xsave);
>> >> > +	  if ((regnum == -1 && tdep->ssp_regnum > 0)
>> >> > +	      || (regnum != -1 && regnum == tdep->ssp_regnum))
>> >> > +	    {
>> >> > +	      x86_linux_store_ssp (regcache, tid);
>> >> > +	      if (regnum != -1)
>> >> > +		return;
>> >> > +	    }
>> >> > +
>> >> >  	  struct iovec iov;
>> >> >
>> >> >  	  iov.iov_base = xstateregs.data ();
>> >>
>> >>
>> >> > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index
>> >> > 4ef640698bd..0881ac4aee5 100644
>> >> > --- a/gdb/doc/gdb.texinfo
>> >> > +++ b/gdb/doc/gdb.texinfo
>> >> > @@ -49959,6 +49959,10 @@ The @samp{org.gnu.gdb.i386.pkeys}
>> feature
>> >> is
>> >> > optional.  It should  describe a single register, @samp{pkru}.  It
>> >> > is a 32-bit register  valid for i386 and amd64.
>> >> >
>> >> > +The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It
>> >> > +should describe the user mode register @samp{pl3_ssp} which has 64
>> >> > +bits on amd64.  Following the restriction of the Linux kernel,
>> >> > +only amd64 is
>> >> supported for now.
>> >>
>> >> You mention a couple of times in this patch that the shadow stack is
>> >> only supported (for now) on amd64 (& x32), but you have added an i386
>> >> version of the org.gnu.gdb.i386.pl3_ssp feature with a 32-bit pl3_ssp
>> register.
>> >
>> > We need the 32-bit pl3_ssp register for x32, since for x32 the shadow
>> stack pointer has 32 bits only.
>> >
>> >> This feature is (as far as I can tell) instantiated for 32-bit
>> >> targets, which means gdbserver will send out target descriptions
>> containing this feature.
>> >>
>> >> I think either (a) you should drop the i386 xml file and feature, or
>> >> (b) my preferred choice, document the i386 version here.  It's still
>> >> fine to say that the 32-bit (i386) register is ignored by GDB for now
>> >> though.  I had a go at rewording the paragraph, but I'm not 100% sure its
>> OK yet:
>> >>
>> >>   The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
>> >>   describe the user mode register @samp{pl3_ssp} which has 64 bits on
>> >>   amd64 and 32 bits on i386.  Following the restriction of the Linux
>> >>   kernel, only GDB for amd64 targets makes use of this feature for now.
>> >
>> > I missed to describe x32 here, actually. So I'd write it as follows:
>> >
>> > The @samp{org.gnu.gdb.i386.pl3_ssp} feature is optional.  It should
>> > describe the user mode register @samp{pl3_ssp} which has 64 bits on
>> > amd64, 32 bits on amd64 with  32-bit pointer size (X32) and 32 bits on
>> i386.
>> > Following the restriction of the Linux kernel, only GDB for amd64
>> > targets makes use of this feature for now.
>> >
>> > What do you think?
>> 
>> Sounds great.
>> 
>> >
>> >>
>> >> > +
>> >> >  @node LoongArch Features
>> >> >  @subsection LoongArch Features
>> >> >  @cindex target descriptions, LoongArch Features diff --git
>> >> > a/gdb/features/Makefile b/gdb/features/Makefile index
>> >> > 7a8c7999733..2afda1ccd00 100644
>> >> > --- a/gdb/features/Makefile
>> >> > +++ b/gdb/features/Makefile
>> >> > @@ -225,6 +225,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
>> >> >  	i386/32bit-avx.xml \
>> >> >  	i386/32bit-avx512.xml \
>> >> >  	i386/32bit-segments.xml \
>> >> > +	i386/32bit-ssp.xml \
>> >> >  	i386/64bit-avx512.xml \
>> >> >  	i386/64bit-core.xml \
>> >> >  	i386/64bit-segments.xml \
>> >> > @@ -232,6 +233,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
>> >> >  	i386/64bit-linux.xml \
>> >> >  	i386/64bit-sse.xml \
>> >> >  	i386/pkeys.xml \
>> >> > +	i386/64bit-ssp.xml \
>> >> >  	i386/x32-core.xml \
>> >> >  	loongarch/base32.xml \
>> >> >  	loongarch/base64.xml \
>> >> > diff --git a/gdb/features/i386/32bit-ssp.c
>> >> > b/gdb/features/i386/32bit-ssp.c new file mode 100644 index
>> >> > 00000000000..991bae3c1e6
>> >> > --- /dev/null
>> >> > +++ b/gdb/features/i386/32bit-ssp.c
>> >> > @@ -0,0 +1,14 @@
>> >> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
>> >> > +  Original: 32bit-ssp.xml */
>> >> > +
>> >> > +#include "gdbsupport/tdesc.h"
>> >> > +
>> >> > +static int
>> >> > +create_feature_i386_32bit_ssp (struct target_desc *result, long
>> >> > +regnum) {
>> >> > +  struct tdesc_feature *feature;
>> >> > +
>> >> > +  feature = tdesc_create_feature (result,
>> >> > +"org.gnu.gdb.i386.pl3_ssp");
>> >> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 32,
>> >> > +"data_ptr");
>> >> > +  return regnum;
>> >> > +}
>> >> > diff --git a/gdb/features/i386/32bit-ssp.xml
>> >> > b/gdb/features/i386/32bit-ssp.xml new file mode 100644 index
>> >> > 00000000000..d17e7004eec
>> >> > --- /dev/null
>> >> > +++ b/gdb/features/i386/32bit-ssp.xml
>> >> > @@ -0,0 +1,11 @@
>> >> > +<?xml version="1.0"?>
>> >> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
>> >> > +
>> >> > +     Copying and distribution of this file, with or without modification,
>> >> > +     are permitted in any medium without royalty provided the
>> copyright
>> >> > +     notice and this notice are preserved.  -->
>> >> > +
>> >> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
>> >> > +name="org.gnu.gdb.i386.pl3_ssp">
>> >> > +  <reg name="pl3_ssp" bitsize="32" type="data_ptr"/> </feature>
>> >> > diff --git a/gdb/features/i386/64bit-ssp.c
>> >> > b/gdb/features/i386/64bit-ssp.c new file mode 100644 index
>> >> > 00000000000..5468099ddf6
>> >> > --- /dev/null
>> >> > +++ b/gdb/features/i386/64bit-ssp.c
>> >> > @@ -0,0 +1,14 @@
>> >> > +/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
>> >> > +  Original: 64bit-ssp.xml */
>> >> > +
>> >> > +#include "gdbsupport/tdesc.h"
>> >> > +
>> >> > +static int
>> >> > +create_feature_i386_64bit_ssp (struct target_desc *result, long
>> >> > +regnum) {
>> >> > +  struct tdesc_feature *feature;
>> >> > +
>> >> > +  feature = tdesc_create_feature (result,
>> >> > +"org.gnu.gdb.i386.pl3_ssp");
>> >> > +  tdesc_create_reg (feature, "pl3_ssp", regnum++, 1, NULL, 64,
>> >> > +"data_ptr");
>> >> > +  return regnum;
>> >> > +}
>> >> > diff --git a/gdb/features/i386/64bit-ssp.xml
>> >> > b/gdb/features/i386/64bit-ssp.xml new file mode 100644 index
>> >> > 00000000000..a0688d018a5
>> >> > --- /dev/null
>> >> > +++ b/gdb/features/i386/64bit-ssp.xml
>> >> > @@ -0,0 +1,11 @@
>> >> > +<?xml version="1.0"?>
>> >> > +<!-- Copyright (C) 2022-2024 Free Software Foundation, Inc.
>> >> > +
>> >> > +     Copying and distribution of this file, with or without modification,
>> >> > +     are permitted in any medium without royalty provided the
>> copyright
>> >> > +     notice and this notice are preserved.  -->
>> >> > +
>> >> > +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> <feature
>> >> > +name="org.gnu.gdb.i386.pl3_ssp">
>> >> > +  <reg name="pl3_ssp" bitsize="64" type="data_ptr"/> </feature>
>> >> > diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index
>> >> > 90ff0c5c706..8eb5b4fac86 100644
>> >> > --- a/gdb/i386-tdep.c
>> >> > +++ b/gdb/i386-tdep.c
>> >> > @@ -8403,7 +8403,8 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
>> *tdep,
>> >> >    const struct tdesc_feature *feature_core;
>> >> >
>> >> >    const struct tdesc_feature *feature_sse, *feature_avx,
>> *feature_avx512,
>> >> > -			     *feature_pkeys, *feature_segments;
>> >> > +			     *feature_pkeys, *feature_segments,
>> >> > +			     *feature_pl3_ssp;
>> >> >    int i, num_regs, valid_p;
>> >> >
>> >> >    if (! tdesc_has_registers (tdesc)) @@ -8429,6 +8430,9 @@
>> >> > i386_validate_tdesc_p (i386_gdbarch_tdep *tdep,
>> >> >    /* Try PKEYS  */
>> >> >    feature_pkeys = tdesc_find_feature (tdesc,
>> >> > "org.gnu.gdb.i386.pkeys");
>> >> >
>> >> > +  /* Try Shadow Stack.  */
>> >> > +  feature_pl3_ssp = tdesc_find_feature (tdesc,
>> >> > + "org.gnu.gdb.i386.pl3_ssp");
>> >> > +
>> >> >    valid_p = 1;
>> >> >
>> >> >    /* The XCR0 bits.  */
>> >> > @@ -8544,6 +8548,15 @@ i386_validate_tdesc_p (i386_gdbarch_tdep
>> >> *tdep,
>> >> >  					    tdep->pkeys_register_names[i]);
>> >> >      }
>> >> >
>> >> > +  if (feature_pl3_ssp != nullptr)
>> >> > +    {
>> >> > +      if (tdep->ssp_regnum < 0)
>> >> > +	tdep->ssp_regnum = I386_PL3_SSP_REGNUM;
>> >> > +
>> >> > +      valid_p &= tdesc_numbered_register (feature_pl3_ssp, tdesc_data,
>> >> > +					  tdep->ssp_regnum, "pl3_ssp");
>> >> > +    }
>> >> > +
>> >> >    return valid_p;
>> >> >  }
>> >> >
>> >> > @@ -8835,6 +8848,9 @@ i386_gdbarch_init (struct gdbarch_info info,
>> >> struct gdbarch_list *arches)
>> >> >    /* No segment base registers.  */
>> >> >    tdep->fsbase_regnum = -1;
>> >> >
>> >> > +  /* No shadow stack pointer register.  */  tdep->ssp_regnum = -1;
>> >> > +
>> >> >    tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
>> >> >
>> >> >    set_gdbarch_relocate_instruction (gdbarch,
>> >> > i386_relocate_instruction); @@ -8955,13 +8971,15 @@ const struct
>> >> > target_desc *  i386_target_description (uint64_t xstate_bv_mask,
>> >> > bool
>> >> > segments)  {
>> >> >    static target_desc *i386_tdescs \
>> >> > -    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*segments*/] =
>> {};
>> >> > +    [2/*SSE*/][2/*AVX*/][2/*AVX512*/][2/*PKRU*/][2/*CET_U*/] \
>> >> > +    [2/*segments*/] = {};
>> >> >    target_desc **tdesc;
>> >> >
>> >> >    tdesc = &i386_tdescs[(xstate_bv_mask & X86_XSTATE_SSE) ? 1 : 0]
>> >> >      [(xstate_bv_mask & X86_XSTATE_AVX) ? 1 : 0]
>> >> >      [(xstate_bv_mask & X86_XSTATE_AVX512) ? 1 : 0]
>> >> >      [(xstate_bv_mask & X86_XSTATE_PKRU) ? 1 : 0]
>> >> > +    [(xstate_bv_mask & X86_XSTATE_CET_U) ? 1 : 0]
>> >> >      [segments ? 1 : 0];
>> >> >
>> >> >    if (*tdesc == NULL)
>> >> > diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index
>> >> > 239bc8674e8..60d6f3eb732 100644
>> >> > --- a/gdb/i386-tdep.h
>> >> > +++ b/gdb/i386-tdep.h
>> >> > @@ -191,6 +191,9 @@ struct i386_gdbarch_tdep : gdbarch_tdep_base
>> >> >    /* PKEYS register names.  */
>> >> >    const char * const *pkeys_register_names = nullptr;
>> >> >
>> >> > +  /* Shadow stack pointer register.  */  int ssp_regnum = 0;
>> >>
>> >> I know all the other *_regnum fields start as 0, but I really don't
>> >> understand why.  Surely starting as -1 would make more sense?  This
>> >> isn't a hard requirement, but maybe you see some reason why these
>> >> fields should start at 0 that I'm missing?
>> >
>> > I agree. As stated before setting the regnums to -1 here by default
>> > would make more sense to me. So I think I should set ssp_regnum here to
>> -1.
>> > I'd do that, unless I see some issues with that when testing of course.
>> >
>> > The other registers set to 0 here could be fixed separately.
>> >
>> > Does that sound reasonable?
>> 
>> Yes absolutely, I certainly don't want you to change unrelated code as part
>> of this work, but we might as well improve new code as its added.
>> Your proposal sounds perfect to me.
>
> The patch was pretty straight-forward. 😊
> Thanks for providing the feedback here, I think this makes the code much better.
>
>> >
>> >> > +
>> >> >    /* Register number for %fsbase.  Set this to -1 to indicate the
>> >> >       absence of segment base registers.  */
>> >> >    int fsbase_regnum = 0;
>> >> > @@ -293,6 +296,7 @@ enum i386_regnum
>> >> >    I386_ZMM0H_REGNUM,		/* %zmm0h */
>> >> >    I386_ZMM7H_REGNUM = I386_ZMM0H_REGNUM + 7,
>> >> >    I386_PKRU_REGNUM,
>> >> > +  I386_PL3_SSP_REGNUM,
>> >> >    I386_FSBASE_REGNUM,
>> >> >    I386_GSBASE_REGNUM
>> >> >  };
>> >>
>> >>
>> >> > diff --git a/gdb/testsuite/gdb.arch/amd64-ssp.exp
>> >> > b/gdb/testsuite/gdb.arch/amd64-ssp.exp
>> >> > new file mode 100644
>> >> > index 00000000000..6ddc875b9a3
>> >> > --- /dev/null
>> >> > +++ b/gdb/testsuite/gdb.arch/amd64-ssp.exp
>> >> > @@ -0,0 +1,50 @@
>> >> > +# Copyright 2018-2024 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 amd64-shadow-stack.c
>> >>
>> >> Test source files should be named to match the .exp file.  But in
>> >> this case 'amd64-shadow-stack' seems better than 'amd64-ssp', so I'd
>> >> renamed the .exp file to amd64-shadow-stack.exp, then this line can be
>> just:
>> >>
>> >>   standard_testfile
>> >
>> > Yes, I agree and will fix.
>> >
>> >> > +
>> >> > +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 -1
>> >> > +    }
>> >> > +
>> >> > +    if {![runto_main]} {
>> >> > +	return -1
>> >> > +    }
>> >>
>> >> The 'return -1' can become just 'return'.  The return value doesn't
>> >> mean anything.
>> >
>> > True will fix (also in following patches, that have the same for the test
>> files).
>> >
>> >> > +
>> >> > +    # Read PL3_SSP register.
>> >> > +    set ssp_main [get_hexadecimal_valueof "\$pl3_ssp" "read
>> >> > + pl3_ssp value"]
>> >> > +
>> >> > +    # Write PL3_SSP register.
>> >> > +    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"
>> >> > +
>> >> > +    # Restore original value.
>> >> > +    gdb_test "print /x \$pl3_ssp = $ssp_main" "= $ssp_main"
>> >> > + "restore
>> >> original pl3_ssp"
>> >> > +
>> >> > +    # Potential CET violations often only occur after resuming
>> >> > + normal
>> >> execution.
>> >> > +    # Therefore, it is important to test normal program continuation
>> after
>> >> > +    # configuring the shadow stack pointer.
>> >> > +    gdb_continue_to_end
>> >>
>> >> I assume that if we continue with the bogus value in place the
>> >> inferior would either give an error or terminate.  Is it worth trying
>> >> this and checking that the inferior behaves as expected?
>> >
>> > If we don't reset the shadow stack pointer to it's original value we will see
>> a SEGV.
>> > Dependent on the address of the wrong shadow stack pointer it's either
>> > a SEGV with si code that points to a control flow protection fault or a
>> different si code.
>> >
>> > So if I stay in a valid address range for configuring pl3_ssp but
>> > don't restore the original value I'll see a control flow protection exception:
>> >
>> > [...]
>> > breakpoint 1, 0x0000555555555148 in main ()^M
>> > (gdb) print /x $pl3_ssp^M
>> > $1 = 0x7ffff7bfffe8^M
>> > (gdb) PASS: gdb.arch/amd64-ssp.exp: get hexadecimal valueof "$pl3_ssp"
>> > print /x $pl3_ssp = 0x7ffff7bfffe0^M
>> > $2 = 0x7ffff7bfffe0^M
>> > (gdb) PASS: gdb.arch/amd64-ssp.exp: set pl3_ssp value print /x
>> > $pl3_ssp^M
>> > $3 = 0x7ffff7bfffe0^M
>> > (gdb) PASS: gdb.arch/amd64-ssp.exp: read pl3_ssp value after setting
>> > continue^M Continuing.^M ^M Program received signal SIGSEGV,
>> > Segmentation fault.^M
>> > 0x0000555555555158 in main ()^M
>> > (gdb) FAIL: gdb.arch/amd64-ssp.exp: continue until exit
>> >
>> > Siginfo shows si_code = 10, which indicates a control protection fault.
>> >
>> > p $_siginfo^M
>> > $4 = {si_signo = 11, si_errno = 0, si_code = 10, [...]
>> >
>> > If I set the value of pl3_ssp as in the current test (0x12345678) I'll
>> > see a different SEGV actually
>> >
>> > p $_siginfo
>> > $4 = {si_signo = 11, si_errno = 0, si_code = 1, [...]
>> >
>> >>
>> >> What if, say, the $pl3_ssp value only ever made it as far as the
>> >> register cache, and was never actually written back to the inferior?
>> >> I don't think the above test would actually spot this bug, right?
>> >
>> > Hm, if I understand you correctly here and you mean the scenario as
>> > shown above the above test would spot this bug I think (as we saw a fail).
>> >
>> > Does my example above show what you described or do you mean a
>> > different scenario?
>> 
>> Yes, something like the above would check that the register is actually being
>> written back to the hardware, and is written to the expected location.
>> 
>> The current test, as written in the patch, writes a bad value to the shadow
>> stack, then restores the correct value.  What if the bad value never actually
>> got written back to the hardware at all, and was just being held in the
>> register cache?
>> 
>> Having a test that writes a bad value, then does 'continue', and expects to
>> see something like 'Program received signal ...' would be a reasonable
>> indication that the write to the shadow stack actually made it to the h/w.
>> 
>> Thanks,
>> Andrew
>
>
> Yes, I agree, I'll add:
>
> ~~~
>     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 -1
>     }
>
>     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
>     }

Sorry for the late reply.  This test looks great, exactly what I
imagined.

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer.
  2025-08-04 13:01     ` Schimpe, Christina
@ 2025-08-14 15:50       ` Andrew Burgess
  2025-08-19 15:37         ` Schimpe, Christina
  0 siblings, 1 reply; 67+ messages in thread
From: Andrew Burgess @ 2025-08-14 15:50 UTC (permalink / raw)
  To: Schimpe, Christina, gdb-patches; +Cc: thiago.bauermann, luis.machado

"Schimpe, Christina" <christina.schimpe@intel.com> writes:

> Hi Andrew,
>
> Thanks for the feedback. Please find my comments to your feedback below.
>
>> -----Original Message-----
>> From: Andrew Burgess <aburgess@redhat.com>
>> Sent: Wednesday, July 30, 2025 2:22 PM
>> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
>> patches@sourceware.org
>> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
>> Subject: Re: [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to
>> get the shadow stack pointer.
>> 
>> Christina Schimpe <christina.schimpe@intel.com> writes:
>> 
>> > This patch is required by the following commit
>> > "gdb: Enable displaced stepping with shadow stack on amd64 linux."
>> >
>> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>> > Reviewed-By: Luis Machado <luis.machado@arm.com>
>> > ---
>> >  gdb/arch-utils.c          | 10 ++++++++++
>> >  gdb/arch-utils.h          |  5 +++++
>> >  gdb/gdbarch-gen.c         | 22 ++++++++++++++++++++++
>> >  gdb/gdbarch-gen.h         | 12 +++++++++++-
>> >  gdb/gdbarch_components.py | 17 ++++++++++++++++-
>> >  5 files changed, 64 insertions(+), 2 deletions(-)
>> >
>> > diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index
>> > f320d3d7365..c396e9e3840 100644
>> > --- a/gdb/arch-utils.c
>> > +++ b/gdb/arch-utils.c
>> > @@ -1218,6 +1218,16 @@ default_gdbarch_return_value
>> >  				readbuf, writebuf);
>> >  }
>> >
>> > +/* See arch-utils.h.  */
>> > +
>> > +std::optional<CORE_ADDR>
>> > +default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache
>> *regcache,
>> > +				  bool &shadow_stack_enabled)
>> > +{
>> > +  shadow_stack_enabled = false;
>> > +  return {};
>> > +}
>> > +
>> >  obstack *gdbarch_obstack (gdbarch *arch)  {
>> >    return &arch->obstack;
>> > diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index
>> > 1509cb7441e..14a84b74733 100644
>> > --- a/gdb/arch-utils.h
>> > +++ b/gdb/arch-utils.h
>> > @@ -414,4 +414,9 @@ extern enum return_value_convention
>> default_gdbarch_return_value
>> >        struct regcache *regcache, struct value **read_value,
>> >        const gdb_byte *writebuf);
>> >
>> > +/* Default implementation of gdbarch default_get_shadow_stack_pointer
>> > +   method.  */
>> > +extern std::optional<CORE_ADDR> default_get_shadow_stack_pointer
>> > +  (gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
>> > +
>> >  #endif /* GDB_ARCH_UTILS_H */
>> > diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index
>> > a4b72793fd8..caeda3cefae 100644
>> > --- a/gdb/gdbarch-gen.c
>> > +++ b/gdb/gdbarch-gen.c
>> > @@ -263,6 +263,7 @@ struct gdbarch
>> >    gdbarch_use_target_description_from_corefile_notes_ftype
>> *use_target_description_from_corefile_notes =
>> default_use_target_description_from_corefile_notes;
>> >    gdbarch_core_parse_exec_context_ftype *core_parse_exec_context =
>> default_core_parse_exec_context;
>> >    gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
>> > +  gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer =
>> > + default_get_shadow_stack_pointer;
>> >  };
>> >
>> >  /* Create a new ``struct gdbarch'' based on information provided by
>> > @@ -537,6 +538,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
>> >    /* Skip verify of use_target_description_from_corefile_notes, invalid_p
>> == 0.  */
>> >    /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
>> >    /* Skip verify of shadow_stack_push, has predicate.  */
>> > +  /* Skip verify of get_shadow_stack_pointer, invalid_p == 0.  */
>> >    if (!log.empty ())
>> >      internal_error (_("verify_gdbarch: the following are invalid ...%s"),
>> >  		    log.c_str ());
>> > @@ -1414,6 +1416,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct
>> ui_file *file)
>> >    gdb_printf (file,
>> >  	      "gdbarch_dump: shadow_stack_push = <%s>\n",
>> >  	      host_address_to_string (gdbarch->shadow_stack_push));
>> > +  gdb_printf (file,
>> > +	      "gdbarch_dump: get_shadow_stack_pointer = <%s>\n",
>> > +	      host_address_to_string (gdbarch->get_shadow_stack_pointer));
>> >    if (gdbarch->dump_tdep != NULL)
>> >      gdbarch->dump_tdep (gdbarch, file);  } @@ -5583,3 +5588,20 @@
>> > set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,  {
>> >    gdbarch->shadow_stack_push = shadow_stack_push;  }
>> > +
>> > +std::optional<CORE_ADDR>
>> > +gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch, regcache
>> > +*regcache, bool &shadow_stack_enabled) {
>> > +  gdb_assert (gdbarch != NULL);
>> > +  gdb_assert (gdbarch->get_shadow_stack_pointer != NULL);
>> > +  if (gdbarch_debug >= 2)
>> > +    gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer
>> > +called\n");
>> > +  return gdbarch->get_shadow_stack_pointer (gdbarch, regcache,
>> > +shadow_stack_enabled); }
>> > +
>> > +void
>> > +set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
>> > +
>> gdbarch_get_shadow_stack_pointer_ftype
>> > +get_shadow_stack_pointer) {
>> > +  gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer; }
>> > diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index
>> > 71142332540..c36171b089e 100644
>> > --- a/gdb/gdbarch-gen.h
>> > +++ b/gdb/gdbarch-gen.h
>> > @@ -1807,7 +1807,8 @@ extern void
>> set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc
>> >     technologies.  For example, the Intel Control-Flow Enforcement
>> Technology
>> >     (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
>> >     To enable shadow stack support for inferior calls the
>> shadow_stack_push
>> > -   gdbarch hook has to be provided.
>> > +   gdbarch hook has to be provided.  The get_shadow_stack_pointer
>> gdbarch
>> > +   hook has to be provided to enable displaced stepping.
>> >
>> >     Push NEW_ADDR to the shadow stack and update the shadow stack
>> > pointer. */
>> >
>> > @@ -1816,3 +1817,12 @@ extern bool gdbarch_shadow_stack_push_p
>> (struct
>> > gdbarch *gdbarch);  typedef void (gdbarch_shadow_stack_push_ftype)
>> > (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
>> > extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
>> > CORE_ADDR new_addr, regcache *regcache);  extern void
>> > set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
>> > gdbarch_shadow_stack_push_ftype *shadow_stack_push);
>> > +
>> > +/* If possible, return the shadow stack pointer.  On some architectures,
>> the
>> > +   shadow stack pointer is available even if the feature is disabled.  To
>> > +   return the feature's enablement state configure
>> SHADOW_STACK_ENABLED.
>> > +   Set it to true in case the shadow stack is enabled. */
>> > +
>> > +typedef std::optional<CORE_ADDR>
>> > +(gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch,
>> > +regcache *regcache, bool &shadow_stack_enabled); extern
>> > +std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct
>> > +gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
>> > +extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch
>> > +*gdbarch, gdbarch_get_shadow_stack_pointer_ftype
>> > +*get_shadow_stack_pointer);
>> > diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
>> > index abc79588473..73459064170 100644
>> > --- a/gdb/gdbarch_components.py
>> > +++ b/gdb/gdbarch_components.py
>> > @@ -2855,7 +2855,8 @@ Some targets support special hardware-
>> assisted
>> > control-flow protection  technologies.  For example, the Intel
>> > Control-Flow Enforcement Technology  (Intel CET) on x86 provides a
>> shadow stack and indirect branch tracking.
>> >  To enable shadow stack support for inferior calls the
>> > shadow_stack_push -gdbarch hook has to be provided.
>> > +gdbarch hook has to be provided.  The get_shadow_stack_pointer
>> > +gdbarch hook has to be provided to enable displaced stepping.
>> 
>> I find the addition of this last sentence here a little strange.  While it's a true
>> statement, wouldn't this be better placed on the comment for
>> get_shadow_stack_pointer?
>
> Mh I see the initial comment section here rather as general overview for the
> shadow stack feature in GDB. It explains which features (e.g. infcalls and displaced
> stepping) are interacting with shadow stacks.  So in my opinion it's okay to keep it as is.
>
>> >
>> >  Push NEW_ADDR to the shadow stack and update the shadow stack
>> pointer.
>> >  """,
>> > @@ -2864,3 +2865,17 @@ Push NEW_ADDR to the shadow stack and
>> update the shadow stack pointer.
>> >      params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
>> >      predicate=True,
>> >  )
>> > +
>> > +Method(
>> > +    comment="""
>> > +If possible, return the shadow stack pointer.  On some architectures,
>> > +the shadow stack pointer is available even if the feature is
>> > +disabled.  To return the feature's enablement state configure
>> SHADOW_STACK_ENABLED.
>> > +Set it to true in case the shadow stack is enabled.
>> 
>> The wording "configure SHADOW_STACK_ENABLED" seems a little strange.
>> Also, there's a bunch of important detail that this comment doesn't cover.
>> Here's what I'd suggest, though it's possible this doesn't match the
>> implementation (I haven't checked the next patch yet), but this does match
>> default_get_shadow_stack_pointer.  Feel free to take any of this that is
>> useful:
>> 
>>   If possible, return the shadow stack pointer.  On some architectures,
>>   the shadow stack pointer is available even if the feature is disabled.
>>   If the shadow stack feature is enabled then set SHADOW_STACK_ENABLED
>>   to true, otherwise set SHADOW_STACK_ENABLED to false.  The
>>   SHADOW_STACK_ENABLED will always be set if this function returns a
>>   value.  If the function doesn't return a value then the state of
>>   SHADOW_STACK_ENABLED is undefined.
>
> Hm, I am not sure if I am missing something here.
>
> We set SHADOW_STACK_ENABLED to false in default_get_shadow_stack_pointer
> and *do not*  return a value. 
> ~~~
> std::optional<CORE_ADDR>
> default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
> 				  bool &shadow_stack_enabled)
> {
>   shadow_stack_enabled = false;
>   return {};
> }
> ~~~
>
> What do you think about the following:
> If possible, return the shadow stack pointer.  If the shadow stack feature is enabled
> then set SHADOW_STACK_ENABLED to true, otherwise set SHADOW_STACK_ENABLED
> to false.
> On some architectures, the shadow stack pointer is available even if the feature is disabled.
> So dependent on the target, an implementation of this function may return a valid shadow
> stack pointer, but set  SHADOW_STACK_ENABLED to false.  

That's great.  Could you also add a sentence similar to the one added to
shadow_stack_push that get_shadow_stack_pointer must be implemented in
order for displaced stepping to work.

I still don't understand why you want to document that detail in the
comment for shadow_stack_push, but I don't think it's going to do any
harm.  But I do think details about get_shadow_stack_pointer should be
documented on get_shadow_stack_pointer!

With that extra sentence added:

Approved-By: Andrew Burgess <aburgess@redhat.com>

Thanks,
Andrew


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer.
  2025-08-14 15:50       ` Andrew Burgess
@ 2025-08-19 15:37         ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-19 15:37 UTC (permalink / raw)
  To: Andrew Burgess, gdb-patches; +Cc: thiago.bauermann, luis.machado

Hi Andrew,

Thanks a lot for the feedback.

> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Thursday, August 14, 2025 5:51 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: RE: [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get
> the shadow stack pointer.
> 
> "Schimpe, Christina" <christina.schimpe@intel.com> writes:
> 
> > Hi Andrew,
> >
> > Thanks for the feedback. Please find my comments to your feedback below.
> >
> >> -----Original Message-----
> >> From: Andrew Burgess <aburgess@redhat.com>
> >> Sent: Wednesday, July 30, 2025 2:22 PM
> >> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> >> patches@sourceware.org
> >> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> >> Subject: Re: [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method
> >> to get the shadow stack pointer.
> >>
> >> Christina Schimpe <christina.schimpe@intel.com> writes:
> >>
> >> > This patch is required by the following commit
> >> > "gdb: Enable displaced stepping with shadow stack on amd64 linux."
> >> >
> >> > Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> >> > Reviewed-By: Luis Machado <luis.machado@arm.com>
> >> > ---
> >> >  gdb/arch-utils.c          | 10 ++++++++++
> >> >  gdb/arch-utils.h          |  5 +++++
> >> >  gdb/gdbarch-gen.c         | 22 ++++++++++++++++++++++
> >> >  gdb/gdbarch-gen.h         | 12 +++++++++++-
> >> >  gdb/gdbarch_components.py | 17 ++++++++++++++++-
> >> >  5 files changed, 64 insertions(+), 2 deletions(-)
> >> >
> >> > diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index
> >> > f320d3d7365..c396e9e3840 100644
> >> > --- a/gdb/arch-utils.c
> >> > +++ b/gdb/arch-utils.c
> >> > @@ -1218,6 +1218,16 @@ default_gdbarch_return_value
> >> >  				readbuf, writebuf);
> >> >  }
> >> >
> >> > +/* See arch-utils.h.  */
> >> > +
> >> > +std::optional<CORE_ADDR>
> >> > +default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache
> >> *regcache,
> >> > +				  bool &shadow_stack_enabled)
> >> > +{
> >> > +  shadow_stack_enabled = false;
> >> > +  return {};
> >> > +}
> >> > +
> >> >  obstack *gdbarch_obstack (gdbarch *arch)  {
> >> >    return &arch->obstack;
> >> > diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index
> >> > 1509cb7441e..14a84b74733 100644
> >> > --- a/gdb/arch-utils.h
> >> > +++ b/gdb/arch-utils.h
> >> > @@ -414,4 +414,9 @@ extern enum return_value_convention
> >> default_gdbarch_return_value
> >> >        struct regcache *regcache, struct value **read_value,
> >> >        const gdb_byte *writebuf);
> >> >
> >> > +/* Default implementation of gdbarch default_get_shadow_stack_pointer
> >> > +   method.  */
> >> > +extern std::optional<CORE_ADDR> default_get_shadow_stack_pointer
> >> > +  (gdbarch *gdbarch, regcache *regcache, bool
> >> > +&shadow_stack_enabled);
> >> > +
> >> >  #endif /* GDB_ARCH_UTILS_H */
> >> > diff --git a/gdb/gdbarch-gen.c b/gdb/gdbarch-gen.c index
> >> > a4b72793fd8..caeda3cefae 100644
> >> > --- a/gdb/gdbarch-gen.c
> >> > +++ b/gdb/gdbarch-gen.c
> >> > @@ -263,6 +263,7 @@ struct gdbarch
> >> >    gdbarch_use_target_description_from_corefile_notes_ftype
> >> *use_target_description_from_corefile_notes =
> >> default_use_target_description_from_corefile_notes;
> >> >    gdbarch_core_parse_exec_context_ftype *core_parse_exec_context =
> >> default_core_parse_exec_context;
> >> >    gdbarch_shadow_stack_push_ftype *shadow_stack_push = nullptr;
> >> > +  gdbarch_get_shadow_stack_pointer_ftype *get_shadow_stack_pointer
> >> > + = default_get_shadow_stack_pointer;
> >> >  };
> >> >
> >> >  /* Create a new ``struct gdbarch'' based on information provided
> >> > by @@ -537,6 +538,7 @@ verify_gdbarch (struct gdbarch *gdbarch)
> >> >    /* Skip verify of use_target_description_from_corefile_notes,
> >> > invalid_p
> >> == 0.  */
> >> >    /* Skip verify of core_parse_exec_context, invalid_p == 0.  */
> >> >    /* Skip verify of shadow_stack_push, has predicate.  */
> >> > +  /* Skip verify of get_shadow_stack_pointer, invalid_p == 0.  */
> >> >    if (!log.empty ())
> >> >      internal_error (_("verify_gdbarch: the following are invalid ...%s"),
> >> >  		    log.c_str ());
> >> > @@ -1414,6 +1416,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct
> >> ui_file *file)
> >> >    gdb_printf (file,
> >> >  	      "gdbarch_dump: shadow_stack_push = <%s>\n",
> >> >  	      host_address_to_string (gdbarch->shadow_stack_push));
> >> > +  gdb_printf (file,
> >> > +	      "gdbarch_dump: get_shadow_stack_pointer = <%s>\n",
> >> > +	      host_address_to_string
> >> > +(gdbarch->get_shadow_stack_pointer));
> >> >    if (gdbarch->dump_tdep != NULL)
> >> >      gdbarch->dump_tdep (gdbarch, file);  } @@ -5583,3 +5588,20 @@
> >> > set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,  {
> >> >    gdbarch->shadow_stack_push = shadow_stack_push;  }
> >> > +
> >> > +std::optional<CORE_ADDR>
> >> > +gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
> >> > +regcache *regcache, bool &shadow_stack_enabled) {
> >> > +  gdb_assert (gdbarch != NULL);
> >> > +  gdb_assert (gdbarch->get_shadow_stack_pointer != NULL);
> >> > +  if (gdbarch_debug >= 2)
> >> > +    gdb_printf (gdb_stdlog, "gdbarch_get_shadow_stack_pointer
> >> > +called\n");
> >> > +  return gdbarch->get_shadow_stack_pointer (gdbarch, regcache,
> >> > +shadow_stack_enabled); }
> >> > +
> >> > +void
> >> > +set_gdbarch_get_shadow_stack_pointer (struct gdbarch *gdbarch,
> >> > +
> >> gdbarch_get_shadow_stack_pointer_ftype
> >> > +get_shadow_stack_pointer) {
> >> > +  gdbarch->get_shadow_stack_pointer = get_shadow_stack_pointer; }
> >> > diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index
> >> > 71142332540..c36171b089e 100644
> >> > --- a/gdb/gdbarch-gen.h
> >> > +++ b/gdb/gdbarch-gen.h
> >> > @@ -1807,7 +1807,8 @@ extern void
> >> set_gdbarch_core_parse_exec_context (struct gdbarch *gdbarch, gdbarc
> >> >     technologies.  For example, the Intel Control-Flow Enforcement
> >> Technology
> >> >     (Intel CET) on x86 provides a shadow stack and indirect branch tracking.
> >> >     To enable shadow stack support for inferior calls the
> >> shadow_stack_push
> >> > -   gdbarch hook has to be provided.
> >> > +   gdbarch hook has to be provided.  The get_shadow_stack_pointer
> >> gdbarch
> >> > +   hook has to be provided to enable displaced stepping.
> >> >
> >> >     Push NEW_ADDR to the shadow stack and update the shadow stack
> >> > pointer. */
> >> >
> >> > @@ -1816,3 +1817,12 @@ extern bool gdbarch_shadow_stack_push_p
> >> (struct
> >> > gdbarch *gdbarch);  typedef void (gdbarch_shadow_stack_push_ftype)
> >> > (struct gdbarch *gdbarch, CORE_ADDR new_addr, regcache *regcache);
> >> > extern void gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
> >> > CORE_ADDR new_addr, regcache *regcache);  extern void
> >> > set_gdbarch_shadow_stack_push (struct gdbarch *gdbarch,
> >> > gdbarch_shadow_stack_push_ftype *shadow_stack_push);
> >> > +
> >> > +/* If possible, return the shadow stack pointer.  On some
> >> > +architectures,
> >> the
> >> > +   shadow stack pointer is available even if the feature is disabled.  To
> >> > +   return the feature's enablement state configure
> >> SHADOW_STACK_ENABLED.
> >> > +   Set it to true in case the shadow stack is enabled. */
> >> > +
> >> > +typedef std::optional<CORE_ADDR>
> >> > +(gdbarch_get_shadow_stack_pointer_ftype) (struct gdbarch *gdbarch,
> >> > +regcache *regcache, bool &shadow_stack_enabled); extern
> >> > +std::optional<CORE_ADDR> gdbarch_get_shadow_stack_pointer (struct
> >> > +gdbarch *gdbarch, regcache *regcache, bool &shadow_stack_enabled);
> >> > +extern void set_gdbarch_get_shadow_stack_pointer (struct gdbarch
> >> > +*gdbarch, gdbarch_get_shadow_stack_pointer_ftype
> >> > +*get_shadow_stack_pointer);
> >> > diff --git a/gdb/gdbarch_components.py b/gdb/gdbarch_components.py
> >> > index abc79588473..73459064170 100644
> >> > --- a/gdb/gdbarch_components.py
> >> > +++ b/gdb/gdbarch_components.py
> >> > @@ -2855,7 +2855,8 @@ Some targets support special hardware-
> >> assisted
> >> > control-flow protection  technologies.  For example, the Intel
> >> > Control-Flow Enforcement Technology  (Intel CET) on x86 provides a
> >> shadow stack and indirect branch tracking.
> >> >  To enable shadow stack support for inferior calls the
> >> > shadow_stack_push -gdbarch hook has to be provided.
> >> > +gdbarch hook has to be provided.  The get_shadow_stack_pointer
> >> > +gdbarch hook has to be provided to enable displaced stepping.
> >>
> >> I find the addition of this last sentence here a little strange.
> >> While it's a true statement, wouldn't this be better placed on the
> >> comment for get_shadow_stack_pointer?
> >
> > Mh I see the initial comment section here rather as general overview
> > for the shadow stack feature in GDB. It explains which features (e.g.
> > infcalls and displaced
> > stepping) are interacting with shadow stacks.  So in my opinion it's okay to
> keep it as is.
> >
> >> >
> >> >  Push NEW_ADDR to the shadow stack and update the shadow stack
> >> pointer.
> >> >  """,
> >> > @@ -2864,3 +2865,17 @@ Push NEW_ADDR to the shadow stack and
> >> update the shadow stack pointer.
> >> >      params=[("CORE_ADDR", "new_addr"), ("regcache *", "regcache")],
> >> >      predicate=True,
> >> >  )
> >> > +
> >> > +Method(
> >> > +    comment="""
> >> > +If possible, return the shadow stack pointer.  On some
> >> > +architectures, the shadow stack pointer is available even if the
> >> > +feature is disabled.  To return the feature's enablement state
> >> > +configure
> >> SHADOW_STACK_ENABLED.
> >> > +Set it to true in case the shadow stack is enabled.
> >>
> >> The wording "configure SHADOW_STACK_ENABLED" seems a little strange.
> >> Also, there's a bunch of important detail that this comment doesn't cover.
> >> Here's what I'd suggest, though it's possible this doesn't match the
> >> implementation (I haven't checked the next patch yet), but this does
> >> match default_get_shadow_stack_pointer.  Feel free to take any of
> >> this that is
> >> useful:
> >>
> >>   If possible, return the shadow stack pointer.  On some architectures,
> >>   the shadow stack pointer is available even if the feature is disabled.
> >>   If the shadow stack feature is enabled then set SHADOW_STACK_ENABLED
> >>   to true, otherwise set SHADOW_STACK_ENABLED to false.  The
> >>   SHADOW_STACK_ENABLED will always be set if this function returns a
> >>   value.  If the function doesn't return a value then the state of
> >>   SHADOW_STACK_ENABLED is undefined.
> >
> > Hm, I am not sure if I am missing something here.
> >
> > We set SHADOW_STACK_ENABLED to false in
> > default_get_shadow_stack_pointer and *do not*  return a value.
> > ~~~
> > std::optional<CORE_ADDR>
> > default_get_shadow_stack_pointer (gdbarch *gdbarch, regcache *regcache,
> > 				  bool &shadow_stack_enabled)
> > {
> >   shadow_stack_enabled = false;
> >   return {};
> > }
> > ~~~
> >
> > What do you think about the following:
> > If possible, return the shadow stack pointer.  If the shadow stack
> > feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise
> > set SHADOW_STACK_ENABLED to false.
> > On some architectures, the shadow stack pointer is available even if the feature
> is disabled.
> > So dependent on the target, an implementation of this function may
> > return a valid shadow stack pointer, but set  SHADOW_STACK_ENABLED to
> false.
> 
> That's great.  Could you also add a sentence similar to the one added to
> shadow_stack_push that get_shadow_stack_pointer must be implemented in
> order for displaced stepping to work.
> 
> I still don't understand why you want to document that detail in the comment
> for shadow_stack_push, but I don't think it's going to do any harm.  But I do
> think details about get_shadow_stack_pointer should be documented on
> get_shadow_stack_pointer!

I totally agree, otherwise there is the risk that this is overseen.

I'll change it to:
If possible, return the shadow stack pointer.  If the shadow stack 
feature is enabled then set SHADOW_STACK_ENABLED to true, otherwise 
set SHADOW_STACK_ENABLED to false.  This hook has to be provided to enable
displaced stepping for shadow stack enabled programs.
On some architectures, the shadow stack pointer is available even if the
feature is disabled.  So dependent on the target, an implementation of
this function may return a valid shadow stack pointer, but set
SHADOW_STACK_ENABLED to false.

> 
> With that extra sentence added:
> 
> Approved-By: Andrew Burgess <aburgess@redhat.com>
> 
> Thanks,
> Andrew

Christina

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
                   ` (13 preceding siblings ...)
  2025-07-11 10:36 ` Luis Machado
@ 2025-08-20  9:16 ` Schimpe, Christina
  2025-08-20 15:21   ` Schimpe, Christina
  14 siblings, 1 reply; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-20  9:16 UTC (permalink / raw)
  To: tom, gdb-patches; +Cc: thiago.bauermann, luis.machado, Andrew Burgess

Hi Tom, 

I retested my CET shadow stack series and noticed a new fail in gdb.dap/scopes.exp, if  I rebase the series on latest upstream master.
Reason seems to be the patch that you added recently: 'Do not allow DAP clients to dereference "void *"' and it's handling of unavailable registers.

The shadow stack feature is disabled by default, so the pl3_ssp register which is added with my CET shadow stack series will be shown as unavailable and we see a TCL error:
~~~
>>> {"seq": 12, "type": "request", "command": "variables", "arguments": {"variablesReference": 2, "count": 85}}
Content-Length: 129^M
^M
{"request_seq": 12, "type": "response", "command": "variables", "success": false, "message": "value is not available", "seq": 25}FAIL: gdb.dap/scopes.exp: fetch all registers success
ERROR: tcl error sourcing /tmp/gdb/testsuite/gdb.dap/scopes.exp.
ERROR: tcl error code TCL LOOKUP DICT body
ERROR: key "body" not known in dictionary
    while executing
"dict get $val body variables"
    (file "/tmp/gdb/testsuite/gdb.dap/scopes.exp" line 152)
    invoked from within
"source /tmp/gdb/testsuite/gdb.dap/scopes.exp"
    ("uplevel" body line 1)
    invoked from within
"uplevel #0 source /tmp/gdb/testsuite/gdb.dap/scopes.exp"
    invoked from within
"catch "uplevel #0 source $test_file_name" msg"
UNRESOLVED: gdb.dap/scopes.exp: testcase '/tmp/gdb/testsuite/gdb.dap/scopes.exp' aborted due to Tcl error
~~~

I could make this test pass by enabling the shadow stack feature in that specific test, if the HW supports CET shadow stack. 
The enablement would be done similar to my CET shadow stack tests:

~~~
save_vars { ::env(GLIBC_TUNABLES) } {

    append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK"
~~~

An alternative would be that the test is able to handle unavailable registers, which I think is better actually.
But I don't have a proper fix available yet.

What do you think ? 

Thanks,
Christina

 

> -----Original Message-----
> From: Christina Schimpe <christina.schimpe@intel.com>
> Sent: Saturday, June 28, 2025 10:28 AM
> To: gdb-patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> Subject: [PATCH v5 00/12] Add CET shadow stack support
> 
> Hi all,
> 
> this is my v5 of the series to add amd64 shadow stack support to GDB on
> linux.
> It addresses the feedback of Luis.
> 
> v4 can be found here:
> https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html
> 
> Changes since v4:
> - Improve some comments.
> - Change the test in "gdb: amd64 linux coredump support with shadow
>   stack." to also test core file generated by the linux kernel.  This
>   requires changes for the core_find procedure to save program output,
>   that have been implemented by Thiago already, so we include this part
>   of the patch in this series: "gdb, testsuite: Extend core_find procedure
>   to save program output.".  The test is now very similar to the test
>   implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
>   providing the input here!
> 
> I am looking forward to your feedback!
> 
> Regards,
> 
> Christina
> 
> Christina Schimpe (12):
>   gdb, testsuite: Extend core_find procedure to save program output.
>   gdbserver: Add optional runtime register set type.
>   gdbserver: Add assert in x86_linux_read_description.
>   gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
>   gdb, gdbserver: Use xstate_bv for target description creation on x86.
>   gdb, gdbserver: Add support of Intel shadow stack pointer register.
>   gdb: amd64 linux coredump support with shadow stack.
>   gdb: Handle shadow stack pointer register unwinding for amd64 linux.
>   gdb, gdbarch: Enable inferior calls for shadow stack support.
>   gdb: Implement amd64 linux shadow stack support for inferior calls.
>   gdb, gdbarch: Introduce gdbarch method to get the shadow stack
>     pointer.
>   gdb: Enable displaced stepping with shadow stack on amd64 linux.
> 
>  gdb/NEWS                                      |   6 +
>  gdb/amd64-linux-nat.c                         |  17 ++
>  gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
>  gdb/amd64-tdep.c                              |  35 ++-
>  gdb/amd64-tdep.h                              |   9 +-
>  gdb/arch-utils.c                              |  10 +
>  gdb/arch-utils.h                              |   5 +
>  gdb/arch/amd64-linux-tdesc.c                  |  33 +--
>  gdb/arch/amd64-linux-tdesc.h                  |   7 +-
>  gdb/arch/amd64.c                              |  25 +-
>  gdb/arch/amd64.h                              |  10 +-
>  gdb/arch/i386-linux-tdesc.c                   |  29 +--
>  gdb/arch/i386-linux-tdesc.h                   |   5 +-
>  gdb/arch/i386.c                               |  19 +-
>  gdb/arch/i386.h                               |   8 +-
>  gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
>  gdb/arch/x86-linux-tdesc-features.h           |  25 +-
>  gdb/doc/gdb.texinfo                           |  42 ++++
>  gdb/features/Makefile                         |   2 +
>  gdb/features/i386/32bit-ssp.c                 |  14 ++
>  gdb/features/i386/32bit-ssp.xml               |  11 +
>  gdb/features/i386/64bit-ssp.c                 |  14 ++
>  gdb/features/i386/64bit-ssp.xml               |  11 +
>  gdb/gdbarch-gen.c                             |  54 +++++
>  gdb/gdbarch-gen.h                             |  24 ++
>  gdb/gdbarch_components.py                     |  31 +++
>  gdb/i386-tdep.c                               |  51 +++-
>  gdb/i386-tdep.h                               |  11 +-
>  gdb/infcall.c                                 |  14 +-
>  gdb/linux-tdep.c                              |  47 ++++
>  gdb/linux-tdep.h                              |   7 +
>  gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
>  gdb/nat/x86-linux-tdesc.c                     |  20 +-
>  gdb/nat/x86-linux-tdesc.h                     |   7 +-
>  gdb/nat/x86-linux.c                           |  57 +++++
>  gdb/nat/x86-linux.h                           |   4 +
>  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
>  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
>  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
> .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
>  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
>  gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
>  .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
>  gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
>  gdb/x86-linux-nat.c                           |  50 +++-
>  gdb/x86-linux-nat.h                           |  11 +
>  gdb/x86-tdep.c                                |  21 ++
>  gdb/x86-tdep.h                                |   9 +
>  gdbserver/i387-fp.cc                          |  40 ++--
>  gdbserver/linux-amd64-ipa.cc                  |  10 +-
>  gdbserver/linux-i386-ipa.cc                   |   6 +-
>  gdbserver/linux-low.cc                        |  50 ++--
>  gdbserver/linux-low.h                         |   7 +-
>  gdbserver/linux-x86-low.cc                    |  44 +++-
>  gdbsupport/x86-xstate.h                       |   7 +-
>  55 files changed, 1687 insertions(+), 217 deletions(-)  create mode 100644
> gdb/features/i386/32bit-ssp.c  create mode 100644
> gdb/features/i386/32bit-ssp.xml  create mode 100644
> gdb/features/i386/64bit-ssp.c  create mode 100644
> gdb/features/i386/64bit-ssp.xml  create mode 100644
> gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-
> corefile.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-
> corefile.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-
> step.exp
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
>  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> 
> --
> 2.43.0

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

* RE: [PATCH v5 00/12] Add CET shadow stack support
  2025-08-20  9:16 ` Schimpe, Christina
@ 2025-08-20 15:21   ` Schimpe, Christina
  0 siblings, 0 replies; 67+ messages in thread
From: Schimpe, Christina @ 2025-08-20 15:21 UTC (permalink / raw)
  To: tom, gdb-patches; +Cc: thiago.bauermann, luis.machado, Andrew Burgess

Hi Tom,

I'll fix the issue in gdb.dap/scopes.exp described below as follows:

~~~~
save_vars { ::env(GLIBC_TUNABLES) } {

    # If x86 shadow stack is supported we need to configure GLIBC_TUNABLES
    # such that the feature is enabled and the register pl3_ssp is
    # available.  Otherwise the request to fetch all registers in this test will fail
    # with "message": "value is not available".
    if { [allow_ssp_tests] } {
	append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK"
    }

    if {[dap_initialize] == ""} {
	return
    }
[...}
~~~

Since to me it seems that the test
dap_check_request_and_response "fetch all registers"

requires all registers to be available.

Please let me know if you think otherwise.

Christina

> -----Original Message-----
> From: Schimpe, Christina <christina.schimpe@intel.com>
> Sent: Wednesday, August 20, 2025 11:16 AM
> To: tom@tromey.com; gdb-patches@sourceware.org
> Cc: thiago.bauermann@linaro.org; luis.machado@arm.com; Andrew
> Burgess <aburgess@redhat.com>
> Subject: RE: [PATCH v5 00/12] Add CET shadow stack support
> 
> Hi Tom,
> 
> I retested my CET shadow stack series and noticed a new fail in
> gdb.dap/scopes.exp, if  I rebase the series on latest upstream master.
> Reason seems to be the patch that you added recently: 'Do not allow DAP
> clients to dereference "void *"' and it's handling of unavailable registers.
> 
> The shadow stack feature is disabled by default, so the pl3_ssp register
> which is added with my CET shadow stack series will be shown as
> unavailable and we see a TCL error:
> ~~~
> >>> {"seq": 12, "type": "request", "command": "variables", "arguments":
> >>> {"variablesReference": 2, "count": 85}}
> Content-Length: 129^M
> ^M
> {"request_seq": 12, "type": "response", "command": "variables", "success":
> false, "message": "value is not available", "seq": 25}FAIL:
> gdb.dap/scopes.exp: fetch all registers success
> ERROR: tcl error sourcing /tmp/gdb/testsuite/gdb.dap/scopes.exp.
> ERROR: tcl error code TCL LOOKUP DICT body
> ERROR: key "body" not known in dictionary
>     while executing
> "dict get $val body variables"
>     (file "/tmp/gdb/testsuite/gdb.dap/scopes.exp" line 152)
>     invoked from within
> "source /tmp/gdb/testsuite/gdb.dap/scopes.exp"
>     ("uplevel" body line 1)
>     invoked from within
> "uplevel #0 source /tmp/gdb/testsuite/gdb.dap/scopes.exp"
>     invoked from within
> "catch "uplevel #0 source $test_file_name" msg"
> UNRESOLVED: gdb.dap/scopes.exp: testcase
> '/tmp/gdb/testsuite/gdb.dap/scopes.exp' aborted due to Tcl error ~~~
> 
> I could make this test pass by enabling the shadow stack feature in that
> specific test, if the HW supports CET shadow stack.
> The enablement would be done similar to my CET shadow stack tests:
> 
> ~~~
> save_vars { ::env(GLIBC_TUNABLES) } {
> 
>     append_environment GLIBC_TUNABLES "glibc.cpu.hwcaps" "SHSTK"
> ~~~
> 
> An alternative would be that the test is able to handle unavailable registers,
> which I think is better actually.
> But I don't have a proper fix available yet.
> 
> What do you think ?
> 
> Thanks,
> Christina
> 
> 
> 
> > -----Original Message-----
> > From: Christina Schimpe <christina.schimpe@intel.com>
> > Sent: Saturday, June 28, 2025 10:28 AM
> > To: gdb-patches@sourceware.org
> > Cc: thiago.bauermann@linaro.org; luis.machado@arm.com
> > Subject: [PATCH v5 00/12] Add CET shadow stack support
> >
> > Hi all,
> >
> > this is my v5 of the series to add amd64 shadow stack support to GDB
> > on linux.
> > It addresses the feedback of Luis.
> >
> > v4 can be found here:
> > https://sourceware.org/pipermail/gdb-patches/2025-June/218744.html
> >
> > Changes since v4:
> > - Improve some comments.
> > - Change the test in "gdb: amd64 linux coredump support with shadow
> >   stack." to also test core file generated by the linux kernel.  This
> >   requires changes for the core_find procedure to save program output,
> >   that have been implemented by Thiago already, so we include this part
> >   of the patch in this series: "gdb, testsuite: Extend core_find procedure
> >   to save program output.".  The test is now very similar to the test
> >   implemented for Guarded Control Stack corefiles.  Thanks to Thiago for
> >   providing the input here!
> >
> > I am looking forward to your feedback!
> >
> > Regards,
> >
> > Christina
> >
> > Christina Schimpe (12):
> >   gdb, testsuite: Extend core_find procedure to save program output.
> >   gdbserver: Add optional runtime register set type.
> >   gdbserver: Add assert in x86_linux_read_description.
> >   gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch.
> >   gdb, gdbserver: Use xstate_bv for target description creation on x86.
> >   gdb, gdbserver: Add support of Intel shadow stack pointer register.
> >   gdb: amd64 linux coredump support with shadow stack.
> >   gdb: Handle shadow stack pointer register unwinding for amd64 linux.
> >   gdb, gdbarch: Enable inferior calls for shadow stack support.
> >   gdb: Implement amd64 linux shadow stack support for inferior calls.
> >   gdb, gdbarch: Introduce gdbarch method to get the shadow stack
> >     pointer.
> >   gdb: Enable displaced stepping with shadow stack on amd64 linux.
> >
> >  gdb/NEWS                                      |   6 +
> >  gdb/amd64-linux-nat.c                         |  17 ++
> >  gdb/amd64-linux-tdep.c                        | 218 +++++++++++++++++-
> >  gdb/amd64-tdep.c                              |  35 ++-
> >  gdb/amd64-tdep.h                              |   9 +-
> >  gdb/arch-utils.c                              |  10 +
> >  gdb/arch-utils.h                              |   5 +
> >  gdb/arch/amd64-linux-tdesc.c                  |  33 +--
> >  gdb/arch/amd64-linux-tdesc.h                  |   7 +-
> >  gdb/arch/amd64.c                              |  25 +-
> >  gdb/arch/amd64.h                              |  10 +-
> >  gdb/arch/i386-linux-tdesc.c                   |  29 +--
> >  gdb/arch/i386-linux-tdesc.h                   |   5 +-
> >  gdb/arch/i386.c                               |  19 +-
> >  gdb/arch/i386.h                               |   8 +-
> >  gdb/arch/x86-linux-tdesc-features.c           |  60 ++---
> >  gdb/arch/x86-linux-tdesc-features.h           |  25 +-
> >  gdb/doc/gdb.texinfo                           |  42 ++++
> >  gdb/features/Makefile                         |   2 +
> >  gdb/features/i386/32bit-ssp.c                 |  14 ++
> >  gdb/features/i386/32bit-ssp.xml               |  11 +
> >  gdb/features/i386/64bit-ssp.c                 |  14 ++
> >  gdb/features/i386/64bit-ssp.xml               |  11 +
> >  gdb/gdbarch-gen.c                             |  54 +++++
> >  gdb/gdbarch-gen.h                             |  24 ++
> >  gdb/gdbarch_components.py                     |  31 +++
> >  gdb/i386-tdep.c                               |  51 +++-
> >  gdb/i386-tdep.h                               |  11 +-
> >  gdb/infcall.c                                 |  14 +-
> >  gdb/linux-tdep.c                              |  47 ++++
> >  gdb/linux-tdep.h                              |   7 +
> >  gdb/nat/x86-gcc-cpuid.h                       | 153 +++++++++---
> >  gdb/nat/x86-linux-tdesc.c                     |  20 +-
> >  gdb/nat/x86-linux-tdesc.h                     |   7 +-
> >  gdb/nat/x86-linux.c                           |  57 +++++
> >  gdb/nat/x86-linux.h                           |   4 +
> >  .../gdb.arch/amd64-shadow-stack-cmds.exp      | 141 +++++++++++
> >  .../gdb.arch/amd64-shadow-stack-corefile.c    |  42 ++++
> >  .../gdb.arch/amd64-shadow-stack-corefile.exp  | 110 +++++++++
> > .../gdb.arch/amd64-shadow-stack-disp-step.exp |  92 ++++++++
> >  gdb/testsuite/gdb.arch/amd64-shadow-stack.c   |  35 +++
> >  gdb/testsuite/gdb.arch/amd64-ssp.exp          |  50 ++++
> >  .../gdb.base/inline-frame-cycle-unwind.py     |   4 +
> >  gdb/testsuite/lib/gdb.exp                     |  80 ++++++-
> >  gdb/x86-linux-nat.c                           |  50 +++-
> >  gdb/x86-linux-nat.h                           |  11 +
> >  gdb/x86-tdep.c                                |  21 ++
> >  gdb/x86-tdep.h                                |   9 +
> >  gdbserver/i387-fp.cc                          |  40 ++--
> >  gdbserver/linux-amd64-ipa.cc                  |  10 +-
> >  gdbserver/linux-i386-ipa.cc                   |   6 +-
> >  gdbserver/linux-low.cc                        |  50 ++--
> >  gdbserver/linux-low.h                         |   7 +-
> >  gdbserver/linux-x86-low.cc                    |  44 +++-
> >  gdbsupport/x86-xstate.h                       |   7 +-
> >  55 files changed, 1687 insertions(+), 217 deletions(-)  create mode
> > 100644 gdb/features/i386/32bit-ssp.c  create mode 100644
> > gdb/features/i386/32bit-ssp.xml  create mode 100644
> > gdb/features/i386/64bit-ssp.c  create mode 100644
> > gdb/features/i386/64bit-ssp.xml  create mode 100644
> > gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-
> > corefile.c
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-
> > corefile.exp
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-disp-
> > step.exp
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack.c
> >  create mode 100644 gdb/testsuite/gdb.arch/amd64-ssp.exp
> >
> > --
> > 2.43.0
> 
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de
> Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 67+ messages in thread

end of thread, other threads:[~2025-08-20 15:22 UTC | newest]

Thread overview: 67+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-06-28  8:27 [PATCH v5 00/12] Add CET shadow stack support Christina Schimpe
2025-06-28  8:27 ` [PATCH v5 01/12] gdb, testsuite: Extend core_find procedure to save program output Christina Schimpe
2025-07-14 12:21   ` Andrew Burgess
2025-07-17 13:37     ` Schimpe, Christina
2025-06-28  8:28 ` [PATCH v5 02/12] gdbserver: Add optional runtime register set type Christina Schimpe
2025-06-28  8:28 ` [PATCH v5 03/12] gdbserver: Add assert in x86_linux_read_description Christina Schimpe
2025-06-28  8:28 ` [PATCH v5 04/12] gdb: Sync up x86-gcc-cpuid.h with cpuid.h from gcc 14 branch Christina Schimpe
2025-06-28  8:28 ` [PATCH v5 05/12] gdb, gdbserver: Use xstate_bv for target description creation on x86 Christina Schimpe
2025-07-14 13:52   ` Andrew Burgess
2025-07-15 10:28     ` Schimpe, Christina
2025-07-23 12:47       ` Schimpe, Christina
2025-08-05 13:47         ` Andrew Burgess
2025-06-28  8:28 ` [PATCH v5 06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register Christina Schimpe
2025-07-25 12:49   ` Andrew Burgess
2025-07-25 15:03     ` Schimpe, Christina
2025-08-01 12:54       ` Schimpe, Christina
2025-08-05 13:57       ` Andrew Burgess
2025-08-06 19:53         ` Schimpe, Christina
2025-08-06 19:54           ` Schimpe, Christina
2025-08-07  3:17             ` Thiago Jung Bauermann
2025-08-14 11:39           ` Andrew Burgess
2025-07-29 13:51   ` Andrew Burgess
2025-08-01 12:40     ` Schimpe, Christina
2025-08-10 19:01   ` H.J. Lu
2025-08-10 20:07     ` Schimpe, Christina
2025-06-28  8:28 ` [PATCH v5 07/12] gdb: amd64 linux coredump support with shadow stack Christina Schimpe
2025-07-29 14:46   ` Andrew Burgess
2025-07-30  1:55     ` Thiago Jung Bauermann
2025-07-30 11:42       ` Schimpe, Christina
2025-08-04 15:28         ` Schimpe, Christina
2025-08-05  4:29           ` Thiago Jung Bauermann
2025-08-05 15:29             ` Schimpe, Christina
2025-08-06 20:52             ` Luis
2025-08-11 11:52               ` Schimpe, Christina
2025-08-04 12:45     ` Schimpe, Christina
2025-06-28  8:28 ` [PATCH v5 08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux Christina Schimpe
2025-07-30  9:58   ` Andrew Burgess
2025-07-30 12:06     ` Schimpe, Christina
2025-06-28  8:28 ` [PATCH v5 09/12] gdb, gdbarch: Enable inferior calls for shadow stack support Christina Schimpe
2025-07-30 10:42   ` Andrew Burgess
2025-06-28  8:28 ` [PATCH v5 10/12] gdb: Implement amd64 linux shadow stack support for inferior calls Christina Schimpe
2025-07-30 11:58   ` Andrew Burgess
2025-07-31 12:32     ` Schimpe, Christina
2025-06-28  8:28 ` [PATCH v5 11/12] gdb, gdbarch: Introduce gdbarch method to get the shadow stack pointer Christina Schimpe
2025-07-30 12:22   ` Andrew Burgess
2025-08-04 13:01     ` Schimpe, Christina
2025-08-14 15:50       ` Andrew Burgess
2025-08-19 15:37         ` Schimpe, Christina
2025-06-28  8:28 ` [PATCH v5 12/12] gdb: Enable displaced stepping with shadow stack on amd64 linux Christina Schimpe
2025-07-30 13:59   ` Andrew Burgess
2025-07-31 17:29     ` Schimpe, Christina
2025-07-08 15:18 ` [PATCH v5 00/12] Add CET shadow stack support Schimpe, Christina
2025-08-14  7:52   ` Schimpe, Christina
2025-07-11 10:36 ` Luis Machado
2025-07-11 13:54   ` Schimpe, Christina
2025-07-11 15:54     ` Luis Machado
2025-07-13 14:01       ` Schimpe, Christina
2025-07-13 19:05         ` Luis Machado
2025-07-13 19:57           ` Schimpe, Christina
2025-07-14  7:13           ` Luis Machado
2025-07-17 12:01             ` Schimpe, Christina
2025-07-17 14:59               ` Luis Machado
2025-07-23 12:45                 ` Schimpe, Christina
2025-07-28 17:05                   ` Luis Machado
2025-07-28 17:20                     ` Schimpe, Christina
2025-08-20  9:16 ` Schimpe, Christina
2025-08-20 15:21   ` Schimpe, Christina

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox