[06/08] nptl: i386: Fix Race conditions in pthread cancellation, (BZ#12683)

Message ID 558DABC8.6090904@linaro.org
State New
Headers show

Commit Message

Adhemerval Zanella June 26, 2015, 7:45 p.m.
This patch adds the i386 modifications required for the BZ#12683 fix.
It basically removes the enable_asynccancel/disable_asynccancel function
usage on code used on x86_64, provide a arch-specific symbol that
contains global markers to be used in SIGCANCEL handler.

--

	* sysdeps/i386/nptl/tls.h (THREAD_ATOMIC_BIT_SET): Remove macro.
	* sysdeps/unix/sysv/linux/i386/Makefile
	[$(subdir) = elf] (sysdep-rtld_routines): Add libc-do-syscall object.
	* sysdeps/unix/sysv/linux/i386/libc-cancellation.c: New file.
	* sysdeps/unix/sysv/linux/i386/lowlevellock.h (lll_wait_tid): Use
	cancellable futex syscall macro.
	* sysdeps/unix/sysv/linux/i386/syscall_cancel.S: New file.
	* sysdeps/unix/sysv/linux/i386/sysdep-cancel.h (PSEUDO): Redefine to
	call __syscall_cancel function for cancellable syscalls.
	* sysdeps/unix/sysv/linux/i386/sysdep.h (SYSCALL_CANCEL_ERROR): New
	define.
	(SYSCALL_CANCEL_ERRNO): Likewise.

--

Patch

diff --git a/sysdeps/i386/nptl/tls.h b/sysdeps/i386/nptl/tls.h
index 829cd3a..ffac3a8 100644
--- a/sysdeps/i386/nptl/tls.h
+++ b/sysdeps/i386/nptl/tls.h
@@ -395,17 +395,6 @@  tls_fill_user_desc (union user_desc_init *desc,
 	      abort (); })
 
 
