signal: Align the sigset_t size passed to from user space to kernel.

Pass kernel space sigset_t size to __rt_sigprocmask to workaround
the miss-match of NSIG/sigset_t definition between kernel and bionic.

Note: Patch originally from Google...
Change-Id: I4840fdc56d0b90d7ce2334250f04a84caffcba2a
Signed-off-by: Chenyang Du <chenyang.du@intel.com>
Signed-off-by: Bruce Beare <bruce.j.beare@intel.com>
diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c
index e8f1052..3435d21 100644
--- a/libc/bionic/pthread.c
+++ b/libc/bionic/pthread.c
@@ -1861,7 +1861,21 @@
     return ret;
 }
 
-extern int __rt_sigprocmask(int, const sigset_t *, sigset_t *, size_t);
+/* Despite the fact that our kernel headers define sigset_t explicitly
+ * as a 32-bit integer, the kernel system call really expects a 64-bit
+ * bitmap for the signal set, or more exactly an array of two-32-bit
+ * values (see $KERNEL/arch/$ARCH/include/asm/signal.h for details).
+ *
+ * Unfortunately, we cannot fix the sigset_t definition without breaking
+ * the C library ABI, so perform a little runtime translation here.
+ */
+typedef union {
+    sigset_t   bionic;
+    uint32_t   kernel[2];
+} kernel_sigset_t;
+
+/* this is a private syscall stub */
+extern int __rt_sigprocmask(int, const kernel_sigset_t *, kernel_sigset_t *, size_t);
 
 int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
 {
@@ -1870,16 +1884,31 @@
      */
     int ret, old_errno = errno;
 
-    /* Use NSIG which corresponds to the number of signals in
-     * our 32-bit sigset_t implementation. As such, this function, or
-     * anything that deals with sigset_t cannot manage real-time signals
-     * (signo >= 32). We might want to introduce sigset_rt_t as an
-     * extension to do so in the future.
-     */
-    ret = __rt_sigprocmask(how, set, oset, NSIG / 8);
+    /* We must convert *set into a kernel_sigset_t */
+    kernel_sigset_t  in_set, *in_set_ptr;
+    kernel_sigset_t  out_set;
+
+    in_set.kernel[0] = in_set.kernel[1] = 0;
+    out_set.kernel[0] = out_set.kernel[1] = 0;
+
+    /* 'in_set_ptr' is the second parameter to __rt_sigprocmask. It must be NULL
+     * if 'set' is NULL to ensure correct semantics (which in this case would
+     * be to ignore 'how' and return the current signal set into 'oset'.
+      */
+    if (set == NULL) {
+        in_set_ptr = NULL;
+    } else {
+        in_set.bionic = *set;
+        in_set_ptr = &in_set;
+    }
+
+    ret = __rt_sigprocmask(how, in_set_ptr, &out_set, sizeof(kernel_sigset_t));
     if (ret < 0)
         ret = errno;
 
+    if (oset)
+        *oset = out_set.bionic;
+
     errno = old_errno;
     return ret;
 }