about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog30
-rw-r--r--sysdeps/unix/sysv/linux/x86/sys/ucontext.h2
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/__start_context.S75
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/getcontext.S50
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/makecontext.c38
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/setcontext.S92
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/swapcontext.S129
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/sysdep.h5
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/ucontext_i.sym1
9 files changed, 421 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 24396a0573..3cf7282707 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+2018-07-25  Vedvyas Shanbhogue  <vedvyas.shanbhogue@intel.com>
+	    H.J. Lu  <hongjiu.lu@intel.com>
+
+	* sysdeps/unix/sysv/linux/x86/sys/ucontext.h (ucontext_t): Add
+	__ssp.
+	* sysdeps/unix/sysv/linux/x86_64/__start_context.S: Include
+	<asm/prctl.h> and "ucontext_i.h" when shadow stack is enabled.
+	(__push___start_context): New.
+	* sysdeps/unix/sysv/linux/x86_64/getcontext.S: Include
+	<asm/prctl.h>.
+	(__getcontext): Record the current shadow stack base.  Save the
+	caller's shadow stack pointer and base.
+	* sysdeps/unix/sysv/linux/x86_64/makecontext.c: Include
+	<pthread.h>, <libc-pointer-arith.h> and <sys/prctl.h>.
+	(__push___start_context): New prototype.
+	(__makecontext): Call __push___start_context to allocate a new
+	shadow stack, push __start_context onto the new stack as well
+	as the new shadow stack.
+	* sysdeps/unix/sysv/linux/x86_64/setcontext.S: Include
+	<asm/prctl.h>.
+	(__setcontext): Restore the target shadow stack.
+	* sysdeps/unix/sysv/linux/x86_64/swapcontext.S: Include
+	<asm/prctl.h>.
+	(__swapcontext): Record the current shadow stack base.  Save
+	the caller's shadow stack pointer and base. Restore the target
+	shadow stack.
+	* sysdeps/unix/sysv/linux/x86_64/sysdep.h
+	(STACK_SIZE_TO_SHADOW_STACK_SIZE_SHIFT): New.
+	* sysdeps/unix/sysv/linux/x86_64/ucontext_i.sym (oSSP): New.
+
 2018-07-25  H.J. Lu  <hongjiu.lu@intel.com>
 
 	* stdlib/Makefile ((tests): Add tst-setcontext6, tst-setcontext7,
diff --git a/sysdeps/unix/sysv/linux/x86/sys/ucontext.h b/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
index afb7c181bf..7367726a50 100644
--- a/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
+++ b/sysdeps/unix/sysv/linux/x86/sys/ucontext.h
@@ -147,6 +147,7 @@ typedef struct ucontext_t
     mcontext_t uc_mcontext;
     sigset_t uc_sigmask;
     struct _libc_fpstate __fpregs_mem;
+    __extension__ unsigned long long int __ssp[4];
   } ucontext_t;
 
 #else /* !__x86_64__ */
@@ -251,6 +252,7 @@ typedef struct ucontext_t
     mcontext_t uc_mcontext;
     sigset_t uc_sigmask;
     struct _libc_fpstate __fpregs_mem;
+    unsigned long int __ssp[4];
   } ucontext_t;
 
 #endif /* !__x86_64__ */
diff --git a/sysdeps/unix/sysv/linux/x86_64/__start_context.S b/sysdeps/unix/sysv/linux/x86_64/__start_context.S
index 0bfde5fc31..87de0e5996 100644
--- a/sysdeps/unix/sysv/linux/x86_64/__start_context.S
+++ b/sysdeps/unix/sysv/linux/x86_64/__start_context.S
@@ -18,6 +18,80 @@
 
 #include <sysdep.h>
 
