446cf2
commit c02695d776406faaf63418e4e80c4a7023af0b4f
446cf2
Author: H.J. Lu <hjl.tools@gmail.com>
446cf2
Date:   Wed Sep 16 16:00:14 2020 -0700
446cf2
446cf2
    x86/CET: Update vfork to prevent child return
446cf2
    
446cf2
    Child of vfork should either call _exit or one of the exec family of
446cf2
    functions.  But normally there is nothing to prevent child of vfork from
446cf2
    return of the vfork-calling function.  Simpilfy x86 vfork when shadow
446cf2
    stack is in use to introduce mismatched shadow stack in child of vfork
446cf2
    to trigger SIGSEGV when the child returns from the function in which
446cf2
    vfork was called.
446cf2
---
446cf2
446cf2
diff --git a/sysdeps/unix/sysv/linux/i386/vfork.S b/sysdeps/unix/sysv/linux/i386/vfork.S
446cf2
index ceb41db0bd..91277a639f 100644
446cf2
--- a/sysdeps/unix/sysv/linux/i386/vfork.S
446cf2
+++ b/sysdeps/unix/sysv/linux/i386/vfork.S
446cf2
@@ -21,39 +21,6 @@
446cf2
 #include <bits/errno.h>
446cf2
 #include <tcb-offsets.h>
446cf2
 
446cf2
-#if SHSTK_ENABLED
446cf2
-/* The shadow stack prevents us from pushing the saved return PC onto
446cf2
-   the stack and returning normally.  Instead we pop the shadow stack
446cf2
-   and return directly.  This is the safest way to return and ensures
446cf2
-   any stack manipulations done by the vfork'd child doesn't cause the
446cf2
-   parent to terminate when CET is enabled.  */
446cf2
-# undef SYSCALL_ERROR_HANDLER
446cf2
-# ifdef PIC
446cf2
-#  define SYSCALL_ERROR_HANDLER				\
446cf2
-0:							\
446cf2
-  calll .L1;						\
446cf2
-.L1:							\
446cf2
-  popl %edx;						\
446cf2
-.L2:							\
446cf2
-  addl $_GLOBAL_OFFSET_TABLE_ + (.L2 - .L1), %edx;	\
446cf2
-  movl __libc_errno@gotntpoff(%edx), %edx;		\
446cf2
-  negl %eax;						\
446cf2
-  movl %eax, %gs:(%edx);				\
446cf2
-  orl $-1, %eax;					\
446cf2
-  jmp 1b;
446cf2
-# else
446cf2
-#  define SYSCALL_ERROR_HANDLER				\
446cf2
-0:							\
446cf2
-  movl __libc_errno@indntpoff, %edx;			\
446cf2
-  negl %eax;						\
446cf2
-  movl %eax, %gs:(%edx);				\
446cf2
-  orl $-1, %eax;					\
446cf2
-  jmp 1b;
446cf2
-# endif
446cf2
-# undef SYSCALL_ERROR_LABEL
446cf2
-# define SYSCALL_ERROR_LABEL 0f
446cf2
-#endif
446cf2
-
446cf2
 /* Clone the calling process, but without copying the whole address space.
446cf2
    The calling process is suspended until the new process exits or is
446cf2
    replaced by a call to `execve'.  Return -1 for errors, 0 to the new process,
446cf2
@@ -70,20 +37,17 @@ ENTRY (__vfork)
446cf2
 	movl	$SYS_ify (vfork), %eax
446cf2
 	int	$0x80
446cf2
 
446cf2
-#if !SHSTK_ENABLED
446cf2
 	/* Jump to the return PC.  Don't jump directly since this
446cf2
 	   disturbs the branch target cache.  Instead push the return
446cf2
 	   address back on the stack.  */
446cf2
 	pushl	%ecx
446cf2
 	cfi_adjust_cfa_offset (4)
446cf2
-#endif
446cf2
 
446cf2
 	cmpl	$-4095, %eax
446cf2
 	/* Branch forward if it failed.  */
446cf2
 	jae	SYSCALL_ERROR_LABEL
446cf2
 
446cf2
 #if SHSTK_ENABLED
446cf2
-1:
446cf2
 	/* Check if shadow stack is in use.  */
446cf2
 	xorl	%edx, %edx
446cf2
 	rdsspd	%edx
446cf2
@@ -91,18 +55,19 @@ ENTRY (__vfork)
446cf2
 	/* Normal return if shadow stack isn't in use.  */
446cf2
 	je	L(no_shstk)
446cf2
 
446cf2
-	/* Pop return address from shadow stack and jump back to caller
446cf2
-	   directly.  */
446cf2
-	movl	$1, %edx
446cf2
-	incsspd	%edx
446cf2
+	testl	%eax, %eax
446cf2
+	/* In parent, normal return.  */
446cf2
+	jnz	L(no_shstk)
446cf2
+
446cf2
+	/* NB: In child, jump back to caller via indirect branch without
446cf2
+	   popping shadow stack which is shared with parent.  Keep shadow
446cf2
+	   stack mismatched so that child returns in the vfork-calling
446cf2
+	   function will trigger SIGSEGV.  */
446cf2
+	popl	%ecx
446cf2
+	cfi_adjust_cfa_offset (-4)
446cf2
 	jmp	*%ecx
