[06/12] gdb, gdbserver: Add support of Intel shadow stack pointer register.

Message ID 20241220200501.324191-7-christina.schimpe@intel.com
State New
Headers
Series Add CET shadow stack support |

Commit Message

Schimpe, Christina Dec. 20, 2024, 8:04 p.m. UTC
  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.
---
 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                               | 18 ++++-
 gdb/i386-tdep.h                               |  4 ++
 gdb/nat/x86-linux-tdesc.c                     |  2 +
 gdb/nat/x86-linux.c                           | 55 +++++++++++++++
 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                     | 69 +++++++++++++++++++
 gdb/x86-linux-nat.c                           | 50 ++++++++++++--
 gdb/x86-linux-nat.h                           | 11 +++
 gdb/x86-tdep.c                                | 22 ++++++
 gdb/x86-tdep.h                                |  9 +++
 gdbserver/linux-x86-low.cc                    | 28 +++++++-
 gdbsupport/x86-xstate.h                       |  5 +-
 28 files changed, 440 insertions(+), 9 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
  

Comments

Thiago Jung Bauermann Feb. 6, 2025, 3:13 a.m. UTC | #1
"Schimpe, Christina" <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

Nit: Linux (uppercase l)

> support.

<snip>

> diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c
> index 4a39028a472..59daaa4c583 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 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 ();
>  }

The patch description mentions that "32 bit support is not covered due
to missing linux kernel support". Is this change useful, or is it
unreachable code?

> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index a86f534528c..fc35456f1d3 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -4225,6 +4225,75 @@ 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]} {
> +	    return 0

There should be a call to "remote_file build delete $obj" here as well.

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

Nit: Linux

> +	    # 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)"
> +	}

<snip>

> diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c
> index d1fece717a7..5bbd4640e30 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 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

The "not" above should be removed.

