From e4a21c89a8b24b32f7a2637b45522dfa59f2aaa4 Mon Sep 17 00:00:00 2001 From: Bruce Beare Date: Mon, 5 Dec 2011 11:25:37 -0800 Subject: [PATCH] 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 Signed-off-by: Bruce Beare --- libc/bionic/pthread.c | 45 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/libc/bionic/pthread.c b/libc/bionic/pthread.c index e8f1052f2..3435d219c 100644 --- a/libc/bionic/pthread.c +++ b/libc/bionic/pthread.c @@ -1861,7 +1861,21 @@ int pthread_kill(pthread_t tid, int sig) 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 pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) */ 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; }