446cf2
 
446cf2
 L(no_shstk):
446cf2
-	/* Jump to the return PC.  Don't jump directly since this
446cf2
-	   disturbs the branch target cache.  Instead push the return
446cf2
-	   address back on the stack.  */
446cf2
-	pushl	%ecx
446cf2
-	cfi_adjust_cfa_offset (4)
446cf2
 #endif
446cf2
 
446cf2
 	ret
446cf2
diff --git a/sysdeps/unix/sysv/linux/x86/Makefile b/sysdeps/unix/sysv/linux/x86/Makefile
446cf2
index 50fd018fa3..6bfd6bec49 100644
446cf2
--- a/sysdeps/unix/sysv/linux/x86/Makefile
446cf2
+++ b/sysdeps/unix/sysv/linux/x86/Makefile
446cf2
@@ -40,6 +40,11 @@ $(objpfx)tst-cet-property-2.out: $(objpfx)tst-cet-property-2 \
446cf2
 	  $(evaluate-test)
446cf2
 endif
446cf2
 
446cf2
+ifeq ($(subdir),posix)
446cf2
+tests += tst-cet-vfork-1
446cf2
+CFLAGS-tst-cet-vfork-1.c += -mshstk
446cf2
+endif
446cf2
+
446cf2
 ifeq ($(subdir),stdlib)
446cf2
 tests += tst-cet-setcontext-1
446cf2
 CFLAGS-tst-cet-setcontext-1.c += -mshstk
446cf2
diff --git a/sysdeps/unix/sysv/linux/x86/tst-cet-vfork-1.c b/sysdeps/unix/sysv/linux/x86/tst-cet-vfork-1.c
446cf2
new file mode 100644
446cf2
index 0000000000..5b9fc8c170
446cf2
--- /dev/null
446cf2
+++ b/sysdeps/unix/sysv/linux/x86/tst-cet-vfork-1.c
446cf2
@@ -0,0 +1,88 @@
446cf2
+/* Verify that child of the vfork-calling function can't return when
446cf2
+   shadow stack is in use.
446cf2
+   Copyright (C) 2020 Free Software Foundation, Inc.
446cf2
+   This file is part of the GNU C Library.
446cf2
+
446cf2
+   The GNU C Library is free software; you can redistribute it and/or
446cf2
+   modify it under the terms of the GNU Lesser General Public
446cf2
+   License as published by the Free Software Foundation; either
446cf2
+   version 2.1 of the License, or (at your option) any later version.
446cf2
+
446cf2
+   The GNU C Library is distributed in the hope that it will be useful,
446cf2
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
446cf2
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
446cf2
+   Lesser General Public License for more details.
446cf2
+
446cf2
+   You should have received a copy of the GNU Lesser General Public
446cf2
+   License along with the GNU C Library; if not, see
446cf2
+   <https://www.gnu.org/licenses/>.  */
446cf2
+
446cf2
+#include <stdio.h>
446cf2
+#include <stdlib.h>
446cf2
+#include <unistd.h>
446cf2
+#include <errno.h>
446cf2
+#include <sys/types.h>
446cf2
+#include <sys/wait.h>
446cf2
+#include <x86intrin.h>
446cf2
+#include <support/test-driver.h>
446cf2
+#include <support/xsignal.h>
446cf2
+#include <support/xunistd.h>
446cf2
+
446cf2
+__attribute__ ((noclone, noinline))
446cf2
+static void
446cf2
+do_test_1 (void)
446cf2
+{
446cf2
+  pid_t p1;
446cf2
+  int fd[2];
446cf2
+
446cf2
+  if (pipe (fd) == -1)
446cf2
+    {
446cf2
+      puts ("pipe failed");
446cf2
+      _exit (EXIT_FAILURE);
446cf2
+    }
446cf2
+
446cf2
+  if ((p1 = vfork ()) == 0)
446cf2
+    {
446cf2
+      pid_t p = getpid ();
446cf2
+      TEMP_FAILURE_RETRY (write (fd[1], &p, sizeof (p)));
446cf2
+      /* Child return should trigger SIGSEGV.  */
446cf2
+      return;
446cf2
+    }
446cf2
+  else if (p1 == -1)
446cf2
+    {
446cf2
+      puts ("vfork failed");
446cf2
+      _exit (EXIT_FAILURE);
446cf2
+    }
446cf2
+
446cf2
+  pid_t p2 = 0;
446cf2
+  if (TEMP_FAILURE_RETRY (read (fd[0], &p2, sizeof (pid_t)))
446cf2
+      != sizeof (pid_t))
446cf2
+    puts ("pipd read failed");
446cf2
+  else
446cf2
+    {
446cf2
+      int r;
446cf2
+      if (TEMP_FAILURE_RETRY (waitpid (p1, &r, 0)) != p1)
446cf2
+	puts ("waitpid failed");
446cf2
+      else if (r != 0)
446cf2
+	puts ("pip write in child failed");
446cf2
+    }
446cf2
+
446cf2
+  /* Parent exits immediately so that parent returns without triggering
446cf2
+     SIGSEGV when shadow stack isn't in use.  */
446cf2
+  _exit (EXIT_FAILURE);
446cf2
+}
446cf2
+
446cf2
+static int
446cf2
+do_test (void)
446cf2
+{
446cf2
+  /* NB: This test should trigger SIGSEGV with shadow stack enabled.  */
446cf2
+  if (_get_ssp () == 0)
446cf2
+    return EXIT_UNSUPPORTED;
446cf2
+  do_test_1 ();
446cf2
+  /* Child exits immediately so that child returns without triggering
446cf2
+     SIGSEGV when shadow stack isn't in use.  */
446cf2
+  _exit (EXIT_FAILURE);
446cf2
+}
446cf2
+
446cf2
+#define EXPECTED_SIGNAL (_get_ssp () == 0 ? 0 : SIGSEGV)
446cf2
+#include <support/test-driver.c>
446cf2
diff --git a/sysdeps/unix/sysv/linux/x86_64/vfork.S b/sysdeps/unix/sysv/linux/x86_64/vfork.S
446cf2
index 776d2fc610..613ff7e846 100644
446cf2
--- a/sysdeps/unix/sysv/linux/x86_64/vfork.S
446cf2
+++ b/sysdeps/unix/sysv/linux/x86_64/vfork.S
446cf2
@@ -20,22 +20,6 @@
446cf2
 #include <bits/errno.h>