> +     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,46 @@ x86_linux_get_thread_area (pid_t pid, void *addr, unsigned int *base_addr)
>  }
>  
>
> +/* 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;

In case the ptrace fails and there is already an old value for ssp in
regcache, shouldn't it be removed from it?

> +
> +  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);
> +
> +  /* Starting with v6.6., the Linux kernel supports CET shadow stack.
> +     Dependent on the target the ssp register can be unavailable or
> +     nullptr when shadow stack is supported by HW and the linux kernel but

Nit: Linux

> +     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"));

Same question here: if the ptrace call fails shouldn't the ssp value in
regcache be removed?

> +}
> +
>  void _initialize_x86_linux_nat ();
>  void
>  _initialize_x86_linux_nat ()
> diff --git a/gdb/x86-linux-nat.h b/gdb/x86-linux-nat.h
> index 3c2241bb0b6..d5dc1908090 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 e50b5fb9fa4..08fb0e8d82d 100644
> --- a/gdb/x86-tdep.c
> +++ b/gdb/x86-tdep.c
> @@ -17,10 +17,32 @@
>     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 "defs.h"

defs.h is now included in all GDB files via the gcc command line, and
shouldn't be #included anymore. See commit 18d2988e5da8 ("gdb,
gdbserver, gdbsupport: remove includes of early headers") and commit
ab7daea3ad0d ("gdb, gdbserver, gdbsupport: include early header files
with `-include`").

> +#include "i386-tdep.h"
>  #include "x86-tdep.h"
>  #include "symtab.h"

--
Thiago
  
Schimpe, Christina Feb. 6, 2025, 2:33 p.m. UTC | #2
Hi Thiago,

Thank you for the review. Please see my comments to your feedback below.

> -----Original Message-----
> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Sent: Thursday, February 6, 2025 4:14 AM
> To: Schimpe, Christina <christina.schimpe@intel.com>
> Cc: gdb-patches@sourceware.org
> Subject: Re: [PATCH 06/12] gdb, gdbserver: Add support of Intel shadow stack
> pointer register.
> 
> 
> "Schimpe, Christina" <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
> 
> Nit: Linux (uppercase l)
> 
> > support.

Will fix.
 
> <snip>
> 
> > diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c index
> > 4a39028a472..59daaa4c583 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 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 ();
> >  }
> 
> The patch description mentions that "32 bit support is not covered due to missing
> linux kernel support". Is this change useful, or is it unreachable code?

I think I consistently did not implement 32 bit linux support, but added the code
for 32 bit support in locations which are not linux dependent, as preparation for
other OS. But for now yes, this should be unreachable code.
Should I (1) Remove the code or (2) improve the commit message ?
I'd be in favour of (2),  would that be acceptable?

> > diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> > index a86f534528c..fc35456f1d3 100644
> > --- a/gdb/testsuite/lib/gdb.exp
> > +++ b/gdb/testsuite/lib/gdb.exp
> > @@ -4225,6 +4225,75 @@ 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]} {
> > +	    return 0
> 
> There should be a call to "remote_file build delete $obj" here as well.

Thanks for catching that. I'll fix it.

> > +	}
> > +	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
> 
> Nit: Linux

Will fix.

> > +	    # 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)"
> > +	}
> 
> <snip>
> 
> > diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c index
> > d1fece717a7..5bbd4640e30 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 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
> 
> The "not" above should be removed.

You mean the "not" in this sentence?
"Also it checks the enablement state of features which are not configured in xcr0, such as CET shadow stack.  "

This should be correct in that context. CET shadow stack is not configured in xcr0.

> > +     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,46 @@ x86_linux_get_thread_area (pid_t pid, void *addr,
> > unsigned int *base_addr)  }
> >
> 
> 
> >
> > +/* 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;
> 
> In case the ptrace fails and there is already an old value for ssp in regcache,
> shouldn't it be removed from it?

Hm, it doesn't seem to be a problem. I ran a test using inline enablement of shadow stack:
~~~
(gdb) p $pl3_ssp
$1 = (void *) 0x7ffff7c00000
(gdb) n
54	   if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) {
(gdb) n
58	   return ret;
(gdb) p $pl3_ssp
$2 = <unavailable>
~~~
So it should be fine from my perspective. Or do you see another potential issue?

> > +
> > +  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);
> > +
> > +  /* Starting with v6.6., the Linux kernel supports CET shadow stack.
> > +     Dependent on the target the ssp register can be unavailable or
> > +     nullptr when shadow stack is supported by HW and the linux
> > + kernel but
> 
> Nit: Linux

Will fix.

> > +     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"));
> 
> Same question here: if the ptrace call fails shouldn't the ssp value in regcache be
> removed?

Please see my answer above.

> > +}
> > +
> >  void _initialize_x86_linux_nat ();
> >  void
> >  _initialize_x86_linux_nat ()
> > diff --git a/gdb/x86-linux-nat.h b/gdb/x86-linux-nat.h index
> > 3c2241bb0b6..d5dc1908090 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
> > e50b5fb9fa4..08fb0e8d82d 100644
> > --- a/gdb/x86-tdep.c
> > +++ b/gdb/x86-tdep.c
> > @@ -17,10 +17,32 @@
> >     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 "defs.h"
> 
> defs.h is now included in all GDB files via the gcc command line, and shouldn't be
> #included anymore. See commit 18d2988e5da8 ("gdb, gdbserver, gdbsupport:
> remove includes of early headers") and commit ab7daea3ad0d ("gdb, gdbserver,
> gdbsupport: include early header files with `-include`").

Will fix, thanks for pointing that out.

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
  
Thiago Jung Bauermann Feb. 8, 2025, 3:44 a.m. UTC | #3
"Schimpe, Christina" <christina.schimpe@intel.com> writes:

> Hi Thiago,
>
> Thank you for the review. Please see my comments to your feedback below.
>
>> -----Original Message-----
>> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>> Sent: Thursday, February 6, 2025 4:14 AM
>> To: Schimpe, Christina <christina.schimpe@intel.com>
>> Cc: gdb-patches@sourceware.org
>> Subject: Re: [PATCH 06/12] gdb, gdbserver: Add support of Intel shadow stack
>> pointer register.
>>
>>
>> "Schimpe, Christina" <christina.schimpe@intel.com> writes:
>>
>> > diff --git a/gdb/arch/i386.c b/gdb/arch/i386.c index
>> > 4a39028a472..59daaa4c583 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 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 ();
>> >  }
>>
>> The patch description mentions that "32 bit support is not covered due to missing
>> linux kernel support". Is this change useful, or is it unreachable code?
>
> I think I consistently did not implement 32 bit linux support, but added the code
> for 32 bit support in locations which are not linux dependent, as preparation for
> other OS. But for now yes, this should be unreachable code.
> Should I (1) Remove the code or (2) improve the commit message ?
> I'd be in favour of (2),  would that be acceptable?

In the general case, I lean towards (1), but in this case it's just two
simple lines, and also in an arch-specific corner of GDB so it's not
really a problem. It's certainly acceptable in my view.

>> > diff --git a/gdb/x86-linux-nat.c b/gdb/x86-linux-nat.c index
>> > d1fece717a7..5bbd4640e30 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 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
>>
>> The "not" above should be removed.
>
> You mean the "not" in this sentence?
> "Also it checks the enablement state of features which are not configured in xcr0, such as
> CET shadow stack.  "
>
> This should be correct in that context. CET shadow stack is not configured in xcr0.

Ah, I see. Sorry, I had misread the comment.

>> > +     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,46 @@ x86_linux_get_thread_area (pid_t pid, void *addr,
>> > unsigned int *base_addr)  }
>> >
>>
>>
>> >
>> > +/* 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;
>>
>> In case the ptrace fails and there is already an old value for ssp in regcache,
>> shouldn't it be removed from it?
>
> Hm, it doesn't seem to be a problem. I ran a test using inline enablement of shadow stack:
> ~~~
> (gdb) p $pl3_ssp
> $1 = (void *) 0x7ffff7c00000
> (gdb) n
> 54	   if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) {
> (gdb) n
> 58	   return ret;
> (gdb) p $pl3_ssp
> $2 = <unavailable>
> ~~~
> So it should be fine from my perspective. Or do you see another potential issue?

Each time the inferior continues, GDB drops the existing regcaches and
builds new ones, so the values from $1 and $2 in the output above come
from different regcache instances.

I made the comment thinking that if GDB knows that ssp is unavailable
then it should strive to make sure that is reflected in the
regcache. But come to think of it, if the inferior is stopped then
whether ssp is available or not won't change until it runs again and the
scenario I was considering can't happen. Sorry about the false alarm.

--
Thiago
  

Patch

diff --git a/gdb/amd64-linux-nat.c b/gdb/amd64-linux-nat.c
index c3b7a19b5cb..df246408eaa 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 2c76a1de5a8..8ed381e1a2c 100644
--- a/gdb/amd64-linux-tdep.c
+++ b/gdb/amd64-linux-tdep.c
@@ -105,6 +105,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 37a2e3b03d4..a298333e70a 100644
--- a/gdb/amd64-tdep.c
+++ b/gdb/amd64-tdep.c
@@ -3197,6 +3197,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;
@@ -3359,12 +3362,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 e93e3931df3..fad28649a5a 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 72bd96aff76..2c45cb637ec 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 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 4a39028a472..59daaa4c583 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 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 3f041ece6eb..e57b6320ce7 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 7b6000abbea..c6c6fcaa17f 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -49750,6 +49750,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 7c10f203faa..5f918ca77e3 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -223,6 +223,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 \
@@ -230,6 +231,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 54aa1294917..b338029d990 100644
--- a/gdb/i386-tdep.c
+++ b/gdb/i386-tdep.c
@@ -8353,7 +8353,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))
@@ -8379,6 +8380,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.  */
@@ -8494,6 +8498,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;
 }
 
