i386: Properly pop restore token in signal frame

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.

This patch also includes 2 tests, one has a restore token with 4 byte
padding and one without.

Tested on Linux/x86-64 CET machine with and without -m32.

libgcc/

	PR libgcc/85334
	* config/i386/shadow-stack-unwind.h (_Unwind_Frames_Increment):
	New.

gcc/testsuite/

	PR libgcc/85334
	* g++.target/i386/pr85334-1.C: New test.
	* g++.target/i386/pr85334-2.C: Likewise.
This commit is contained in:
H.J. Lu 2020-02-10 07:58:45 -08:00
parent 1cad5e89a9
commit bf6465d046
5 changed files with 158 additions and 0 deletions

View File

@ -1,3 +1,9 @@
2020-02-10 H.J. Lu <hongjiu.lu@intel.com>
PR libgcc/85334
* config/i386/shadow-stack-unwind.h (_Unwind_Frames_Increment):
New.
2020-02-10 Richard Earnshaw <rearnsha@arm.com>
PR target/91913

View File

@ -1,3 +1,9 @@
2020-02-10 H.J. Lu <hongjiu.lu@intel.com>
PR libgcc/85334
* g++.target/i386/pr85334-1.C: New test.
* g++.target/i386/pr85334-2.C: Likewise.
2020-02-10 Jakub Jelinek <jakub@redhat.com>
PR other/93641

View File

@ -0,0 +1,55 @@
// { dg-do run }
// { dg-require-effective-target cet }
// { dg-additional-options "-fexceptions -fnon-call-exceptions -fcf-protection" }
// Delta between numbers of call stacks of pr85334-1.C and pr85334-2.C is 1.
#include <signal.h>
#include <stdlib.h>
void sighandler (int signo, siginfo_t * si, void * uc)
{
throw (5);
}
char *
__attribute ((noinline, noclone))
dosegv ()
{
* ((volatile int *)0) = 12;
return 0;
}
int
__attribute ((noinline, noclone))
func2 ()
{
try {
dosegv ();
}
catch (int x) {
return (x != 5);
}
return 1;
}
int
__attribute ((noinline, noclone))
func1 ()
{
return func2 ();
}
int main ()
{
struct sigaction sa;
int status;
sa.sa_sigaction = sighandler;
sa.sa_flags = SA_SIGINFO;
status = sigaction (SIGSEGV, & sa, NULL);
status = sigaction (SIGBUS, & sa, NULL);
return func1 ();
}

View File

@ -0,0 +1,48 @@
// { dg-do run }
// { dg-require-effective-target cet }
// { dg-additional-options "-fexceptions -fnon-call-exceptions -fcf-protection" }
// Delta between numbers of call stacks of pr85334-1.C and pr85334-2.C is 1.
#include <signal.h>
#include <stdlib.h>
void sighandler (int signo, siginfo_t * si, void * uc)
{
throw (5);
}
char *
__attribute ((noinline, noclone))
dosegv ()
{
* ((volatile int *)0) = 12;
return 0;
}
int
__attribute ((noinline, noclone))
func1 ()
{
try {
dosegv ();
}
catch (int x) {
return (x != 5);
}
return 1;
}
int main ()
{
struct sigaction sa;
int status;
sa.sa_sigaction = sighandler;
sa.sa_flags = SA_SIGINFO;
status = sigaction (SIGSEGV, & sa, NULL);
status = sigaction (SIGBUS, & sa, NULL);
return func1 ();
}

View File

@ -49,3 +49,46 @@ 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. */
#ifndef __x86_64__
#undef _Unwind_Frames_Increment
#define _Unwind_Frames_Increment(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++;
#endif