+#if SHSTK_ENABLED
+# include <asm/prctl.h>
+# include "ucontext_i.h"
+
+/* Use CALL to push __start_context onto the new stack as well as the new
+   shadow stack.  RDI points to ucontext:
+   Incoming:
+     __ssp[0]: The original caller's shadow stack pointer.
+     __ssp[1]: The size of the new shadow stack.
+     __ssp[2]: The size of the new shadow stack.
+   Outgoing:
+     __ssp[0]: The new shadow stack pointer.
+     __ssp[1]: The base address of the new shadow stack.
+     __ssp[2]: The size of the new shadow stack.
+ */
+
+ENTRY(__push___start_context)
+	/* Save the pointer to ucontext.  */
+	movq	%rdi, %r9
+	/* Get the original shadow stack pointer.  */
+	rdsspq	%r8
+	/* Save the original stack pointer.  */
+	movq	%rsp, %rdx
+	/* Load the top of the new stack into RSI.  */
+	movq 	oRSP(%rdi), %rsi
+	/* Add 8 bytes to RSI since CALL will push the 8-byte return
+	   address onto stack.  */
+	leaq	8(%rsi), %rsp
+	/* Allocate the new shadow stack.  The size of the new shadow
+	   stack is passed in __ssp[1].  */
+	lea	(oSSP + 8)(%rdi), %RSI_LP
+	movl	$ARCH_CET_ALLOC_SHSTK, %edi
+	movl	$__NR_arch_prctl, %eax
+	/* The new shadow stack base is returned in __ssp[1].  */
+	syscall
+	testq	%rax, %rax
+	jne	L(hlt)		/* This should never happen.  */
+
+	/* Get the size of the new shadow stack.  */
+	movq	8(%rsi), %rdi
+
+	/* Get the base address of the new shadow stack.  */
+	movq	(%rsi), %rsi
+
+	/* Use the restore stoken to restore the new shadow stack.  */
+	rstorssp -8(%rsi, %rdi)
+
+	/* Save the restore token on the original shadow stack.  */
+	saveprevssp
+
+	/* Push the address of "jmp __start_context" onto the new stack
+	   as well as the new shadow stack.  */
+	call	1f
+	jmp	__start_context
+1:
+
+	/* Get the new shadow stack pointer.  */
+	rdsspq	%rdi
+
+	/* Use the restore stoken to restore the original shadow stack.  */
+	rstorssp -8(%r8)
+
+	/* Save the restore token on the new shadow stack.  */
+	saveprevssp
+
+	/* Store the new shadow stack pointer in __ssp[0].  */
+	movq	%rdi, oSSP(%r9)
+
+	/* Restore the original stack.  */
+	mov	%rdx, %rsp
+	ret
+END(__push___start_context)
+#endif
+
 /* This is the helper code which gets called if a function which is
    registered with 'makecontext' returns.  In this case we have to
    install the context listed in the uc_link element of the context
@@ -45,5 +119,6 @@ ENTRY(__start_context)
 	call	HIDDEN_JUMPTARGET(exit)
 	/* The 'exit' call should never return.  In case it does cause
 	   the process to terminate.  */
+L(hlt):
 	hlt
 END(__start_context)
diff --git a/sysdeps/unix/sysv/linux/x86_64/getcontext.S b/sysdeps/unix/sysv/linux/x86_64/getcontext.S
index 33347bc02e..84b986ca98 100644
--- a/sysdeps/unix/sysv/linux/x86_64/getcontext.S
+++ b/sysdeps/unix/sysv/linux/x86_64/getcontext.S
@@ -18,6 +18,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <sysdep.h>
+#include <asm/prctl.h>
 
 #include "ucontext_i.h"
 
@@ -53,6 +54,55 @@ ENTRY(__getcontext)
 	leaq	8(%rsp), %rcx		/* Exclude the return address.  */
 	movq	%rcx, oRSP(%rdi)
 
