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