[ovs-dev] [PATCHv2] process: block signals while spawning child processes

Ansis Atteka aatteka at nicira.com
Wed May 28 21:40:23 UTC 2014


Between fork() and execvp() calls in the process_start()
function both child and parent processes share the same
file descriptors.  This means that, if a child process
received a signal during this time interval, then it could
potentially write data to a shared file descriptor.

One such example is fatal signal handler, where, if
child process received SIGTERM signal, then it would
write data into pipe.  Then a read event would occur
on the other end of the pipe where parent process is
listening and this would make parent process to incorrectly
believe that it was the one who received SIGTERM.
Also, since parent process never reads data from this
pipe, then this bug would make parent process to consume
100% CPU by immediately waking up from the event loop.

This patch will help to avoid this problem by blocking
signals until child closes all its file descriptors.

Signed-off-by: Ansis Atteka <aatteka at nicira.com>
Reported-by: Suganya Ramachandran <suganyar at vmware.com>
Issue: 1255110
---
 lib/fatal-signal.c | 20 ++++++++++++++++++++
 lib/fatal-signal.h |  7 +++++++
 lib/process.c      | 25 ++++++++++++++++++++++---
 3 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/lib/fatal-signal.c b/lib/fatal-signal.c
index ef3fbc0..9536984 100644
--- a/lib/fatal-signal.c
+++ b/lib/fatal-signal.c
@@ -374,3 +374,23 @@ fatal_signal_fork(void)
         raise(stored_sig_nr);
     }
 }
+
+#ifndef _WIN32
+/* Blocks all fatal signals and returns previous signal mask into
+ * 'prev_mask'.
+ *
+ * Returns 0 if successful, otherwise a positive errno value. */
+int
+fatal_signal_block(sigset_t *prev_mask)
+{
+    int i;
+    sigset_t block_mask;
+
+    sigemptyset(&block_mask);
+    for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) {
+        int sig_nr = fatal_signals[i];
+        sigaddset(&block_mask, sig_nr);
+    }
+    return pthread_sigmask(SIG_BLOCK, &block_mask, prev_mask);
+}
+#endif
diff --git a/lib/fatal-signal.h b/lib/fatal-signal.h
index caf24ec..a10413d 100644
--- a/lib/fatal-signal.h
+++ b/lib/fatal-signal.h
@@ -17,6 +17,9 @@
 #ifndef FATAL_SIGNAL_H
 #define FATAL_SIGNAL_H 1
 
+#ifndef _WIN32
+#include <signal.h>
+#endif
 #include <stdbool.h>
 
 /* Basic interface. */
@@ -41,4 +44,8 @@ int fatal_signal_unlink_file_now(const char *);
  * it through. */
 void fatal_signal_handler(int sig_nr);
 
+#ifndef _WIN32
+int fatal_signal_block(sigset_t *prev_mask);
+#endif
+
 #endif /* fatal-signal.h */
diff --git a/lib/process.c b/lib/process.c
index 313f11f..cc6bd8c 100644
--- a/lib/process.c
+++ b/lib/process.c
@@ -226,7 +226,8 @@ process_start(char **argv, struct process **pp)
 {
 #ifndef _WIN32
     pid_t pid;
-    int error;
+    int error, error2;
+    sigset_t prev_mask;
 
     assert_single_threaded();
 
@@ -237,14 +238,20 @@ process_start(char **argv, struct process **pp)
         return error;
     }
 
+    error = fatal_signal_block(&prev_mask);
+    if (error) {
+        VLOG_ERR("failed to block fatal signals before fork: %s",
+                 ovs_strerror(error));
+        return error;
+    }
     pid = fork();
     if (pid < 0) {
         VLOG_WARN("fork failed: %s", ovs_strerror(errno));
-        return errno;
+        error = errno;
     } else if (pid) {
         /* Running in parent process. */
         *pp = process_register(argv[0], pid);
-        return 0;
+        error = 0;
     } else {
         /* Running in child process. */
         int fd_max = get_max_fds();
@@ -254,11 +261,23 @@ process_start(char **argv, struct process **pp)
         for (fd = 3; fd < fd_max; fd++) {
             close(fd);
         }
+        error = pthread_sigmask(SIG_SETMASK, &prev_mask, NULL);
+        if (error) {
+            fprintf(stderr, "failed to unblock fatal signals for \"%s\": %s\n",
+                    argv[0], ovs_strerror(error));
+            _exit(1);
+        }
         execvp(argv[0], argv);
         fprintf(stderr, "execvp(\"%s\") failed: %s\n",
                 argv[0], ovs_strerror(errno));
         _exit(1);
     }
+    error2 = pthread_sigmask(SIG_SETMASK, &prev_mask, NULL);
+    if (error2) {
+        VLOG_FATAL("failed to unblock fatal signals after fork: %s",
+                   ovs_strerror(error2));
+    }
+    return error;
 #else
     *pp = NULL;
     return ENOSYS;
-- 
1.8.1.2




More information about the dev mailing list