+#if SHSTK_ENABLED
+	/* Check if shadow stack is enabled.  */
+	testl	$X86_FEATURE_1_SHSTK, %fs:FEATURE_1_OFFSET
+	jz	L(no_shstk)
+
+	/* Save RDI in RDX which won't be clobbered by syscall.  */
+	movq	%rdi, %rdx
+
+	xorl	%eax, %eax
+	cmpq	%fs:SSP_BASE_OFFSET, %rax
+	jnz	L(shadow_stack_bound_recorded)
+
+	/* Get the base address and size of the default shadow stack
+	   which must be the current shadow stack since nothing has
+	   been recorded yet.  */
+	sub	$24, %RSP_LP
+	mov	%RSP_LP, %RSI_LP
+	movl	$ARCH_CET_STATUS, %edi
+	movl	$__NR_arch_prctl, %eax
+	syscall
+	testq	%rax, %rax
+	jz	L(continue_no_err)
+
+	/* This should never happen.  */
+	hlt
+
+L(continue_no_err):
+	/* Record the base of the current shadow stack.  */
+	movq	8(%rsp), %rax
+	movq	%rax, %fs:SSP_BASE_OFFSET
+	add	$24, %RSP_LP
+
+	/* Restore RDI.  */
+	movq	%rdx, %rdi
+
+L(shadow_stack_bound_recorded):
+	/* Get the current shadow stack pointer.  */
+	rdsspq	%rax
+	/* NB: Save the caller's shadow stack so that we can jump back
+	   to the caller directly.  */
+	addq	$8, %rax
+	movq	%rax, oSSP(%rdx)
+
+	/* Save the current shadow stack base in ucontext.  */
+	movq	%fs:SSP_BASE_OFFSET, %rax
+	movq	%rax, (oSSP + 8)(%rdi)
+
+L(no_shstk):
+#endif
 	/* We have separate floating-point register content memory on the
 	   stack.  We use the __fpregs_mem block in the context.  Set the
 	   links up correctly.  */
diff --git a/sysdeps/unix/sysv/linux/x86_64/makecontext.c b/sysdeps/unix/sysv/linux/x86_64/makecontext.c
index 0d0802bf43..3eb4c59a5d 100644
--- a/sysdeps/unix/sysv/linux/x86_64/makecontext.c
+++ b/sysdeps/unix/sysv/linux/x86_64/makecontext.c
@@ -21,6 +21,11 @@
 #include <stdarg.h>
 #include <stdint.h>
 #include <ucontext.h>
+#if SHSTK_ENABLED
+# include <pthread.h>
+# include <libc-pointer-arith.h>
+# include <sys/prctl.h>
+#endif
 
 #include "ucontext_i.h"
 