-/* Atomic set bit.  */
-#define THREAD_ATOMIC_BIT_SET(descr, member, bit) \
-  (void) ({ if (sizeof ((descr)->member) == 4)				      \
-	      asm volatile (LOCK_PREFIX "orl %1, %%gs:%P0"		      \
-			    :: "i" (offsetof (struct pthread, member)),	      \
-			       "ir" (1 << (bit)));			      \
-	    else							      \
-	      /* Not necessary for other sizes in the moment.  */	      \
-	      abort (); })
-
-
 /* Call the user-provided thread function.  */
 #define CALL_THREAD_FCT(descr) \
   ({ void *__res;							      \
diff --git a/sysdeps/unix/sysv/linux/i386/Makefile b/sysdeps/unix/sysv/linux/i386/Makefile
index 80da593..4f83d08 100644
--- a/sysdeps/unix/sysv/linux/i386/Makefile
+++ b/sysdeps/unix/sysv/linux/i386/Makefile
@@ -6,6 +6,7 @@  sysdep_routines += ioperm iopl vm86
 endif
 
 ifeq ($(subdir),elf)
+sysdep-rtld_routines += libc-do-syscall
 sysdep-others += lddlibc4
 install-bin += lddlibc4
 endif
diff --git a/sysdeps/unix/sysv/linux/i386/libc-cancellation.c b/sysdeps/unix/sysv/linux/i386/libc-cancellation.c
new file mode 100644
index 0000000..ff07ea4
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/libc-cancellation.c
@@ -0,0 +1,46 @@ 
+/* i386 cancellation definitions.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+#define INTERNAL_SYSCALL_MAIN_6_NCS(name, err, arg1, arg2, arg3,	\
+				    arg4, arg5, arg6)			\
+  struct libc_do_syscall_args _xv =					\
+    {									\
+      (int) (arg1),							\
+      (int) (arg5),							\
+      (int) (arg6)							\
+    };									\
+    asm volatile (							\
+    "movl %1, %%eax\n\t"						\
+    "call __libc_do_syscall"						\
+    : "=a" (resultvar)							\
+    : "0" (name), "c" (arg2), "d" (arg3), "S" (arg4), "D" (&_xv)	\
+    : "memory", "cc")
+
+#undef INTERNAL_SYSCALL_NCS
+#define INTERNAL_SYSCALL_NCS(name, err, nr, arg1, arg2, arg3, arg4,	\
+			     arg5, arg6)				\
+  ({									\
+    unsigned int resultvar;						\
+    INTERNAL_SYSCALL_MAIN_6_NCS (name, err, arg1, arg2, arg3, arg4,	\
+				 arg5, arg6);				\
+    (int) resultvar;							\
+  })
+
+#include <nptl/libc-cancellation.c>
diff --git a/sysdeps/unix/sysv/linux/i386/lowlevellock.h b/sysdeps/unix/sysv/linux/i386/lowlevellock.h
index f57afc6..69fcaa4 100644
--- a/sysdeps/unix/sysv/linux/i386/lowlevellock.h
+++ b/sysdeps/unix/sysv/linux/i386/lowlevellock.h
@@ -287,7 +287,7 @@  extern int __lll_timedlock_elision (int *futex, short *adapt_count,
   do {					\
     __typeof (tid) __tid;		\
     while ((__tid = (tid)) != 0)	\
-      lll_futex_wait (&(tid), __tid, LLL_SHARED);\
+      lll_futex_wait_cancel (&(tid), __tid, LLL_SHARED);\
   } while (0)
 
 extern int __lll_timedwait_tid (int *tid, const struct timespec *abstime)
diff --git a/sysdeps/unix/sysv/linux/i386/syscall_cancel.S b/sysdeps/unix/sysv/linux/i386/syscall_cancel.S
new file mode 100644
index 0000000..1ff6288
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/i386/syscall_cancel.S
@@ -0,0 +1,105 @@ 
+/* Cancellable syscall wrapper - x86_64 version.
+   Copyright (C) 2015 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sysdep.h>
+
+/* long int [eax] __syscall_cancel_arch (int *cancelhandling [SP],
+					 long int nr   [SP+4],
+					 long int arg1 [SP+8],
+					 long int arg2 [SP+12],
+					 long int arg3 [SP+16],
+					 long int arg4 [SP+20],
+					 long int arg5 [SP+24],
+					 long int arg6 [SP+28])  */
+
+ENTRY (__syscall_cancel_arch)
+	pushl %ebp
+	cfi_def_cfa_offset (8)
+	cfi_offset (ebp, -8)
+	pushl %edi
+	cfi_def_cfa_offset (12)
+	cfi_offset (edi, -12)
+	pushl %esi
+	cfi_def_cfa_offset (16)
+	cfi_offset (esi, -16)
+	pushl %ebx
+	cfi_def_cfa_offset (20)
+	cfi_offset (ebx, -20)
+
+	.globl __syscall_cancel_arch_start
+	.type  __syscall_cancel_arch_start,@function
+__syscall_cancel_arch_start:
+
+	/* if (*cancelhandling & CANCELED_BITMASK)
+	     __syscall_do_cancel()  */
+	testb	$4, (%eax)
+	jne     1f
+
+	/* Issue a 6 argument syscall, the nr [%eax] being the syscall
+	   number.  */
+	movl    24(%esp), %eax
+	movl    28(%esp), %ebx
+	movl    32(%esp), %ecx
+	movl    36(%esp), %edx
+	movl    40(%esp), %esi
+	movl    44(%esp), %edi
+	movl    48(%esp), %ebp
+
+        /* It can not use the vDSO __kernel_vsyscall because the cancelable
+	   checks in libc-cancelation.c requires to know if the cancellation
+	   IP value that was trigger between the two
+	   __syscall_cancel_arch_{start,end} marks.  */
+	int	$128
+
+	.globl __syscall_cancel_arch_end
+	.type  __syscall_cancel_arch_end,@function
+__syscall_cancel_arch_end:
+
+	popl %ebx
+	cfi_restore (ebx)
+	cfi_def_cfa_offset (16)
+	popl %esi
+	cfi_restore (esi)
+	cfi_def_cfa_offset (12)
+	popl %edi
+	cfi_restore (edi)
+	cfi_def_cfa_offset (8)
+	popl %ebp
+	cfi_restore (ebp)
+	cfi_def_cfa_offset (4)
+        ret
+
+1:
+	/* Although the __syscall_do_cancel do not return, we need to stack
+	   being set correctly so exceptions work correctly.  */
+	popl %ebx
+	cfi_restore (ebx)
+	cfi_def_cfa_offset (16)
+	popl %esi
+	cfi_restore (esi)
+	cfi_def_cfa_offset (12)
+	popl %edi
+	cfi_restore (edi)
+	cfi_def_cfa_offset (8)
+	popl %ebp
+	cfi_restore (ebp)
+	cfi_def_cfa_offset (4)
+	jmp __syscall_do_cancel
+
+END (__syscall_cancel_arch)
+libc_hidden_def (__syscall_cancel_arch)
diff --git a/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h b/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
index 1f86dae..d8cdd27 100644
--- a/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
+++ b/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
@@ -24,112 +24,66 @@ 
 
 #if IS_IN (libc) || IS_IN (libpthread) || IS_IN (librt)
 
+# if IS_IN (libc)
+#  define JMP_STARTFUNC							      \
+    subl $16, %esp
+#  define JMP_SYSCALL_CANCEL						      \
+    HIDDEN_JUMPTARGET(__syscall_cancel)
+#  define JMP_ENDFUNC							      \
+    addl $44, %esp;							      \
+    cfi_def_cfa_offset (4)
+# else
+#  define JMP_STARTFUNC							      \
+    pushl %ebx;								      \
+    cfi_def_cfa_offset (8);						      \
+    cfi_offset (ebx, -8);						      \
+    SETUP_PIC_REG (bx);							      \
+    addl $_GLOBAL_OFFSET_TABLE_, %ebx;					      \
+    subl $12, %esp
+#  define JMP_SYSCALL_CANCEL \
+    __syscall_cancel@plt
+#  define JMP_ENDFUNC							      \
+    addl $40, %esp;							      \
+    cfi_def_cfa_offset (8);						      \
+    popl %ebx;								      \
+    cfi_restore (ebx);							      \
+    cfi_def_cfa_offset (4)
+# endif
+
 # undef PSEUDO
 # define PSEUDO(name, syscall_name, args)				      \
   .text;								      \
   ENTRY (name)								      \
     cmpl $0, %gs:MULTIPLE_THREADS_OFFSET;				      \
     jne L(pseudo_cancel);						      \
-  .type __##syscall_name##_nocancel,@function;				      \
-  .globl __##syscall_name##_nocancel;					      \
-  __##syscall_name##_nocancel:						      \
     DO_CALL (syscall_name, args);					      \
     cmpl $-4095, %eax;							      \
     jae SYSCALL_ERROR_LABEL;						      \
     ret;								      \
-  .size __##syscall_name##_nocancel,.-__##syscall_name##_nocancel;	      \
   L(pseudo_cancel):							      \
-    CENABLE								      \
-    SAVE_OLDTYPE_##args							      \
-    PUSHCARGS_##args							      \
-    DOCARGS_##args							      \
-    movl $SYS_ify (syscall_name), %eax;					      \
-    ENTER_KERNEL;							      \
-    POPCARGS_##args;							      \
-    POPSTATE_##args							      \
+    JMP_STARTFUNC;							      \
+    cfi_def_cfa_offset (20);						      \
+    pushl 40(%esp);							      \
+    cfi_def_cfa_offset (24);						      \
+    pushl 40(%esp);							      \
+    cfi_def_cfa_offset (28);						      \
+    pushl 40(%esp);							      \
+    cfi_def_cfa_offset (32);						      \
+    pushl 40(%esp);							      \
+    cfi_def_cfa_offset (36);						      \
+    pushl 40(%esp);							      \
+    cfi_def_cfa_offset (40);						      \
+    pushl 40(%esp);							      \
+    cfi_def_cfa_offset (44);						      \
+    pushl $SYS_ify (syscall_name);					      \
+    cfi_def_cfa_offset (48);						      \
+    call JMP_SYSCALL_CANCEL;						      \
+    JMP_ENDFUNC;							      \
     cmpl $-4095, %eax;							      \
     jae SYSCALL_ERROR_LABEL
 
-# define SAVE_OLDTYPE_0	movl %eax, %ecx;
-# define SAVE_OLDTYPE_1	SAVE_OLDTYPE_0
-# define SAVE_OLDTYPE_2	pushl %eax; cfi_adjust_cfa_offset (4);
-# define SAVE_OLDTYPE_3	SAVE_OLDTYPE_2
-# define SAVE_OLDTYPE_4	SAVE_OLDTYPE_2
-# define SAVE_OLDTYPE_5	SAVE_OLDTYPE_2
-# define SAVE_OLDTYPE_6	SAVE_OLDTYPE_2
-
-# define PUSHCARGS_0	/* No arguments to push.  */
-# define DOCARGS_0	/* No arguments to frob.  */
-# define POPCARGS_0	/* No arguments to pop.  */
-# define _PUSHCARGS_0	/* No arguments to push.  */
-# define _POPCARGS_0	/* No arguments to pop.  */
-
-# define PUSHCARGS_1	movl %ebx, %edx; cfi_register (ebx, edx); PUSHCARGS_0
-# define DOCARGS_1	_DOARGS_1 (4)
-# define POPCARGS_1	POPCARGS_0; movl %edx, %ebx; cfi_restore (ebx);
-# define _PUSHCARGS_1	pushl %ebx; cfi_adjust_cfa_offset (4); \
-			cfi_rel_offset (ebx, 0); _PUSHCARGS_0
-# define _POPCARGS_1	_POPCARGS_0; popl %ebx; \
-			cfi_adjust_cfa_offset (-4); cfi_restore (ebx);
-
-# define PUSHCARGS_2	PUSHCARGS_1
-# define DOCARGS_2	_DOARGS_2 (12)
-# define POPCARGS_2	POPCARGS_1
-# define _PUSHCARGS_2	_PUSHCARGS_1
-# define _POPCARGS_2	_POPCARGS_1
-
-# define PUSHCARGS_3	_PUSHCARGS_2
-# define DOCARGS_3	_DOARGS_3 (20)
-# define POPCARGS_3	_POPCARGS_3
-# define _PUSHCARGS_3	_PUSHCARGS_2
-# define _POPCARGS_3	_POPCARGS_2
-
-# define PUSHCARGS_4	_PUSHCARGS_4
-# define DOCARGS_4	_DOARGS_4 (28)
-# define POPCARGS_4	_POPCARGS_4
-# define _PUSHCARGS_4	pushl %esi; cfi_adjust_cfa_offset (4); \
-			cfi_rel_offset (esi, 0); _PUSHCARGS_3
-# define _POPCARGS_4	_POPCARGS_3; popl %esi; \
-			cfi_adjust_cfa_offset (-4); cfi_restore (esi);
-
-# define PUSHCARGS_5	_PUSHCARGS_5
-# define DOCARGS_5	_DOARGS_5 (36)
-# define POPCARGS_5	_POPCARGS_5
-# define _PUSHCARGS_5	pushl %edi; cfi_adjust_cfa_offset (4); \
-			cfi_rel_offset (edi, 0); _PUSHCARGS_4
-# define _POPCARGS_5	_POPCARGS_4; popl %edi; \
-			cfi_adjust_cfa_offset (-4); cfi_restore (edi);
-
-# define PUSHCARGS_6	_PUSHCARGS_6
-# define DOCARGS_6	_DOARGS_6 (44)
-# define POPCARGS_6	_POPCARGS_6
-# define _PUSHCARGS_6	pushl %ebp; cfi_adjust_cfa_offset (4); \
-			cfi_rel_offset (ebp, 0); _PUSHCARGS_5
-# define _POPCARGS_6	_POPCARGS_5; popl %ebp; \
-			cfi_adjust_cfa_offset (-4); cfi_restore (ebp);
-
-# if IS_IN (libpthread)
-#  define CENABLE	call __pthread_enable_asynccancel;
-#  define CDISABLE	call __pthread_disable_asynccancel
-# elif IS_IN (libc)
-#  define CENABLE	call __libc_enable_asynccancel;
-#  define CDISABLE	call __libc_disable_asynccancel
-# elif IS_IN (librt)
-#  define CENABLE	call __librt_enable_asynccancel;
-#  define CDISABLE	call __librt_disable_asynccancel
-# else
-#  error Unsupported library
-# endif
-# define POPSTATE_0 \
- pushl %eax; cfi_adjust_cfa_offset (4); movl %ecx, %eax; \
- CDISABLE; popl %eax; cfi_adjust_cfa_offset (-4);
-# define POPSTATE_1	POPSTATE_0
-# define POPSTATE_2	xchgl (%esp), %eax; CDISABLE; popl %eax; \
-			cfi_adjust_cfa_offset (-4);
-# define POPSTATE_3	POPSTATE_2
-# define POPSTATE_4	POPSTATE_3
-# define POPSTATE_5	POPSTATE_4
-# define POPSTATE_6	POPSTATE_5
+# undef PSEUDO_RET
+# define PSEUDO_RET
 
 # ifndef __ASSEMBLER__
 #  define SINGLE_THREAD_P \
@@ -150,4 +104,10 @@ 
 # define RTLD_SINGLE_THREAD_P \
   __builtin_expect (THREAD_GETMEM (THREAD_SELF, \
 				   header.multiple_threads) == 0, 1)
+
+static inline
+long int __pthread_get_ip (const ucontext_t *uc)
+{
+  return (long int)uc->uc_mcontext.gregs[REG_EIP];
+}
 #endif
diff --git a/sysdeps/unix/sysv/linux/i386/sysdep.h b/sysdeps/unix/sysv/linux/i386/sysdep.h
index d76aca5..10ae7e0 100644
--- a/sysdeps/unix/sysv/linux/i386/sysdep.h
+++ b/sysdeps/unix/sysv/linux/i386/sysdep.h
@@ -452,6 +452,14 @@  struct libc_do_syscall_args
 #undef INTERNAL_SYSCALL_ERRNO
 #define INTERNAL_SYSCALL_ERRNO(val, err)	(-(val))
 
+#undef SYSCALL_CANCEL_ERROR
+#define SYSCALL_CANCEL_ERROR(__val) \
+  ((unsigned int) (__val) >= 0xfffff001u)
+
+#undef SYSCALL_CANCEL_ERRNO
+#define SYSCALL_CANCEL_ERRNO(__val) \
+  (-(__val))
+
 #define LOADARGS_0
 #ifdef __PIC__
 # if defined I386_USE_SYSENTER && defined SHARED
diff --git a/sysdeps/unix/sysv/linux/socketcall.h b/sysdeps/unix/sysv/linux/socketcall.h
index 007acba..1bd29fe 100644
--- a/sysdeps/unix/sysv/linux/socketcall.h
+++ b/sysdeps/unix/sysv/linux/socketcall.h
@@ -86,13 +86,35 @@ 
     sc_ret;								\
   })
 
