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:
parent
1cad5e89a9
commit
bf6465d046
@ -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
|
||||
|
@ -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
|
||||
|
55
gcc/testsuite/g++.target/i386/pr85334-1.C
Normal file
55
gcc/testsuite/g++.target/i386/pr85334-1.C
Normal 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 ();
|
||||
}
|
48
gcc/testsuite/g++.target/i386/pr85334-2.C
Normal file
48
gcc/testsuite/g++.target/i386/pr85334-2.C
Normal 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 ();
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user