@@ -52,6 +57,8 @@ void
 __makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
 {
   extern void __start_context (void) attribute_hidden;
+  extern void __push___start_context (ucontext_t *)
+    attribute_hidden;
   greg_t *sp;
   unsigned int idx_uc_link;
   va_list ap;
@@ -74,7 +81,36 @@ __makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
   ucp->uc_mcontext.gregs[REG_RSP] = (uintptr_t) sp;
 
   /* Setup stack.  */
-  sp[0] = (uintptr_t) &__start_context;
+#if SHSTK_ENABLED
+  struct pthread *self = THREAD_SELF;
+  unsigned int feature_1 = THREAD_GETMEM (self, header.feature_1);
+  /* NB: We must check feature_1 before accessing __ssp since caller
+	 may be compiled against ucontext_t without __ssp.  */
+  if ((feature_1 & X86_FEATURE_1_SHSTK) != 0)
+    {
+      /* Shadow stack is enabled.  We need to allocate a new shadow
+         stack.  */
+      unsigned long ssp_size = (((uintptr_t) sp
+				 - (uintptr_t) ucp->uc_stack.ss_sp)
+				>> STACK_SIZE_TO_SHADOW_STACK_SIZE_SHIFT);
+      /* Align shadow stack to 8 bytes.  */
+      ssp_size = ALIGN_UP (ssp_size, 8);
+
+      ucp->__ssp[1] = ssp_size;
+      ucp->__ssp[2] = ssp_size;
+
+      /* Call __push___start_context to allocate a new shadow stack,
+	 push __start_context onto the new stack as well as the new
+	 shadow stack.  NB: After __push___start_context returns,
+	   ucp->__ssp[0]: The new shadow stack pointer.
+	   ucp->__ssp[1]: The base address of the new shadow stack.
+	   ucp->__ssp[2]: The size of the new shadow stack.
+       */
+      __push___start_context (ucp);
+    }
+  else
+#endif
+    sp[0] = (uintptr_t) &__start_context;
   sp[idx_uc_link] = (uintptr_t) ucp->uc_link;
 
   va_start (ap, argc);
diff --git a/sysdeps/unix/sysv/linux/x86_64/setcontext.S b/sysdeps/unix/sysv/linux/x86_64/setcontext.S
index b42af8e291..0afdf8ce0a 100644
--- a/sysdeps/unix/sysv/linux/x86_64/setcontext.S
+++ b/sysdeps/unix/sysv/linux/x86_64/setcontext.S
@@ -18,6 +18,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <sysdep.h>
+#include <asm/prctl.h>
 
 #include "ucontext_i.h"
 
@@ -79,6 +80,97 @@ ENTRY(__setcontext)
 	movq	oR14(%rdx), %r14
 	movq	oR15(%rdx), %r15
 
+#if SHSTK_ENABLED
+	/* Check if shadow stack is enabled.  */
+	testl	$X86_FEATURE_1_SHSTK, %fs:FEATURE_1_OFFSET
+	jz	L(no_shstk)
+
+	/* If the base of the target shadow stack is the same as the
+	   base of the current shadow stack, we unwind the shadow
+	   stack.  Otherwise it is a stack switch and we look for a
+	   restore token.  */
+	movq	oSSP(%rdx), %rsi
+	movq	%rsi, %rdi
+
+	/* Get the base of the target shadow stack.  */
+	movq	(oSSP + 8)(%rdx), %rcx
+	cmpq	%fs:SSP_BASE_OFFSET, %rcx
+	je	L(unwind_shadow_stack)
+
+L(find_restore_token_loop):
+	/* Look for a restore token.  */
+	movq	-8(%rsi), %rax
+	andq	$-8, %rax
+	cmpq	%rsi, %rax
+	je	L(restore_shadow_stack)
+
+	/* Try the next slot.  */
+	subq	$8, %rsi
+	jmp	L(find_restore_token_loop)
+
+L(restore_shadow_stack):
+	/* Pop return address from the shadow stack since setcontext
+	   will not return.  */
+	movq	$1, %rax
+	incsspq	%rax
+
+	/* Use the restore stoken to restore the target shadow stack.  */
+	rstorssp -8(%rsi)
+
+	/* Save the restore token on the old shadow stack.  NB: This
+	   restore token may be checked by setcontext or swapcontext
+	   later.  */
+	saveprevssp
+
+	/* Record the new shadow stack base that was switched to.  */
+	movq	(oSSP + 8)(%rdx), %rax
+	movq	%rax, %fs:SSP_BASE_OFFSET
+
+L(unwind_shadow_stack):
+	rdsspq	%rcx
+	subq	%rdi, %rcx
+	je	L(skip_unwind_shadow_stack)
+	negq	%rcx
+	shrq	$3, %rcx
+	movl	$255, %esi
+L(loop):
+	cmpq	%rsi, %rcx
+	cmovb	%rcx, %rsi
+	incsspq	%rsi
+	subq	%rsi, %rcx
+	ja	L(loop)
+
+L(skip_unwind_shadow_stack):
+	movq	oRSI(%rdx), %rsi
+	movq	oRDI(%rdx), %rdi
+	movq	oRCX(%rdx), %rcx
+	movq	oR8(%rdx), %r8
+	movq	oR9(%rdx), %r9
+
+	/* Get the return address set with getcontext.  */
+	movq	oRIP(%rdx), %r10
+
+	/* Setup finally %rdx.  */
+	movq	oRDX(%rdx), %rdx
+
+	/* Check if return address is valid for the case when setcontext
+	   is invoked from __start_context with linked context.  */
+	rdsspq	%rax
+	cmpq	(%rax), %r10
+	/* Clear RAX to indicate success.  NB: Don't use xorl to keep
+	   EFLAGS for jne.  */
+	movl	$0, %eax
+	jne	L(jmp)
+	/* Return to the new context if return address valid.  */
+	pushq	%r10
+	ret
+
+L(jmp):
+	/* Jump to the new context directly.  */
+	jmp	*%r10
+
+L(no_shstk):
+#endif
 	/* The following ret should return to the address set with
 	getcontext.  Therefore push the address on the stack.  */
 	movq	oRIP(%rdx), %rcx
diff --git a/sysdeps/unix/sysv/linux/x86_64/swapcontext.S b/sysdeps/unix/sysv/linux/x86_64/swapcontext.S
index 1110c479fa..92d3371807 100644
--- a/sysdeps/unix/sysv/linux/x86_64/swapcontext.S
+++ b/sysdeps/unix/sysv/linux/x86_64/swapcontext.S
@@ -18,6 +18,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <sysdep.h>
+#include <asm/prctl.h>
 
 #include "ucontext_i.h"
 
@@ -67,6 +68,7 @@ ENTRY(__swapcontext)
 
 	/* The syscall destroys some registers, save them.  */
 	movq	%rsi, %r12
+	movq	%rdi, %r9
 
 	/* Save the current signal mask and install the new one with
 	   rt_sigprocmask (SIG_BLOCK, newset, oldset,_NSIG/8).  */
@@ -99,6 +101,133 @@ ENTRY(__swapcontext)
 	movq	oR14(%rdx), %r14
 	movq	oR15(%rdx), %r15
 
+#if SHSTK_ENABLED
+	/* Check if shadow stack is enabled.  */
+	testl	$X86_FEATURE_1_SHSTK, %fs:FEATURE_1_OFFSET
+	jz	L(no_shstk)
+
+	xorl	%eax, %eax
+	cmpq	%fs:SSP_BASE_OFFSET, %rax
+	jnz	L(shadow_stack_bound_recorded)
+
+	/* Get the base address and size of the default shadow stack
+	   which must be the current shadow stack since nothing has
+	   been recorded yet.  */
+	sub	$24, %RSP_LP
+	mov	%RSP_LP, %RSI_LP
+	movl	$ARCH_CET_STATUS, %edi
+	movl	$__NR_arch_prctl, %eax
+	syscall
+	testq	%rax, %rax
+	jz	L(continue_no_err)
+
+	/* This should never happen.  */
+	hlt
+
+L(continue_no_err):
+	/* Record the base of the current shadow stack.  */
+	movq	8(%rsp), %rax
+	movq	%rax, %fs:SSP_BASE_OFFSET
+	add	$24, %RSP_LP
+
+L(shadow_stack_bound_recorded):
+        /* If we unwind the stack, we can't undo stack unwinding.  Just
+	   save the target shadow stack pointer as the current shadow
+	   stack pointer.   */
+	movq	oSSP(%rdx), %rcx
+	movq	%rcx, oSSP(%r9)
+
+	/* Save the base of the current shadow stack.  */
+	movq	%fs:SSP_BASE_OFFSET, %rax
+	movq	%rax, (oSSP + 8)(%r9)
+
+	/* If the base of the target shadow stack is the same as the
+	   base of the current shadow stack, we unwind the shadow
+	   stack.  Otherwise it is a stack switch and we look for a
+	   restore token.  */
+	movq	oSSP(%rdx), %rsi
+	movq	%rsi, %rdi
+
+	/* Get the base of the target shadow stack.  */
+	movq	(oSSP + 8)(%rdx), %rcx
+	cmpq	%fs:SSP_BASE_OFFSET, %rcx
+	je	L(unwind_shadow_stack)
+
+L(find_restore_token_loop):
+	/* Look for a restore token.  */
+	movq	-8(%rsi), %rax
+	andq	$-8, %rax
+	cmpq	%rsi, %rax
+	je	L(restore_shadow_stack)
+
+	/* Try the next slot.  */
+	subq	$8, %rsi
+	jmp	L(find_restore_token_loop)
+
+L(restore_shadow_stack):
+        /* The target shadow stack will be restored.  Save the current
+	   shadow stack pointer.  */
+	rdsspq	%rcx
+	movq	%rcx, oSSP(%r9)
+
+	/* Restore the target shadow stack.  */
+	rstorssp -8(%rsi)
+
+	/* Save the restore token on the old shadow stack.  NB: This
+	   restore token may be checked by setcontext or swapcontext
+	   later.  */
+	saveprevssp
+
+	/* Record the new shadow stack base that was switched to.   */
+	movq	(oSSP + 8)(%rdx), %rax
+	movq	%rax, %fs:SSP_BASE_OFFSET
+
+L(unwind_shadow_stack):
+	rdsspq	%rcx
+	subq	%rdi, %rcx
+	je	L(skip_unwind_shadow_stack)
+	negq	%rcx
+	shrq	$3, %rcx
+	movl	$255, %esi
+L(loop):
+	cmpq	%rsi, %rcx
+	cmovb	%rcx, %rsi
+	incsspq	%rsi
+	subq	%rsi, %rcx
+	ja	L(loop)
+
+L(skip_unwind_shadow_stack):
+	/* Setup registers used for passing args.  */
+	movq	oRDI(%rdx), %rdi
+	movq	oRSI(%rdx), %rsi
+	movq	oRCX(%rdx), %rcx
+	movq	oR8(%rdx), %r8
+	movq	oR9(%rdx), %r9
+
+	/* Get the return address set with getcontext.  */
+	movq	oRIP(%rdx), %r10
+
+	/* Setup finally %rdx.  */
+	movq	oRDX(%rdx), %rdx
+
+	/* Check if return address is valid for the case when setcontext
+	   is invoked from __start_context with linked context.  */
+	rdsspq	%rax
+	cmpq	(%rax), %r10
+	/* Clear rax to indicate success.  NB: Don't use xorl to keep
+	   EFLAGS for jne.  */
+	movl	$0, %eax
+	jne	L(jmp)
+	/* Return to the new context if return address valid.  */
+	pushq	%r10
+	ret
+
+L(jmp):
+	/* Jump to the new context directly.  */
+	jmp	*%r10
+
+L(no_shstk):
+#endif
 	/* The following ret should return to the address set with
 	getcontext.  Therefore push the address on the stack.  */
 	movq	oRIP(%rdx), %rcx
diff --git a/sysdeps/unix/sysv/linux/x86_64/sysdep.h b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
index 1ef0f742ae..f07eb04962 100644
--- a/sysdeps/unix/sysv/linux/x86_64/sysdep.h
+++ b/sysdeps/unix/sysv/linux/x86_64/sysdep.h
@@ -423,4 +423,9 @@
 #undef LO_HI_LONG
 #define LO_HI_LONG(val) (val), 0
 
+/* Each shadow stack slot takes 8 bytes.  Assuming that each stack
+   frame takes 256 bytes, this is used to compute shadow stack size
+   from stack size.  */
+#define STACK_SIZE_TO_SHADOW_STACK_SIZE_SHIFT 5
+
 #endif /* linux/x86_64/sysdep.h */
diff --git a/sysdeps/unix/sysv/linux/x86_64/ucontext_i.sym b/sysdeps/unix/sysv/linux/x86_64/ucontext_i.sym
index af3e0e544b..c08b3b8b47 100644
--- a/sysdeps/unix/sysv/linux/x86_64/ucontext_i.sym
+++ b/sysdeps/unix/sysv/linux/x86_64/ucontext_i.sym
@@ -35,3 +35,4 @@ oFPREGS		mcontext (fpregs)
 oSIGMASK	ucontext (uc_sigmask)
 oFPREGSMEM	ucontext (__fpregs_mem)
 oMXCSR		ucontext (__fpregs_mem.mxcsr)
+oSSP		ucontext (__ssp)