From 656769d136f84e3b4783b33f530757dee7c71da5 Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Thu, 15 Jan 2015 18:02:33 -0600 Subject: [PATCH] Work around GNO libc's buggy sigprocmask implementation by defining our own version of it. This should fix issues where hush could hang in wait() calls. The sigprocmask implementation in GNO 2.0.6 libc has bugs that cause it to never actually unblock signals that have been blocked. This was causing many signals to remain blocked when they shouldn't be, particularly after running signal handlers. This problem combined with a kernel issue (wait() calls won't recognize that a child has finished if SIGCHLD is blocked) to cause hangs in certain conditions. This patch works around these issues by defining our own non-buggy version of sigprocmask in place of libc's. --- libbb/signals.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/libbb/signals.c b/libbb/signals.c index 2364a77a8..fbd9e65e2 100644 --- a/libbb/signals.c +++ b/libbb/signals.c @@ -53,3 +53,38 @@ void FAST_FUNC kill_myself_with_sig(int sig) raise(sig); _exit(sig | 128); /* Should not reach it */ } + +#ifdef __GNO__ +/* Include our own version of sigprocmask, because the one in + * GNO 2.0.6 libc is broken for SIG_SETMASK and SIG_UNBLOCK cases + * (it will never unblock any signals). */ +int sigprocmask(int how, const sigset_t *set, sigset_t *oset) +{ + sigset_t oldmask; + + if (set) { + switch (how) { + case SIG_BLOCK: + oldmask = sigblock(*set); + break; + case SIG_SETMASK: + oldmask = sigsetmask(*set); + break; + case SIG_UNBLOCK: + oldmask = sigblock(0); + sigsetmask(oldmask & ~*set); + break; + default: + errno = EINVAL; + return -1; + } + } else if (oset) { + oldmask = sigblock(0); + } + + if (oset) + *oset = oldmask; + + return 0; +} +#endif