@@ -8785,6 +8798,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);
diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h
index 3f990e71abf..8912a72cc6e 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 c5eac277bfd..60ab23eb795 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 ad3ed3c2289..0b3a0d438dd 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,52 @@  x86_linux_ptrace_get_arch_size (int tid)
   return x86_linux_arch_size (false, false);
 #endif
 }
+
+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 7c0ac7718b6..e0df4effcde 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 278fa857d5d..94d553a8cce 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 a86f534528c..fc35456f1d3 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -4225,6 +4225,75 @@  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]} {
+	    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 d1fece717a7..5bbd4640e30 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 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,46 @@  x86_linux_get_thread_area (pid_t pid, void *addr, unsigned int *base_addr)
 }
 
 
+/* 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);
+
+  /* Starting with v6.6., the Linux kernel supports CET shadow stack.
+     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"));
+}
+
 void _initialize_x86_linux_nat ();
 void
 _initialize_x86_linux_nat ()
diff --git a/gdb/x86-linux-nat.h b/gdb/x86-linux-nat.h
index 3c2241bb0b6..d5dc1908090 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 e50b5fb9fa4..08fb0e8d82d 100644
--- a/gdb/x86-tdep.c
+++ b/gdb/x86-tdep.c
@@ -17,10 +17,32 @@ 
    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 "defs.h"
+#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 2b5853adb8a..fcb3909b33e 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 4575ea2fdc0..0e668c23b2a 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 76932c765a7..9dbf3be6404 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