-#define SOCKETCALL_CANCEL(name, __a1, __a2, __a3, __a4, __a5, __a6)	\
+
+#define __SOCKETCALL_CANCEL1(__name, __a1) \
+  SYSCALL_CANCEL_NCS (socketcall, __name, \
+     ((long int [1]) { (long int) __a1 }))
+#define __SOCKETCALL_CANCEL2(__name, __a1, __a2) \
+  SYSCALL_CANCEL_NCS (socketcall, __name, \
+     ((long int [2]) { (long int) __a1, (long int) __a2 }))
+#define __SOCKETCALL_CANCEL3(__name, __a1, __a2, __a3) \
+  SYSCALL_CANCEL_NCS (socketcall, __name, \
+     ((long int [3]) { (long int) __a1, (long int) __a2, (long int) __a3 }))
+#define __SOCKETCALL_CANCEL4(__name, __a1, __a2, __a3, __a4) \
+  SYSCALL_CANCEL_NCS (socketcall, __name, \
+     ((long int [4]) { (long int) __a1, (long int) __a2, (long int) __a3, \
+                       (long int) __a4 }))
+#define __SOCKETCALL_CANCEL5(__name, __a1, __a2, __a3, __a4, __a5) \
+  SYSCALL_CANCEL_NCS (socketcall, __name, \
+     ((long int [5]) { (long int) __a1, (long int) __a2, (long int) __a3, \
+                       (long int) __a4, (long int) __a5 }))
+#define __SOCKETCALL_CANCEL6(__name, __a1, __a2, __a3, __a4, __a5, __a6) \
+  SYSCALL_CANCEL_NCS (socketcall, __name, \
+     ((long int [6]) { (long int) __a1, (long int) __a2, (long int) __a3, \
+                       (long int) __a4, (long int) __a5, (long int) __a6 }))
+
+#define __SOCKETCALL_CANCEL(...) __SOCKETCALL_DISP (__SOCKETCALL_CANCEL,\
+						    __VA_ARGS__)
+
+#define SOCKETCALL_CANCEL(name, args...)				\
   ({									\
-    __syscall_arg_t __args[6] = { __SSC (__a1), __SSC (__a2),		\
-				  __SSC (__a3), __SSC (__a4), 		\
-				  __SSC (__a5), __SSC (__a6) };		\
-    long int sc_ret = SYSCALL_CANCEL_NCS (socketcall, SOCKOP_##name,	\
-					  __args);			\
+    long int sc_ret = __SOCKETCALL_CANCEL (SOCKOP_##name, args);	\
     if (SYSCALL_CANCEL_ERROR (sc_ret))					\
       {									\
         __set_errno (SYSCALL_CANCEL_ERRNO (sc_ret));			\