446cf2
 #include <tcb-offsets.h>
446cf2
 
446cf2
-#if SHSTK_ENABLED
446cf2
-/* The shadow stack prevents us from pushing the saved return PC onto
446cf2
-   the stack and returning normally.  Instead we pop the shadow stack
446cf2
-   and return directly.  This is the safest way to return and ensures
446cf2
-   any stack manipulations done by the vfork'd child doesn't cause the
446cf2
-   parent to terminate when CET is enabled.  */
446cf2
-# undef SYSCALL_ERROR_HANDLER
446cf2
-# define SYSCALL_ERROR_HANDLER			\
446cf2
-0:						\
446cf2
-  SYSCALL_SET_ERRNO;				\
446cf2
-  or $-1, %RAX_LP;				\
446cf2
-  jmp 1b;
446cf2
-# undef SYSCALL_ERROR_LABEL
446cf2
-# define SYSCALL_ERROR_LABEL 0f
446cf2
-#endif
446cf2
-
446cf2
 /* Clone the calling process, but without copying the whole address space.
446cf2
    The calling process is suspended until the new process exits or is
446cf2
    replaced by a call to `execve'.  Return -1 for errors, 0 to the new process,
446cf2
@@ -53,17 +37,14 @@ ENTRY (__vfork)
446cf2
 	movl	$SYS_ify (vfork), %eax
446cf2
 	syscall
446cf2
 
446cf2
-#if !SHSTK_ENABLED
446cf2
 	/* Push back the return PC.  */
446cf2
 	pushq	%rdi
446cf2
 	cfi_adjust_cfa_offset(8)
446cf2
-#endif
446cf2
 
446cf2
 	cmpl	$-4095, %eax
446cf2
 	jae SYSCALL_ERROR_LABEL		/* Branch forward if it failed.  */
446cf2
 
446cf2
 #if SHSTK_ENABLED
446cf2
-1:
446cf2
 	/* Check if shadow stack is in use.  */
446cf2
 	xorl	%esi, %esi
446cf2
 	rdsspq	%rsi
446cf2
@@ -71,16 +52,19 @@ ENTRY (__vfork)
446cf2
 	/* Normal return if shadow stack isn't in use.  */
446cf2
 	je	L(no_shstk)
446cf2
 
446cf2
-	/* Pop return address from shadow stack and jump back to caller
446cf2
-	   directly.  */
446cf2
-	movl	$1, %esi
446cf2
-	incsspq	%rsi
446cf2
+	testl	%eax, %eax
446cf2
+	/* In parent, normal return.  */
446cf2
+	jnz	L(no_shstk)
446cf2
+
446cf2
+	/* NB: In child, jump back to caller via indirect branch without
446cf2
+	   popping shadow stack which is shared with parent.  Keep shadow
446cf2
+	   stack mismatched so that child returns in the vfork-calling
446cf2
+	   function will trigger SIGSEGV.  */
446cf2
+	popq	%rdi
446cf2
+	cfi_adjust_cfa_offset(-8)
446cf2
 	jmp	*%rdi
446cf2
 
446cf2
 L(no_shstk):
446cf2
-	/* Push back the return PC.  */
446cf2
-	pushq	%rdi
446cf2
-	cfi_adjust_cfa_offset(8)
446cf2
 #endif
446cf2
 
446cf2
 	/* Normal return.  */
446cf2
-- 
446cf2
2.26.2
446cf2