kernel - Fix signal masking race assertion panic w/vkernel
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 1 Apr 2012 01:30:13 +0000 (18:30 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 1 Apr 2012 01:30:13 +0000 (18:30 -0700)
* sigsuspend() and pselect() record the old signal mask in order to
  allow an interrupting signal to run its handler before the old mask
  is restored.

* When multiple threads are present a race can ensue where another thread
  changes the signal handler after sigsuspend() or pselect() have interrupted,
  but before they are able to process the signal.

* If the signal is no longer enabled the old signal mask is not restored
  on system call return, resulting in an assertion and panic.

* Fix the problem by checking the flag and restoring the old signal mask
  on return (rather than asserting when the flag is found to be non-zero
  on return).

Reported-by: Venkatesh Srinivas
sys/platform/pc32/i386/trap.c
sys/platform/pc64/x86_64/trap.c
sys/platform/vkernel/i386/trap.c
sys/platform/vkernel64/x86_64/trap.c

index 2885a5f..fa3b218 100644 (file)
@@ -322,10 +322,16 @@ recheck:
        }
 
        /*
-        * Make sure postsig() handled request to restore old signal mask after
-        * running signal handler.
+        * In a multi-threaded program it is possible for a thread to change
+        * signal state during a system call which temporarily changes the
+        * signal mask.  In this case postsig() might not be run and we
+        * have to restore the mask ourselves.
         */
-       KKASSERT((lp->lwp_flags & LWP_OLDMASK) == 0);
+       if (lp->lwp_flags & LWP_OLDMASK) {
+               lp->lwp_flags &= ~LWP_OLDMASK;
+               lp->lwp_sigmask = lp->lwp_oldsigmask;
+               goto recheck;
+       }
 }
 
 /*
index a2fc611..fb4245c 100644 (file)
@@ -309,10 +309,16 @@ recheck:
        }
 
        /*
-        * Make sure postsig() handled request to restore old signal mask after
-        * running signal handler.
+        * In a multi-threaded program it is possible for a thread to change
+        * signal state during a system call which temporarily changes the
+        * signal mask.  In this case postsig() might not be run and we
+        * have to restore the mask ourselves.
         */
-       KKASSERT((lp->lwp_flags & LWP_OLDMASK) == 0);
+       if (lp->lwp_flags & LWP_OLDMASK) {
+               lp->lwp_flags &= ~LWP_OLDMASK;
+               lp->lwp_sigmask = lp->lwp_oldsigmask;
+               goto recheck;
+       }
 }
 
 /*
index d8e5124..0367345 100644 (file)
@@ -296,10 +296,16 @@ recheck:
        }
 
        /*
-        * Make sure postsig() handled request to restore old signal mask after
-        * running signal handler.
+        * In a multi-threaded program it is possible for a thread to change
+        * signal state during a system call which temporarily changes the
+        * signal mask.  In this case postsig() might not be run and we
+        * have to restore the mask ourselves.
         */
-       KKASSERT((lp->lwp_flags & LWP_OLDMASK) == 0);
+       if (lp->lwp_flags & LWP_OLDMASK) {
+               lp->lwp_flags &= ~LWP_OLDMASK;
+               lp->lwp_sigmask = lp->lwp_oldsigmask;
+               goto recheck;
+       }
 }
 
 /*
index 5c558a1..a0cd718 100644 (file)
@@ -296,10 +296,16 @@ recheck:
        }
 
        /*
-        * Make sure postsig() handled request to restore old signal mask after
-        * running signal handler.
+        * In a multi-threaded program it is possible for a thread to change
+        * signal state during a system call which temporarily changes the
+        * signal mask.  In this case postsig() might not be run and we
+        * have to restore the mask ourselves.
         */
-       KKASSERT((lp->lwp_flags & LWP_OLDMASK) == 0);
+       if (lp->lwp_flags & LWP_OLDMASK) {
+               lp->lwp_flags &= ~LWP_OLDMASK;
+               lp->lwp_sigmask = lp->lwp_oldsigmask;
+               goto recheck;
+       }
 }
 
 /*