[PATCH] os: don't unconditionally unblock SIGIO in OsReleaseSignals()

Peter Hutterer peter.hutterer at who-t.net
Sat Aug 4 00:41:11 PDT 2012


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 at who-t.net>
---
 os/utils.c |    9 +++++----
 test/os.c  |   36 ++++++++++++++++++++++++++++++++++++
 2 files changed, 41 insertions(+), 4 deletions(-)

diff --git a/os/utils.c b/os/utils.c
index cb37162..afd50b3 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;
@@ -1222,7 +1223,7 @@ OsReleaseSIGIO(void)
 
         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;
diff --git a/test/os.c b/test/os.c
index 1460a34..8f1107d 100644
--- a/test/os.c
+++ b/test/os.c
@@ -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;
 }
-- 
1.7.10.4



More information about the xorg-devel mailing list