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