> Am 16.12.2023 um 16:56 schrieb H.J. Lu <hjl.to...@gmail.com>:
> 
> Linux CET kernel places a restore token on shadow stack followed by
> optional additional information for signal handler to enhance security.
> The restore token is the previous shadow stack pointer with bit 63 set.
> It is usually transparent to user programs since kernel will pop the
> restore token and additional information when signal handler returns.
> But when an exception is thrown from a signal handler, now we need to
> pop the restore token and additional information from shadow stack.
> For x86-64, we just need to get the previous shadow stack pointer from
> the restore token.  For i386, shadow stack is unsupported.
> 
> To be compatible with the old unwinder which doesn't use the restore
> token to skip shadow stack frames used by signal handler, Linux kernel
> won't put additional information after the restore token by default.
> Define __cet_features to 1 to indicate that unwinder uses the restore
> token to skip shadow stack frames used by signal handler.  It can be
> checked by glibc before enabling additional information in shadow stack
> for signal handler.

Doesn’t the check need to be two ways
To support kernels not doing this?  Or
Not do it if the high bit isn’t set?

Richard 

>    * config/i386/libgcc-glibc.ver: Add __cet_features to
>    GCC_CET_FEATURES.
>    * config/i386/shadow-stack-unwind.h (_Unwind_Frames_Increment):
>    Only define for x86-64.  Get the previous shadow stack pointer
>    from the restore token and skip to the previous frame.
>    (__cet_features): New.
> ---
> libgcc/config/i386/libgcc-glibc.ver      |  4 ++
> libgcc/config/i386/shadow-stack-unwind.h | 84 +++++++-----------------
> 2 files changed, 29 insertions(+), 59 deletions(-)
> 
> diff --git a/libgcc/config/i386/libgcc-glibc.ver 
> b/libgcc/config/i386/libgcc-glibc.ver
> index 1c4665719da..9a8525757a4 100644
> --- a/libgcc/config/i386/libgcc-glibc.ver
> +++ b/libgcc/config/i386/libgcc-glibc.ver
> @@ -152,6 +152,10 @@ GCC_4.8.0 {
>   __cpu_model
>   __cpu_indicator_init
> }
> +
> +GCC_CET_FEATURES {
> +  __cet_features
> +}
> %else
> GCC_4.4.0 {
>   __addtf3
> diff --git a/libgcc/config/i386/shadow-stack-unwind.h 
> b/libgcc/config/i386/shadow-stack-unwind.h
> index e07ab4a10e4..afcce4b482d 100644
> --- a/libgcc/config/i386/shadow-stack-unwind.h
> +++ b/libgcc/config/i386/shadow-stack-unwind.h
> @@ -43,18 +43,15 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  
> If not, see
>     }                        \
>     while (0)
> 
> -/* Linux CET kernel places a restore token on shadow stack for signal
> -   handler to enhance security.  The restore token is 8 byte and aligned
> -   to 8 bytes.  It is usually transparent to user programs since kernel
> -   will pop the restore token when signal handler returns.  But when an
> -   exception is thrown from a signal handler, now we need to pop the
> -   restore token from shadow stack.  For x86-64, we just need to treat
> -   the signal frame as normal frame.  For i386, we need to search for
> -   the restore token to check if the original shadow stack is 8 byte
> -   aligned.  If the original shadow stack is 8 byte aligned, we just
> -   need to pop 2 slots, one restore token, from shadow stack.  Otherwise,
> -   we need to pop 3 slots, one restore token + 4 byte padding, from
> -   shadow stack.
> +/* Linux CET kernel places a restore token on shadow stack followed by
> +   additional information for signal handler to enhance security.  The
> +   restore token is the previous shadow stack pointer with bit 63 set.
> +   It is usually transparent to user programs since kernel will pop the
> +   restore token and additional information when signal handler returns.
> +   But when an exception is thrown from a signal handler, now we need to
> +   pop the restore token and additional information from shadow stack.
> +   For x86-64, we just need to get the previous shadow stack pointer from
> +   the restore token.  For i386, shadow stack is unsupported.
> 
>    When popping a stack frame, we compare the return address on normal
>    stack against the return address on shadow stack.  If they don't match,
> @@ -66,65 +63,34 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  
> If not, see
>    3. Signal stack frame since kernel puts a restore token on shadow
>       stack.
>  */
> -#undef _Unwind_Frames_Increment
> #ifdef __x86_64__
> +#undef _Unwind_Frames_Increment
> #define _Unwind_Frames_Increment(exc, context, frames)    \
>     {                            \
>       frames++;                        \
> -      if (exc->exception_class != 0            \
> -      && _Unwind_GetIP (context) != 0        \
> -      && !_Unwind_IsSignalFrame (context))        \
> +      _Unwind_Word ssp = _get_ssp ();            \
> +      if (ssp != 0)                    \
>    {                        \
> -      _Unwind_Word ssp = _get_ssp ();        \
> -      if (ssp != 0)                    \
> +      ssp += 8 * frames;            \
> +      if (_Unwind_IsSignalFrame (context))        \
>        {                        \
> -          ssp += 8 * frames;            \
> -          _Unwind_Word ra = *(_Unwind_Word *) ssp;    \
> -          if (ra != _Unwind_GetIP (context))    \
> -        return _URC_FATAL_PHASE2_ERROR;        \
> +          /* Get the previous SSP.  */        \
> +          _Unwind_Word prev_ssp            \
> +        = ((*(_Unwind_Word *) ssp)        \
> +           & ~0x8000000000000000LL);        \
> +          /* Skip to the previous frame.  */    \
> +          frames += (prev_ssp - ssp) / 8 - 1;    \
>        }                        \
> -    }                        \
> -    }
> -#else
> -#define _Unwind_Frames_Increment(exc, context, frames)    \
> -  if (_Unwind_IsSignalFrame (context))            \
> -    do                            \
> -      {                            \
> -    _Unwind_Word ssp, prev_ssp, token;        \
> -    ssp = _get_ssp ();                \
> -    if (ssp != 0)                    \
> -      {                        \
> -        /* Align shadow stack pointer to the next    \
> -           8 byte aligned boundary.  */        \
> -        ssp = (ssp + 4) & ~7;            \
> -        do                        \
> -          {                        \
> -        /* Look for a restore token.  */    \
> -        token = (*(_Unwind_Word *) (ssp - 8));    \
> -        prev_ssp = token & ~7;            \
> -        if (prev_ssp == ssp)            \
> -          break;                \
> -        ssp += 8;                \
> -          }                        \
> -        while (1);                    \
> -        frames += (token & 0x4) ? 3 : 2;        \
> -      }                        \
> -      }                            \
> -    while (0);                        \
> -  else                            \
> -    {                            \
> -      frames++;                        \
> -      if (exc->exception_class != 0            \
> -      && _Unwind_GetIP (context) != 0)        \
> -    {                        \
> -      _Unwind_Word ssp = _get_ssp ();        \
> -      if (ssp != 0)                    \
> +      else if (_Unwind_GetIP (context) != 0        \
> +           && exc->exception_class != 0)    \
>        {                        \
> -          ssp += 4 * frames;            \
>          _Unwind_Word ra = *(_Unwind_Word *) ssp;    \
>          if (ra != _Unwind_GetIP (context))    \
>        return _URC_FATAL_PHASE2_ERROR;        \
>        }                        \
>    }                        \
>     }
> +
> +/* Bit 0: Unwinder uses the restore token in signal frame.  */
> +const int __cet_features = 1;
> #endif
> -- 
> 2.43.0
> 

Reply via email to