about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog13
-rw-r--r--sysdeps/unix/sysv/linux/i386/vfork.S57
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/vfork.S38
3 files changed, 108 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 2a1a53d9df..8b892eb686 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2018-07-24  H.J. Lu  <hongjiu.lu@intel.com>
 
+	* sysdeps/unix/sysv/linux/i386/vfork.S (SYSCALL_ERROR_HANDLER):
+	Redefine if shadow stack is enabled.
+	(SYSCALL_ERROR_LABEL): Likewise.
+	(__vfork): Pop shadow stack and jump back to to caller directly
+	when shadow stack is in use.
+	* sysdeps/unix/sysv/linux/x86_64/vfork.S (SYSCALL_ERROR_HANDLER):
+	Redefine if shadow stack is enabled.
+	(SYSCALL_ERROR_LABEL): Likewise.
+	(__vfork): Pop shadow stack and jump back to to caller directly
+	when shadow stack is in use.
+
+2018-07-24  H.J. Lu  <hongjiu.lu@intel.com>
+
 	* sysdeps/x86_64/tst-quadmod1.S (func): Add endbr64 if IBT is
 	enabled.
 	(foo): Likewise.
diff --git a/sysdeps/unix/sysv/linux/i386/vfork.S b/sysdeps/unix/sysv/linux/i386/vfork.S
index 8f40d02d09..ce6dbfac48 100644
--- a/sysdeps/unix/sysv/linux/i386/vfork.S
+++ b/sysdeps/unix/sysv/linux/i386/vfork.S
@@ -21,6 +21,38 @@
 #include <bits/errno.h>
 #include <tcb-offsets.h>
 
+#if SHSTK_ENABLED
+/* The shadow stack prevents us from pushing the saved return PC onto
+   the stack and returning normally.  Instead we pop the shadow stack
+   and return directly.  This is the safest way to return and ensures
+   any stack manipulations done by the vfork'd child doesn't cause the
+   parent to terminate when CET is enabled.  */
+# undef SYSCALL_ERROR_HANDLER
+# ifdef PIC
+#  define SYSCALL_ERROR_HANDLER				\
+0:							\
+  calll .L1;						\
+.L1:							\
+  popl %edx;						\
+.L2:							\
+  addl $_GLOBAL_OFFSET_TABLE_ + (.L2 - .L1), %edx;	\
+  movl __libc_errno@gotntpoff(%edx), %edx;		\
+  negl %eax;						\
+  movl %eax, %gs:(%edx);				\
+  orl $-1, %eax;					\
+  jmp 1b;
+# else
+#  define SYSCALL_ERROR_HANDLER				\
+0:							\
+  movl __libc_errno@indntpoff, %edx;			\
+  negl %eax;						\
+  movl %eax, %gs:(%edx);				\
+  orl $-1, %eax;					\
+  jmp 1b;
+# endif
+# undef SYSCALL_ERROR_LABEL
+# define SYSCALL_ERROR_LABEL 0f
+#endif
 
 /* Clone the calling process, but without copying the whole address space.
    The calling process is suspended until the new process exits or is
@@ -38,16 +70,41 @@ ENTRY (__vfork)
 	movl	$SYS_ify (vfork), %eax
 	int	$0x80
 
+#if !SHSTK_ENABLED
 	/* Jump to the return PC.  Don't jump directly since this
 	   disturbs the branch target cache.  Instead push the return
 	   address back on the stack.  */
 	pushl	%ecx
 	cfi_adjust_cfa_offset (4)
+#endif
 
 	cmpl	$-4095, %eax
 	/* Branch forward if it failed.  */
 	jae	SYSCALL_ERROR_LABEL
 
+#if SHSTK_ENABLED
+1:
+	/* Check if shadow stack is in use.  */
+	xorl	%edx, %edx
+	rdsspd	%edx
+	testl	%edx, %edx
+	/* Normal return if shadow stack isn't in use.  */
+	je	L(no_shstk)
+
+	/* Pop return address from shadow stack and jump back to caller
+	   directly.  */
+	movl	$1, %edx
+	incsspd	%edx
+	jmp	*%ecx
+
+L(no_shstk):
+	/* Jump to the return PC.  Don't jump directly since this
+	   disturbs the branch target cache.  Instead push the return
+	   address back on the stack.  */
+	pushl	%ecx
+	cfi_adjust_cfa_offset (4)
+#endif
+
 	ret
 
 PSEUDO_END (__vfork)
diff --git a/sysdeps/unix/sysv/linux/x86_64/vfork.S b/sysdeps/unix/sysv/linux/x86_64/vfork.S
index e4c8269e3d..8f1ca9f836 100644
--- a/sysdeps/unix/sysv/linux/x86_64/vfork.S
+++ b/sysdeps/unix/sysv/linux/x86_64/vfork.S
@@ -20,6 +20,21 @@
 #include <bits/errno.h>
 #include <tcb-offsets.h>
 
+#if SHSTK_ENABLED
+/* The shadow stack prevents us from pushing the saved return PC onto
+   the stack and returning normally.  Instead we pop the shadow stack
+   and return directly.  This is the safest way to return and ensures
+   any stack manipulations done by the vfork'd child doesn't cause the
+   parent to terminate when CET is enabled.  */
+# undef SYSCALL_ERROR_HANDLER
+# define SYSCALL_ERROR_HANDLER			\
+0:						\
+  SYSCALL_SET_ERRNO;				\
+  or $-1, %RAX_LP;				\
+  jmp 1b;
+# undef SYSCALL_ERROR_LABEL
+# define SYSCALL_ERROR_LABEL 0f
+#endif
 
 /* Clone the calling process, but without copying the whole address space.
    The calling process is suspended until the new process exits or is
@@ -38,13 +53,36 @@ ENTRY (__vfork)
 	movl	$SYS_ify (vfork), %eax
 	syscall
 
+#if !SHSTK_ENABLED
 	/* Push back the return PC.  */
 	pushq	%rdi
 	cfi_adjust_cfa_offset(8)
+#endif
 
 	cmpl	$-4095, %eax
 	jae SYSCALL_ERROR_LABEL		/* Branch forward if it failed.  */
 
+#if SHSTK_ENABLED
+1:
+	/* Check if shadow stack is in use.  */
+	xorl	%esi, %esi
+	rdsspq	%rsi
+	testq	%rsi, %rsi
+	/* Normal return if shadow stack isn't in use.  */
+	je	L(no_shstk)
+
+	/* Pop return address from shadow stack and jump back to caller
+	   directly.  */
+	movl	$1, %esi
+	incsspq	%rsi
+	jmp	*%rdi
+
+L(no_shstk):
+	/* Push back the return PC.  */
+	pushq	%rdi
+	cfi_adjust_cfa_offset(8)
+#endif
+
 	/* Normal return.  */
 	ret