diff mbox series

[v2,19/21] nptl: mips: Fix Race conditions in pthread cancellation (BZ#12683)

Message ID 1519679016-12241-20-git-send-email-adhemerval.zanella@linaro.org
State New
Headers show
Series nptl: Fix Race conditions in pthread cancellation (BZ#12683) | expand

Commit Message

Adhemerval Zanella Feb. 26, 2018, 9:03 p.m. UTC
This patch adds the mips modifications required for the BZ#12683.
It adds the required ucontext_get_pc function, a mips32 cancellable
syscall wrapper and 7 argument cancellable syscall support.

To avoid code pessimization and add a requirement on all architectures
to support {INLINE,INTERNAL)_SYSCALL with 7 argument, its support is
added through a flag, HAVE_CANCELABLE_SYSCALL_WITH_7_ARGS, which changes
the signature and prototype of the requires macros and functions
(SYSCALL_CANCEL, __syscall_cancel and __syscall_cancel_arch). As
default 6 arguments cancellable syscalls are use.

MIPS o32 requires an arch-specific implementation because
INTERNAL_SYSCALL_NCS adds an 'addiu' just after the syscall
instruction which invalidates the checks on sigcancel_handler.

Checked against a build and make check run-built-tests=no for
mips-gnu-linux, mips64-linux-gnu, mips64-n32-linux-gnu.  I also
ran some basic o32 and n64 cancellation tests on a simulated
mips64 qemu system.

	[BZ #12683]
	* nptl/libc-cancellation.c (__syscall_cancel): Define and use 7
	argument syscall if architecture requires it.
	* nptl/pthreadP.h (__syscall_cancel_arch): Likewise.
	* sysdeps/unix/sysdep.h (__syscall_cancel, __SYSCALL_CANCEL*): Define
	with 7 argument if architecture requires it.
	(__SYSCALL_CANCEL7_ARG_DEF): New macro.
	(__SYSCALL_CANCEL7_ARG): Likewise.
	(__SYSCALL_CANCEL7_ARG7): Likewise.
	* sysdeps/unix/sysv/linux/syscall_cancel.c (__syscall_cancel_arch):
	Likewise.
	* sysdeps/mips/nptl/tls.h (READ_THREAD_POINTER): Check __mips_isa_rev
	existance for macro definition.
	* sysdeps/unix/sysv/linux/mips/sigcontextinfo.h (ucontext_get_pc):
	New function.
	* sysdeps/unix/sysv/linux/mips/mips32/syscall_cancel.S: New file.
	* sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
	(HAVE_CANCELABLE_SYSCALL_WITH_7_ARGS): Define.
---
 ChangeLog                                          |  19 +++
 nptl/libc-cancellation.c                           |   7 +-
 nptl/pthreadP.h                                    |   3 +-
 sysdeps/mips/nptl/tls.h                            |   2 +-
 sysdeps/unix/sysdep.h                              |  38 ++++--
 .../unix/sysv/linux/mips/mips32/syscall_cancel.S   | 128 +++++++++++++++++++++
 sysdeps/unix/sysv/linux/mips/mips32/sysdep.h       |   4 +
 sysdeps/unix/sysv/linux/mips/sigcontextinfo.h      |  13 ++-
 sysdeps/unix/sysv/linux/syscall_cancel.c           |   6 +-
 9 files changed, 204 insertions(+), 16 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/mips/mips32/syscall_cancel.S

-- 
2.7.4
diff mbox series

Patch

diff --git a/nptl/libc-cancellation.c b/nptl/libc-cancellation.c
index 9b054aa..beb433c 100644
--- a/nptl/libc-cancellation.c
+++ b/nptl/libc-cancellation.c
@@ -25,7 +25,7 @@  long int
 __syscall_cancel (__syscall_arg_t nr, __syscall_arg_t a1,
 		  __syscall_arg_t a2, __syscall_arg_t a3,
 		  __syscall_arg_t a4, __syscall_arg_t a5,
-		  __syscall_arg_t a6)
+		  __syscall_arg_t a6 __SYSCALL_CANCEL7_ARG_DEF)
 {
   pthread_t self = (pthread_t) THREAD_SELF;
   volatile struct pthread *pd = (volatile struct pthread *) self;
@@ -35,7 +35,8 @@  __syscall_cancel (__syscall_arg_t nr, __syscall_arg_t a1,
   if (pd->cancelhandling & CANCELSTATE_BITMASK)
     {
       INTERNAL_SYSCALL_DECL (err);
-      result = INTERNAL_SYSCALL_NCS_CALL (nr, err, a1, a2, a3, a4, a5, a6);
+      result = INTERNAL_SYSCALL_NCS_CALL (nr, err, a1, a2, a3, a4, a5, a6
+					  __SYSCALL_CANCEL7_ARG7);
       if (INTERNAL_SYSCALL_ERROR_P (result, err))
 	return -INTERNAL_SYSCALL_ERRNO (result, err);
       return result;
@@ -44,7 +45,7 @@  __syscall_cancel (__syscall_arg_t nr, __syscall_arg_t a1,
   /* Call the arch-specific entry points that contains the globals markers
      to be checked by SIGCANCEL handler.  */
   result = __syscall_cancel_arch (&pd->cancelhandling, nr, a1, a2, a3, a4, a5,
-			          a6);
+			          a6 __SYSCALL_CANCEL7_ARG7);
 
   if ((result == -EINTR)
       && (pd->cancelhandling & CANCELED_BITMASK)
diff --git a/nptl/pthreadP.h b/nptl/pthreadP.h
index bb7ff81..448abea 100644
--- a/nptl/pthreadP.h
+++ b/nptl/pthreadP.h
@@ -317,7 +317,8 @@  __do_cancel (void)
 
 extern long int __syscall_cancel_arch (volatile int *, __syscall_arg_t nr,
      __syscall_arg_t arg1, __syscall_arg_t arg2, __syscall_arg_t arg3,
-     __syscall_arg_t arg4, __syscall_arg_t arg5, __syscall_arg_t arg6);
+     __syscall_arg_t arg4, __syscall_arg_t arg5, __syscall_arg_t arg6
+     __SYSCALL_CANCEL7_ARG_DEF);
 libc_hidden_proto (__syscall_cancel_arch);
 
 extern void __syscall_do_cancel (void)
diff --git a/sysdeps/mips/nptl/tls.h b/sysdeps/mips/nptl/tls.h
index 5f8cd21..d037cb8 100644
--- a/sysdeps/mips/nptl/tls.h
+++ b/sysdeps/mips/nptl/tls.h
@@ -35,7 +35,7 @@ 
 # define READ_THREAD_POINTER() (__builtin_thread_pointer ())
 #else
 /* Note: rd must be $v1 to be ABI-conformant.  */
-# if __mips_isa_rev >= 2
+# if defined __mips_isa_rev && __mips_isa_rev >= 2
 #  define READ_THREAD_POINTER() \
      ({ void *__result;							      \
         asm volatile ("rdhwr\t%0, $29" : "=v" (__result));	      	      \
diff --git a/sysdeps/unix/sysdep.h b/sysdeps/unix/sysdep.h
index cea81a5..42720c5 100644
--- a/sysdeps/unix/sysdep.h
+++ b/sysdeps/unix/sysdep.h
@@ -121,29 +121,51 @@  typedef long int __syscall_arg_t;
 # define __SSC(__x) ((__syscall_arg_t) (__x))
 #endif
 
+/* Adjust both the __syscall_cancel and the SYSCALL_CANCEL macro to support
+   7 arguments instead of default 6 (for some architectures like mip32).
+   We need it because using 7 arguments for all architecture would require
+   then to implement both {INTERNAL,INLINE}_SYSCALL and __syscall_cancel_arch
+   to accept 7 arguments.  */
+#ifdef HAVE_CANCELABLE_SYSCALL_WITH_7_ARGS
+# define __SYSCALL_CANCEL7_ARG_DEF 	, __syscall_arg_t arg7
+# define __SYSCALL_CANCEL7_ARG		, 0
+# define __SYSCALL_CANCEL7_ARG7		, arg7
+#else
+# define __SYSCALL_CANCEL7_ARG_DEF
+# define __SYSCALL_CANCEL7_ARG
+# define __SYSCALL_CANCEL7_ARG7
+#endif
+
 long int __syscall_cancel (__syscall_arg_t nr, __syscall_arg_t arg1,
 			   __syscall_arg_t arg2, __syscall_arg_t arg3,
 			   __syscall_arg_t arg4, __syscall_arg_t arg5,
-			   __syscall_arg_t arg6);
+			   __syscall_arg_t arg6 __SYSCALL_CANCEL7_ARG_DEF);
 libc_hidden_proto (__syscall_cancel);
 
 #define __SYSCALL_CANCEL0(name) \
-  (__syscall_cancel)(__NR_##name, 0, 0, 0, 0, 0, 0)
+  (__syscall_cancel)(__NR_##name, 0, 0, 0, 0, 0, 0 \
+		     __SYSCALL_CANCEL7_ARG)
 #define __SYSCALL_CANCEL1(name, a1) \
-  (__syscall_cancel)(__NR_##name, __SSC(a1), 0, 0, 0, 0, 0)
+  (__syscall_cancel)(__NR_##name, __SSC(a1), 0, 0, 0, 0, 0 \
+		     __SYSCALL_CANCEL7_ARG)
 #define __SYSCALL_CANCEL2(name, a1, a2) \
-  (__syscall_cancel)(__NR_##name, __SSC(a1), __SSC(a2), 0, 0, 0, 0)
+  (__syscall_cancel)(__NR_##name, __SSC(a1), __SSC(a2), 0, 0, 0, 0 \
+		     __SYSCALL_CANCEL7_ARG)
 #define __SYSCALL_CANCEL3(name, a1, a2, a3) \
-  (__syscall_cancel)(__NR_##name, __SSC(a1), __SSC(a2), __SSC(a3), 0, 0, 0)
+  (__syscall_cancel)(__NR_##name, __SSC(a1), __SSC(a2), __SSC(a3), 0, 0, 0 \
+		     __SYSCALL_CANCEL7_ARG)
 #define __SYSCALL_CANCEL4(name, a1, a2, a3, a4) \
   (__syscall_cancel)(__NR_##name, __SSC(a1), __SSC(a2), __SSC(a3), \
-		     __SSC(a4), 0, 0)
+		     __SSC(a4), 0, 0 __SYSCALL_CANCEL7_ARG)
 #define __SYSCALL_CANCEL5(name, a1, a2, a3, a4, a5) \
   (__syscall_cancel)(__NR_##name, __SSC(a1), __SSC(a2), __SSC(a3), \
-		     __SSC(a4), __SSC(a5), 0)
+		     __SSC(a4), __SSC(a5), 0 __SYSCALL_CANCEL7_ARG)
 #define __SYSCALL_CANCEL6(name, a1, a2, a3, a4, a5, a6) \
   (__syscall_cancel)(__NR_##name, __SSC(a1), __SSC(a2), __SSC(a3), \
-		     __SSC(a4), __SSC(a5), __SSC(a6))
+		     __SSC(a4), __SSC(a5), __SSC(a6) __SYSCALL_CANCEL7_ARG)
+#define __SYSCALL_CANCEL7(name, a1, a2, a3, a4, a5, a6, a7) \
+  (__syscall_cancel)(__NR_##name, __SSC(a1), __SSC(a2), __SSC(a3), \
+		     __SSC(a4), __SSC(a5), __SSC(a6), __SSC(a7))
 
 #define __SYSCALL_CANCEL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n
 #define __SYSCALL_CANCEL_NARGS(...) \
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/syscall_cancel.S b/sysdeps/unix/sysv/linux/mips/mips32/syscall_cancel.S
new file mode 100644
index 0000000..75c4390
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/mips/mips32/syscall_cancel.S
@@ -0,0 +1,128 @@ 
+/* Cancellable syscall wrapper.  Linux/mips32 version.
+   Copyright (C) 2017 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>
+#include <sys/asm.h>
+
+/* long int __syscall_cancel_arch (int *cancelhandling,
+				   __syscall_arg_t nr,
+				   __syscall_arg_t arg1,
+				   __syscall_arg_t arg2,
+				   __syscall_arg_t arg3,
+				   __syscall_arg_t arg4,
+				   __syscall_arg_t arg5,
+				   __syscall_arg_t arg6,
+				   __syscall_arg_t arg7)  */
+
+#define FRAME_SIZE 56
+
+NESTED (__syscall_cancel_arch, FRAME_SIZE, fp)
+	.mask	0xc0070000,-SZREG
+	.fmask	0x00000000,0
+
+	PTR_ADDIU sp, -FRAME_SIZE
+	cfi_def_cfa_offset (FRAME_SIZE)
+
+	sw	fp,48(sp)
+	sw	ra,52(sp)
+	sw	s2,44(sp)
+	sw	s1,40(sp)
+	sw	s0,36(sp)
+#ifdef __PIC__
+	.cprestore	16
+#endif
+	cfi_offset (31, -4)
+	cfi_offset (30, -8)
+	cfi_offset (18, -12)
+	cfi_offset (17, -16)
+	cfi_offset (16, -20)
+	move	fp,sp
+	cfi_def_cfa_register (30)
+
+	.globl __syscall_cancel_arch_start
+	.type __syscall_cancel_arch_start, @function
+__syscall_cancel_arch_start:
+
+	lw	v0,0(a0)
+	andi	v0,v0,0x4
+	bne	v0,zero,2f
+
+	addiu	sp,sp,-16
+	addiu	v0,sp,16
+	sw	v0,24(fp)
+
+	move	s0,a1
+	move	a0,a2
+	move	a1,a3
+	lw	a2,72(fp)
+	lw	a3,76(fp)
+	lw	v0,84(fp)
+	lw	s1,80(fp)
+	lw	s2,88(fp)
+
+	.set	noreorder
+	subu	sp, 32
+	sw	s1, 16(sp)
+	sw	v0, 20(sp)
+	sw	s2, 24(sp)
+	move	v0, $16
+	syscall
+
+	.globl __syscall_cancel_arch_end
+	.type __syscall_cancel_arch_end, @function
+__syscall_cancel_arch_end:
+	addiu	sp, 32
+	.set	reorder
+
+	beq	a3,zero,1f
+	subu	v0,zero,v0
+1:
+	move	sp,fp
+	cfi_remember_state
+	cfi_def_cfa_register (29)
+	lw	ra,52(fp)
+	lw	fp,48(sp)
+	lw	s2,44(sp)
+	lw	s1,40(sp)
+	lw	s0,36(sp)
+	.set	noreorder
+	.set	nomacro
+	jr	ra
+	addiu	sp,sp,FRAME_SIZE
+
+	.set	macro
+	.set	reorder
+
+	cfi_def_cfa_offset (0)
+	cfi_restore (16)
+	cfi_restore (17)
+	cfi_restore (18)
+	cfi_restore (30)
+	cfi_restore (31)
+
+2:
+	cfi_restore_state
+#ifdef __PIC__
+	PTR_LA	t9, __syscall_do_cancel
+	jalr	t9
+#else
+	jal	__syscall_do_cancel
+#endif
+
+END (__syscall_cancel_arch)
+libc_hidden_def (__syscall_cancel_arch)
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h b/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
index cbe9cd2..5417c43 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
+++ b/sysdeps/unix/sysv/linux/mips/mips32/sysdep.h
@@ -18,6 +18,10 @@ 
 #ifndef _LINUX_MIPS_MIPS32_SYSDEP_H
 #define _LINUX_MIPS_MIPS32_SYSDEP_H 1
 
+/* mips32 have cancelable syscalls with 7 arguments (currently only
+   sync_file_range).  */
+#define HAVE_CANCELABLE_SYSCALL_WITH_7_ARGS	1
+
 /* There is some commonality.  */
 #include <sysdeps/unix/sysv/linux/sysdep.h>
 #include <sysdeps/unix/mips/mips32/sysdep.h>
diff --git a/sysdeps/unix/sysv/linux/mips/sigcontextinfo.h b/sysdeps/unix/sysv/linux/mips/sigcontextinfo.h
index d183a33..7da55f5 100644
--- a/sysdeps/unix/sysv/linux/mips/sigcontextinfo.h
+++ b/sysdeps/unix/sysv/linux/mips/sigcontextinfo.h
@@ -16,7 +16,10 @@ 
    License along with the GNU C Library.  If not, see
    <http://www.gnu.org/licenses/>.  */
 
+#ifndef _SIGCONTEXTINFO_H
+#define _SIGCONTEXTINFO_H
 
+#include <stdint.h>
 #include <sgidefs.h>
 
 #if _MIPS_SIM == _ABIO32
@@ -39,4 +42,12 @@ 
 #define CALL_SIGHANDLER(handler, signo, ctx) \
   (handler)((signo), SIGCONTEXT_EXTRA_ARGS (ctx))
 
-#endif
+#endif /* _MIPS_SIM == _ABIO32  */
+
+static inline uintptr_t
+ucontext_get_pc (const ucontext_t *uc)
+{
+  return uc->uc_mcontext.pc;
+}
+
+#endif /* _SIGCONTEXTINFO_H  */
diff --git a/sysdeps/unix/sysv/linux/syscall_cancel.c b/sysdeps/unix/sysv/linux/syscall_cancel.c
index ac08bb7..5a3d383 100644
--- a/sysdeps/unix/sysv/linux/syscall_cancel.c
+++ b/sysdeps/unix/sysv/linux/syscall_cancel.c
@@ -46,14 +46,16 @@  long int
 __syscall_cancel_arch (volatile int *ch, __syscall_arg_t nr,
 		       __syscall_arg_t a1, __syscall_arg_t a2,
 		       __syscall_arg_t a3, __syscall_arg_t a4,
-		       __syscall_arg_t a5, __syscall_arg_t a6)
+		       __syscall_arg_t a5, __syscall_arg_t a6
+		       __SYSCALL_CANCEL7_ARG_DEF)
 {
   ADD_LABEL ("__syscall_cancel_arch_start");
   if (__glibc_unlikely (*ch & CANCELED_BITMASK))
     __syscall_do_cancel();
 
   INTERNAL_SYSCALL_DECL(err);
-  long int result = INTERNAL_SYSCALL_NCS (nr, err, 6, a1, a2, a3, a4, a5, a6);
+  long int result = INTERNAL_SYSCALL_NCS_CALL (nr, err, a1, a2, a3, a4, a5,
+					       a6 __SYSCALL_CANCEL7_ARG7);
   ADD_LABEL ("__syscall_cancel_arch_end");
   if (INTERNAL_SYSCALL_ERROR_P (result, err))
     return -INTERNAL_SYSCALL_ERRNO (result, err);