diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2012-08-03 15:36:34 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2012-08-07 09:39:56 +1000 |
commit | 7f09126e068015db54c56bb982b8f91065375700 (patch) | |
tree | c933156e2632b185211e2de5109d92dbcc1b576b | |
parent | cb306a8f174bec9ded95191b91797f59250e6808 (diff) |
os: don't unconditionally unblock SIGIO in OsReleaseSignals()
Calling OsReleaseSignal() inside the signal handler releases SIGIO, causing
the signal handler to be called again from within the handler.
Practical use-case: when synaptics calls TimerSet in the signal handler,
this causes the signals to be released, eventually hanging the server.
Regression introduced in 08962951de.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Reviewed-by: Jeremy Huddleston <jeremyhu@apple.com>
-rw-r--r-- | os/utils.c | 13 | ||||
-rw-r--r-- | test/os.c | 36 |
2 files changed, 41 insertions, 8 deletions
diff --git a/os/utils.c b/os/utils.c index cdf5fd82e..947f8673a 100644 --- a/os/utils.c +++ b/os/utils.c @@ -1186,6 +1186,7 @@ OsBlockSignals(void) #ifdef SIG_BLOCK static sig_atomic_t sigio_blocked; +static sigset_t PreviousSigIOMask; #endif /** @@ -1198,13 +1199,13 @@ OsBlockSIGIO(void) #ifdef SIGIO #ifdef SIG_BLOCK if (sigio_blocked++ == 0) { - sigset_t set, old; + sigset_t set; int ret; sigemptyset(&set); sigaddset(&set, SIGIO); - sigprocmask(SIG_BLOCK, &set, &old); - ret = sigismember(&old, SIGIO); + sigprocmask(SIG_BLOCK, &set, &PreviousSigIOMask); + ret = sigismember(&PreviousSigIOMask, SIGIO); return ret; } else return 1; @@ -1218,11 +1219,7 @@ OsReleaseSIGIO(void) #ifdef SIGIO #ifdef SIG_BLOCK if (--sigio_blocked == 0) { - sigset_t set; - - sigemptyset(&set); - sigaddset(&set, SIGIO); - sigprocmask(SIG_UNBLOCK, &set, NULL); + sigprocmask(SIG_SETMASK, &PreviousSigIOMask, 0); } else if (sigio_blocked < 0) { BUG_WARN(sigio_blocked < 0); sigio_blocked = 0; @@ -28,6 +28,21 @@ #include <signal.h> #include "os.h" +static int last_signal = 0; +static int expect_signal = 0; + +static void sighandler(int signal) +{ + assert(expect_signal); + expect_signal = 0; + if (!last_signal) + raise(signal); + OsBlockSignals(); + OsReleaseSignals(); + last_signal = 1; + expect_signal = 1; +} + static int sig_is_blocked(int sig) { @@ -118,7 +133,27 @@ static void block_sigio_test(void) assert(sig_is_blocked(SIGIO)); OsReleaseSignals(); assert(!sig_is_blocked(SIGIO)); +#endif +} +static void block_sigio_test_nested(void) +{ +#ifdef SIG_BLOCK + /* Check for bug releasing SIGIO during SIGIO signal handling. + test case: + raise signal + → in signal handler: + raise signal + OsBlockSignals() + OsReleaseSignals() + tail guard + tail guard must be hit. + */ + sighandler_t old_handler; + old_handler = signal(SIGIO, sighandler); + expect_signal = 1; + assert(raise(SIGIO) == 0); + assert(signal(SIGIO, old_handler) == sighandler); #endif } @@ -126,5 +161,6 @@ int main(int argc, char **argv) { block_sigio_test(); + block_sigio_test_nested(); return 0; } |