diff mbox

[10/13] nptl: aarch64: Fix Race conditions in pthread cancellation (BZ#12683)

Message ID 1444234995-9542-11-git-send-email-adhemerval.zanella@linaro.com
State Dropped
Headers show

Commit Message

Adhemerval Zanella Oct. 7, 2015, 4:23 p.m. UTC
From: Adhemerval Zanella <adhemerval.zanella@linaro.org>

This patch adds the aarch64 modifications required for the BZ#12683 fix.
It basically removes the enable_asynccancel/disable_asynccancel function
usage on code, provide a arch-specific symbol that contains global
markers to be used in SIGCANCEL handler.

Checked on aarch64.

	* sysdeps/unix/sysv/linux/aarch64/syscall_cancel.S: New file.
	* sysdeps/unix/sysv/linux/aarch64/sysdep-cancel.h (PSEUDO): Redefine
	to call __syscall_cancel function for cancellable syscalls.
	(__pthread_get_ip): Add implementation.
	* sysdeps/unix/sysv/linux/aarch64/sysdep.h (SYSCALL_CANCEL_ERROR): Add
	definition.
	(SYSCALL_CANCEL_ERRNO): Likewise.
---
 ChangeLog                                        |  8 ++
 sysdeps/unix/sysv/linux/aarch64/syscall_cancel.S | 61 +++++++++++++++
 sysdeps/unix/sysv/linux/aarch64/sysdep-cancel.h  | 95 ++++++++++--------------
 sysdeps/unix/sysv/linux/aarch64/sysdep.h         |  8 ++
 4 files changed, 116 insertions(+), 56 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/aarch64/syscall_cancel.S
diff mbox

Patch

diff --git a/ChangeLog b/ChangeLog
index 43e32b4..9b0f0ee 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@ 
 2015-10-07  Adhemerval Zanella  <adhemerval.zanella@linaro.org>
 
+	* sysdeps/unix/sysv/linux/aarch64/syscall_cancel.S: New file.
+	* sysdeps/unix/sysv/linux/aarch64/sysdep-cancel.h (PSEUDO): Redefine
+	to call __syscall_cancel function for cancellable syscalls.
+	(__pthread_get_ip): Add implementation.
+	* sysdeps/unix/sysv/linux/aarch64/sysdep.h (SYSCALL_CANCEL_ERROR): Add
+	definition.
+	(SYSCALL_CANCEL_ERRNO): Likewise.
+
 	* sysdeps/unix/sysv/linux/i386/fcntl.c (NO_CANCELLATION): Replace
 	by IS_IN (rtld).
 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/sysdep-cancel.h (PSEUDO):
diff --git a/sysdeps/unix/sysv/linux/aarch64/syscall_cancel.S b/sysdeps/unix/sysv/linux/aarch64/syscall_cancel.S
new file mode 100644
index 0000000..56ede8e
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/aarch64/syscall_cancel.S
@@ -0,0 +1,61 @@ 
+/* Cancellable syscall wrapper - aarch64 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 [r0] __syscall_cancel_arch (int *cancelhandling [x0],
+					long int nr   [x1],
+					long int arg1 [x2],
+					long int arg2 [x3],
+					long int arg3 [x4],
+					long int arg4 [x5],
+					long int arg5 [x6],
+					long int arg6 [x7])  */
+
+ENTRY (__syscall_cancel_arch)
+
+	.globl __syscall_cancel_arch_start
+	.type  __syscall_cancel_arch_start,@function
+__syscall_cancel_arch_start:
+
+	/* if (*cancelhandling & CANCELED_BITMASK)
+	     __syscall_do_cancel()  */
+	ldr	w0, [x0]
+	tbnz    w0, 2, 1f
+
+	/* Issue a 6 argument syscall, the nr [x1] being the syscall
+	   number.  */
+	mov	x8, x1
+	mov	x0, x2
+	mov	x1, x3
+	mov	x2, x4
+	mov	x3, x5
+	mov	x4, x6
+	mov	x5, x7
+	svc	0x0
+
+	.globl __syscall_cancel_arch_end
+	.type  __syscall_cancel_arch_end,@function
+__syscall_cancel_arch_end:
+	ret
+
+1:
+	b	__syscall_do_cancel
+
+END (__syscall_cancel_arch)
+libc_hidden_def (__syscall_cancel_arch)
diff --git a/sysdeps/unix/sysv/linux/aarch64/sysdep-cancel.h b/sysdeps/unix/sysv/linux/aarch64/sysdep-cancel.h
index 36e8e39..71207b3 100644
--- a/sysdeps/unix/sysv/linux/aarch64/sysdep-cancel.h
+++ b/sysdeps/unix/sysv/linux/aarch64/sysdep-cancel.h
@@ -20,80 +20,57 @@ 
 #include <tls.h>
 #ifndef __ASSEMBLER__
 # include <nptl/pthreadP.h>
+# include <sys/ucontext.h>
 #endif
 
 #if IS_IN (libc) || IS_IN (libpthread) || IS_IN (librt)
 
+# if IS_IN (libc)
+#  define JMP_SYSCALL_CANCEL HIDDEN_JUMPTARGET(__syscall_cancel)
+# else
+#  define JMP_SYSCALL_CANCEL __syscall_cancel
+# endif
+
 # undef PSEUDO
 # define PSEUDO(name, syscall_name, args)				\
-	.section ".text";						\
-ENTRY (__##syscall_name##_nocancel);					\
-.Lpseudo_nocancel:							\
-	DO_CALL (syscall_name, args);					\
-.Lpseudo_finish:							\
-	cmn	x0, 4095;						\
-	b.cs	.Lsyscall_error;					\
-	.subsection 2;							\
-	.size __##syscall_name##_nocancel,.-__##syscall_name##_nocancel; \
 ENTRY (name);								\
 	SINGLE_THREAD_P(16);						\
-	cbz	w16, .Lpseudo_nocancel;					\
-	/* Setup common stack frame no matter the number of args.	\
-	   Also save the first arg, since it's basically free.  */	\
-	stp	x30, x0, [sp, -64]!;					\
-	cfi_adjust_cfa_offset (64);					\
-	cfi_rel_offset (x30, 0);					\
-	DOCARGS_##args;		/* save syscall args around CENABLE.  */ \
-	CENABLE;							\
-	mov	x16, x0;	/* save mask around syscall.  */	\
-	UNDOCARGS_##args;	/* restore syscall args.  */		\
+	cbnz	w16, L(pseudo_cancel);					\
 	DO_CALL (syscall_name, args);					\
-	str	x0, [sp, 8];	/* save result around CDISABLE.  */	\
-	mov	x0, x16;	/* restore mask for CDISABLE.  */	\
-	CDISABLE;							\
-	/* Break down the stack frame, restoring result at once.  */	\
-	ldp	x30, x0, [sp], 64;					\
-	cfi_adjust_cfa_offset (-64);					\
-	cfi_restore (x30);						\
-	b	.Lpseudo_finish;					\
-	cfi_endproc;							\
-	.size	name, .-name;						\
-	.previous
+	b	L(pseudo_finish);					\
+L(pseudo_cancel):							\
+	stp     x29, x30, [sp, -16]!;					\
+	cfi_def_cfa_offset (16);					\
+	cfi_offset (29, -16);						\
+	cfi_offset (30, -8);						\
+	add	x29, sp, 0;						\
+	cfi_def_cfa_register (29);					\
+	mov	x6, x5;							\
+	mov	x5, x4;							\
+	mov	x4, x3;							\
+	mov	x3, x2;							\
+	mov	x2, x1;							\
+	mov	x1, x0;							\
+	mov	x0, SYS_ify (syscall_name);				\
+	bl	JMP_SYSCALL_CANCEL;					\
+	ldp     x29, x30, [sp], 16;					\
+	cfi_restore (30);                                               \
+	cfi_restore (29);						\
+	cfi_def_cfa (31, 0);						\
+L(pseudo_finish):							\
+        cmn     x0, 4095;                                               \
+        b.cs    L(syscall_error);
 
 # undef PSEUDO_END
 # define PSEUDO_END(name)						\
 	SYSCALL_ERROR_HANDLER;						\
-	cfi_endproc
-
-# define DOCARGS_0
-# define DOCARGS_1
-# define DOCARGS_2	str x1, [sp, 16]
-# define DOCARGS_3	stp x1, x2, [sp, 16]
-# define DOCARGS_4	DOCARGS_3; str x3, [sp, 32]
-# define DOCARGS_5	DOCARGS_3; stp x3, x4, [sp, 32]
-# define DOCARGS_6	DOCARGS_5; str x5, [sp, 48]
-
-# define UNDOCARGS_0
-# define UNDOCARGS_1	ldr x0, [sp, 8]
-# define UNDOCARGS_2	ldp x0, x1, [sp, 8]
-# define UNDOCARGS_3	UNDOCARGS_1; ldp x1, x2, [sp, 16]
-# define UNDOCARGS_4	UNDOCARGS_2; ldp x2, x3, [sp, 24]
-# define UNDOCARGS_5	UNDOCARGS_3; ldp x3, x4, [sp, 32]
-# define UNDOCARGS_6	UNDOCARGS_4; ldp x4, x5, [sp, 40]
+	cfi_endproc;							\
+	.size	name, .-name;
 
 # if IS_IN (libpthread)
-#  define CENABLE	bl __pthread_enable_asynccancel
-#  define CDISABLE	bl __pthread_disable_asynccancel
 #  define __local_multiple_threads __pthread_multiple_threads
 # elif IS_IN (libc)
-#  define CENABLE	bl __libc_enable_asynccancel
-#  define CDISABLE	bl __libc_disable_asynccancel
 #  define __local_multiple_threads __libc_multiple_threads
-# elif IS_IN (librt)
-#  define CENABLE	bl __librt_enable_asynccancel
-#  define CDISABLE	bl __librt_disable_asynccancel
-# else
-#  error Unsupported library
 # endif
 
 # if IS_IN (libpthread) || IS_IN (libc)
@@ -131,4 +108,10 @@  extern int __local_multiple_threads attribute_hidden;
 # define RTLD_SINGLE_THREAD_P \
   __builtin_expect (THREAD_GETMEM (THREAD_SELF, \
 				   header.multiple_threads) == 0, 1)
+
+static inline
+long int __pthread_get_ip (const struct ucontext *uc)
+{
+  return uc->uc_mcontext.pc;
+}
 #endif
diff --git a/sysdeps/unix/sysv/linux/aarch64/sysdep.h b/sysdeps/unix/sysv/linux/aarch64/sysdep.h
index fe94a50..5f73b9e 100644
--- a/sysdeps/unix/sysv/linux/aarch64/sysdep.h
+++ b/sysdeps/unix/sysv/linux/aarch64/sysdep.h
@@ -199,6 +199,14 @@ 
 # undef INTERNAL_SYSCALL_ERRNO
 # define INTERNAL_SYSCALL_ERRNO(val, err)	(-(val))
 
+#undef SYSCALL_CANCEL_ERROR
+#define SYSCALL_CANCEL_ERROR(__val) \
+  ((unsigned long) (__val) >= (unsigned long) -4095)
+
+#undef SYSCALL_CANCEL_ERRNO
+#define SYSCALL_CANCEL_ERRNO(__val) \
+  (-(__val))
+
 # define LOAD_ARGS_0()				\
   register long _x0 asm ("x0");
 # define LOAD_ARGS_1(x0)			\