coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] timeout: Ensure SIGCHLD signal is not blocked


From: Pádraig Brady
Subject: Re: [PATCH] timeout: Ensure SIGCHLD signal is not blocked
Date: Wed, 18 Oct 2017 19:45:29 -0700
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.8.0

On 18/10/17 09:47, Thomas Jarosch wrote:
> We inherit the signal mask from our parent process,
> therefore ensure SIGCHLD is not blocked.
> 
> If SIGCHLD is blocked, sigsuspend() won't be interrupted
> when the child process exits and we hang until the timeout (SIGALRM).
> 
> The issue has been found by an "autotest" testsuite
> doing high level tests of a custom distribution.
> One complex test case got stuck 100% of the time.
> 
> This fixes a regression from
> 
> commit 2f69dba5df8caaf9eda658c1808b1379e9949f22
> Author: Tobias Stoeckmann <address@hidden>
> Date:   Sun Feb 5 13:23:22 2017 +0100
> 
>     timeout: fix race possibly terminating wrong process
> 
> 
> Please CC: comments, I'm not subscribed to the list.
> 
> Signed-off-by: Thomas Jarosch <address@hidden>
> ---
>  src/timeout.c | 24 ++++++++++++++----------
>  1 file changed, 14 insertions(+), 10 deletions(-)
> 
> diff --git a/src/timeout.c b/src/timeout.c
> index a58b84f4e..aa2fff7d9 100644
> --- a/src/timeout.c
> +++ b/src/timeout.c
> @@ -323,6 +323,16 @@ parse_duration (const char* str)
>    return duration;
>  }
>  
> +static void
> +unblock_signal (int sig)
> +{
> +  sigset_t unblock_set;
> +  sigemptyset (&unblock_set);
> +  sigaddset (&unblock_set, sig);
> +  if (sigprocmask (SIG_UNBLOCK, &unblock_set, NULL) != 0)
> +    error (0, errno, _("warning: sigprocmask"));
> +}
> +
>  static void
>  install_sigchld (void)
>  {
> @@ -333,6 +343,10 @@ install_sigchld (void)
>                                   more likely to work cleanly.  */
>  
>    sigaction (SIGCHLD, &sa, NULL);
> +
> +  /* we inherit the signal mask from our parent process,
> +     ensure SIGCHLD is not blocked. */
> +  unblock_signal(SIGCHLD);
>  }
>  
>  static void
> @@ -369,16 +383,6 @@ block_cleanup (int sigterm, sigset_t *old_set)
>      error (0, errno, _("warning: sigprocmask"));
>  }
>  
> -static void
> -unblock_signal (int sig)
> -{
> -  sigset_t unblock_set;
> -  sigemptyset (&unblock_set);
> -  sigaddset (&unblock_set, sig);
> -  if (sigprocmask (SIG_UNBLOCK, &unblock_set, NULL) != 0)
> -    error (0, errno, _("warning: sigprocmask"));
> -}
> -
>  /* Try to disable core dumps for this process.
>     Return TRUE if successful, FALSE otherwise.  */
>  static bool
> 

Drats, right thanks.

Isn't there also a small race if SIGCHLD is received
after wait() returns but before the sigsuspend() runs?
I.E. would this also be an appropriate addition to your patch:

diff --git a/src/timeout.c b/src/timeout.c
index a58b84f..1a9fe50 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -352,10 +352,12 @@ install_cleanup (int sigterm)
   sigaction (sigterm, &sa, NULL); /* user specified termination signal.  */
 }

-/* Blocks all signals which were registered with cleanup
-   as signal handler.  Return original mask in OLD_SET.  */
+/* Block all signals which were registered with cleanup
+   as signal handler.  Also block SIGCHLD to ensure
+   sigsuspend() doesn't miss it.
+   Return original mask in OLD_SET.  */
 static void
-block_cleanup (int sigterm, sigset_t *old_set)
+block_cleanup_and_chld (int sigterm, sigset_t *old_set)
 {
   sigset_t block_set;
   sigemptyset (&block_set);
@@ -364,6 +366,7 @@ block_cleanup (int sigterm, sigset_t *old_set)
   sigaddset (&block_set, SIGQUIT);
   sigaddset (&block_set, SIGHUP);
   sigaddset (&block_set, SIGTERM);
+  sigaddset (&block_set, SIGCHLD);
   sigaddset (&block_set, sigterm);
   if (sigprocmask (SIG_BLOCK, &block_set, old_set) != 0)
     error (0, errno, _("warning: sigprocmask"));
@@ -504,7 +507,7 @@ main (int argc, char **argv)
       /* Ensure we don't cleanup() after waitpid() reaps the child,
          to avoid sending signals to a possibly different process.  */
       sigset_t cleanup_set;
-      block_cleanup (term_signal, &cleanup_set);
+      block_cleanup_and_chld (term_signal, &cleanup_set);

       while ((wait_result = waitpid (monitored_pid, &status, WNOHANG)) == 0)
         sigsuspend (&cleanup_set);  /* Wait with cleanup signals unblocked.  */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]