[08/12] gdb: Handle shadow stack pointer register unwinding for amd64 linux.
Commit Message
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.
---
gdb/amd64-linux-tdep.c | 69 +++++++++++++++
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, 224 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
Comments
On 12/20/24 5:04 PM, Schimpe, Christina wrote:
> 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.
> ---
> gdb/amd64-linux-tdep.c | 69 +++++++++++++++
> 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, 224 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 95f643b1217..895feac85e8 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -45,6 +45,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"
> @@ -1873,6 +1875,72 @@ amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch,
> return (addr & amd64_linux_lam_untag_mask ());
> }
>
> +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);
> +
> + /* Starting with v6.6., the Linux kernel supports CET shadow stack.
> + 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.
> + For x32 the shadow stack elements are still 64-bit aligned.
> + Thus, we cannot use gdbarch_addr_bit to compute the new stack
> + pointer. */
> + const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
> + const int bytes_per_word
> + = (binfo->bits_per_word / binfo->bits_per_byte);
In patch 10 of this series, you introduce
amd64_linux_shadow_stack_element_size_aligned to simplify this
calculation. is there any reason you didn't introduce it here?
> -----Original Message-----
> From: Guinevere Larsen <guinevere@redhat.com>
> Sent: Thursday, January 30, 2025 3:29 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Subject: Re: [PATCH 08/12] gdb: Handle shadow stack pointer register unwinding
> for amd64 linux.
>
> On 12/20/24 5:04 PM, Schimpe, Christina wrote:
> > 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.
> > ---
> > gdb/amd64-linux-tdep.c | 69 +++++++++++++++
> > 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, 224 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
> > 95f643b1217..895feac85e8 100644
> > --- a/gdb/amd64-linux-tdep.c
> > +++ b/gdb/amd64-linux-tdep.c
> > @@ -45,6 +45,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"
> > @@ -1873,6 +1875,72 @@
> amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch,
> > return (addr & amd64_linux_lam_untag_mask ());
> > }
> >
> > +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);
> > +
> > + /* Starting with v6.6., the Linux kernel supports CET shadow stack.
> > + 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.
> > + For x32 the shadow stack elements are still 64-bit aligned.
> > + Thus, we cannot use gdbarch_addr_bit to compute the new stack
> > + pointer. */
> > + const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
> > + const int bytes_per_word
> > + = (binfo->bits_per_word / binfo->bits_per_byte);
> In patch 10 of this series, you introduce
> amd64_linux_shadow_stack_element_size_aligned to simplify this calculation. is
> there any reason you didn't introduce it here?
Thanks a lot for looking at this.
The reason is that at this state of the series I only had one occurrence of this
particular code line and its comment. To avoid duplication I decided to make a
small function for it in patch 10, but before it seemed to introduce more
overhead. Would that make sense to 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
On 1/30/25 1:11 PM, Schimpe, Christina wrote:
>> -----Original Message-----
>> From: Guinevere Larsen <guinevere@redhat.com>
>> Sent: Thursday, January 30, 2025 3:29 PM
>> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
>> patches@sourceware.org
>> Subject: Re: [PATCH 08/12] gdb: Handle shadow stack pointer register unwinding
>> for amd64 linux.
>>
>> On 12/20/24 5:04 PM, Schimpe, Christina wrote:
>>> 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.
>>> ---
>>> gdb/amd64-linux-tdep.c | 69 +++++++++++++++
>>> 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, 224 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
>>> 95f643b1217..895feac85e8 100644
>>> --- a/gdb/amd64-linux-tdep.c
>>> +++ b/gdb/amd64-linux-tdep.c
>>> @@ -45,6 +45,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"
>>> @@ -1873,6 +1875,72 @@
>> amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch,
>>> return (addr & amd64_linux_lam_untag_mask ());
>>> }
>>>
>>> +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);
>>> +
>>> + /* Starting with v6.6., the Linux kernel supports CET shadow stack.
>>> + 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.
>>> + For x32 the shadow stack elements are still 64-bit aligned.
>>> + Thus, we cannot use gdbarch_addr_bit to compute the new stack
>>> + pointer. */
>>> + const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
>>> + const int bytes_per_word
>>> + = (binfo->bits_per_word / binfo->bits_per_byte);
>> In patch 10 of this series, you introduce
>> amd64_linux_shadow_stack_element_size_aligned to simplify this calculation. is
>> there any reason you didn't introduce it here?
> Thanks a lot for looking at this.
>
> The reason is that at this state of the series I only had one occurrence of this
> particular code line and its comment. To avoid duplication I decided to make a
> small function for it in patch 10, but before it seemed to introduce more
> overhead. Would that make sense to you?
>
This makes sense, but in that case I think it's better to just create
this function at this point, so that code doesn't get created and
deleted unnecessarily.
> -----Original Message-----
> From: Guinevere Larsen <guinevere@redhat.com>
> Sent: Thursday, January 30, 2025 5:14 PM
> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> patches@sourceware.org
> Subject: Re: [PATCH 08/12] gdb: Handle shadow stack pointer register unwinding
> for amd64 linux.
>
> On 1/30/25 1:11 PM, Schimpe, Christina wrote:
> >> -----Original Message-----
> >> From: Guinevere Larsen <guinevere@redhat.com>
> >> Sent: Thursday, January 30, 2025 3:29 PM
> >> To: Schimpe, Christina <christina.schimpe@intel.com>; gdb-
> >> patches@sourceware.org
> >> Subject: Re: [PATCH 08/12] gdb: Handle shadow stack pointer register
> >> unwinding for amd64 linux.
> >>
> >> On 12/20/24 5:04 PM, Schimpe, Christina wrote:
> >>> 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.
> >>> ---
> >>> gdb/amd64-linux-tdep.c | 69 +++++++++++++++
> >>> 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, 224 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
> >>> 95f643b1217..895feac85e8 100644
> >>> --- a/gdb/amd64-linux-tdep.c
> >>> +++ b/gdb/amd64-linux-tdep.c
> >>> @@ -45,6 +45,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"
> >>> @@ -1873,6 +1875,72 @@
> >> amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch,
> >>> return (addr & amd64_linux_lam_untag_mask ());
> >>> }
> >>>
> >>> +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);
> >>> +
> >>> + /* Starting with v6.6., the Linux kernel supports CET shadow stack.
> >>> + 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.
> >>> + For x32 the shadow stack elements are still 64-bit aligned.
> >>> + Thus, we cannot use gdbarch_addr_bit to compute the new stack
> >>> + pointer. */
> >>> + const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
> >>> + const int bytes_per_word
> >>> + = (binfo->bits_per_word / binfo->bits_per_byte);
> >> In patch 10 of this series, you introduce
> >> amd64_linux_shadow_stack_element_size_aligned to simplify this
> >> calculation. is there any reason you didn't introduce it here?
> > Thanks a lot for looking at this.
> >
> > The reason is that at this state of the series I only had one
> > occurrence of this particular code line and its comment. To avoid
> > duplication I decided to make a small function for it in patch 10, but
> > before it seemed to introduce more overhead. Would that make sense to you?
> >
> This makes sense, but in that case I think it's better to just create this function at
> this point, so that code doesn't get created and deleted unnecessarily.
Mh right, that's also true. I will apply your feedback in the next version of this series.
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
"Schimpe, Christina" <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.
> ---
> gdb/amd64-linux-tdep.c | 69 +++++++++++++++
> 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, 224 insertions(+)
> create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
Just some minor comments.
Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c
> index 95f643b1217..895feac85e8 100644
> --- a/gdb/amd64-linux-tdep.c
> +++ b/gdb/amd64-linux-tdep.c
> @@ -45,6 +45,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"
> @@ -1873,6 +1875,72 @@ amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch,
> return (addr & amd64_linux_lam_untag_mask ());
> }
>
> +static value *
> +amd64_linux_dwarf2_prev_ssp (const frame_info_ptr &this_frame,
> + void **this_cache, int regnum)
> +{
Add documentation comment to this function.
> + 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);
> +
> + /* Starting with v6.6., the Linux kernel supports CET shadow stack.
> + 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.
> + For x32 the shadow stack elements are still 64-bit aligned.
> + Thus, we cannot use gdbarch_addr_bit to compute the new stack
> + pointer. */
> + const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
> + const int bytes_per_word
> + = (binfo->bits_per_word / binfo->bits_per_byte);
> + CORE_ADDR new_ssp = ssp + bytes_per_word;
I agree with Guinevere's comment about introducing
amd64_linux_shadow_stack_element_size_aligned in this patch.
> + /* 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;
> +}
> +
> +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;
> + }
> +}
Add documentation comment to this function.
--
Thiago
> -----Original Message-----
> From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
> Sent: Thursday, February 6, 2025 4:30 AM
> To: Schimpe, Christina <christina.schimpe@intel.com>
> Cc: gdb-patches@sourceware.org
> Subject: Re: [PATCH 08/12] gdb: Handle shadow stack pointer register unwinding
> for amd64 linux.
>
>
> "Schimpe, Christina" <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.
> > ---
> > gdb/amd64-linux-tdep.c | 69 +++++++++++++++
> > 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, 224 insertions(+)
> > create mode 100644 gdb/testsuite/gdb.arch/amd64-shadow-stack-cmds.exp
>
> Just some minor comments.
>
> Reviewed-by: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
>
> > diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index
> > 95f643b1217..895feac85e8 100644
> > --- a/gdb/amd64-linux-tdep.c
> > +++ b/gdb/amd64-linux-tdep.c
> > @@ -45,6 +45,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"
> > @@ -1873,6 +1875,72 @@
> amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch,
> > return (addr & amd64_linux_lam_untag_mask ()); }
> >
> > +static value *
> > +amd64_linux_dwarf2_prev_ssp (const frame_info_ptr &this_frame,
> > + void **this_cache, int regnum) {
>
> Add documentation comment to this function.
>
> > + 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);
> > +
> > + /* Starting with v6.6., the Linux kernel supports CET shadow stack.
> > + 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.
> > + For x32 the shadow stack elements are still 64-bit aligned.
> > + Thus, we cannot use gdbarch_addr_bit to compute the new stack
> > + pointer. */
> > + const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
> > + const int bytes_per_word
> > + = (binfo->bits_per_word / binfo->bits_per_byte);
> > + CORE_ADDR new_ssp = ssp + bytes_per_word;
>
> I agree with Guinevere's comment about introducing
> amd64_linux_shadow_stack_element_size_aligned in this patch.
>
> > + /* 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;
> > +}
> > +
> > +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;
> > + }
> > +}
>
> Add documentation comment to this function.
>
> --
> Thiago
Thank you for the review. I'll apply all your comments in the next version.
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
@@ -45,6 +45,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"
@@ -1873,6 +1875,72 @@ amd64_linux_remove_non_address_bits_watchpoint (gdbarch *gdbarch,
return (addr & amd64_linux_lam_untag_mask ());
}
+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);
+
+ /* Starting with v6.6., the Linux kernel supports CET shadow stack.
+ 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.
+ For x32 the shadow stack elements are still 64-bit aligned.
+ Thus, we cannot use gdbarch_addr_bit to compute the new stack
+ pointer. */
+ const bfd_arch_info *binfo = gdbarch_bfd_arch_info (gdbarch);
+ const int bytes_per_word
+ = (binfo->bits_per_word / binfo->bits_per_byte);
+ CORE_ADDR new_ssp = ssp + bytes_per_word;
+
+ /* 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;
+}
+
+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)
@@ -1927,6 +1995,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
@@ -47,6 +47,7 @@
#include <ctype.h>
#include <unordered_map>
+#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;
}
}
@@ -2744,6 +2751,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.
@@ -117,4 +117,11 @@ extern CORE_ADDR linux_get_hwcap2 ();
extern struct link_map_offsets *linux_ilp32_fetch_link_map_offsets ();
extern struct link_map_offsets *linux_lp64_fetch_link_map_offsets ();
+/* 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 */
new file mode 100644
@@ -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
+ }
+}
@@ -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;
}