php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login

Patch php-fpm-76601-avoid-child-ignorance_7.2.11_2018-09-28.patch for FPM related Bug #76601

Patch version 2018-10-16 04:32 UTC

Return to Bug #76601 | Download this patch
Patch Revisions:

Developer: mnikulin@plesk.com

Fix php-fpm failure after concurrent reload attempts. Bug #76601

Postpone signal delivery while spawning children. Prevent the following case:
* Reload (reexec) is in progress.
* New master is forking to start enough children for pools where pm is not on-demand.
* Another SIGUSR2 is received by master process.
* Master process switches to reloading state.
* Some child has not set its own signal handlers.
* SIGQUIT and SIGTERM sent by master process a caught by signal handler
  set by master process and so they are ignored.
* A child is running, it has no reason to finish
* Master process is waiting for completion of all its children.
* SIGKILL is not scheduled after SIGTERM due to a bug around of reusing
  the same timer in the event loop (see another patch).
* Reload has stuck.
* No incoming requests are processed besides ones handled by a few of survived children.
* Event loop is running however, systemd is receiving alive notification.
* New reload attempts do not change the state of master process.
* Most of web sites are unavailable.

Use sigprocmask() around fork() to avoid race of delivery signal to children
and setting of own signal handlers.

Index: php-7.2.11/sapi/fpm/fpm/fpm_children.c
===================================================================
--- php-7.2.11.orig/sapi/fpm/fpm/fpm_children.c
+++ php-7.2.11/sapi/fpm/fpm/fpm_children.c
@@ -398,6 +398,11 @@ int fpm_children_make(struct fpm_worker_
 			return 2;
 		}
 
+		zlog(ZLOG_DEBUG, "blocking signals before child birth");
+		if (0 > fpm_signals_child_block()) {
+			zlog(ZLOG_WARNING, "child may miss signals");
+		}
+
 		pid = fork();
 
 		switch (pid) {
@@ -409,12 +414,16 @@ int fpm_children_make(struct fpm_worker_
 				return 0;
 
 			case -1 :
+				zlog(ZLOG_DEBUG, "unblocking signals");
+				fpm_signals_unblock();
 				zlog(ZLOG_SYSERROR, "fork() failed");
 
 				fpm_resources_discard(child);
 				return 2;
 
 			default :
+				zlog(ZLOG_DEBUG, "unblocking signals, child born");
+				fpm_signals_unblock();
 				child->pid = pid;
 				fpm_clock_get(&child->started);
 				fpm_parent_resources_use(child);
Index: php-7.2.11/sapi/fpm/fpm/fpm_signals.c
===================================================================
--- php-7.2.11.orig/sapi/fpm/fpm/fpm_signals.c
+++ php-7.2.11/sapi/fpm/fpm/fpm_signals.c
@@ -22,6 +22,7 @@
 
 static int sp[2];
 static sigset_t block_sigset;
+static sigset_t child_block_sigset;
 
 const char *fpm_signal_names[NSIG + 1] = {
 #ifdef SIGHUP
@@ -169,7 +170,8 @@ static void sig_handler(int signo) /* {{
 
 	if (fpm_globals.parent_pid != getpid()) {
 		/* prevent a signal race condition when child process
-			have not set up it's own signal handler yet */
+			do not set up it's own sigprocmask for some reason,
+			leads to #76601 in such cases */
 		return;
 	}
 
@@ -249,6 +251,10 @@ int fpm_signals_init_child() /* {{{ */
 	}
 
 	zend_signal_init();
+
+	if (0 > fpm_signals_unblock()) {
+		return -1;
+	}
 	return 0;
 }
 /* }}} */
@@ -278,6 +284,12 @@ int fpm_signals_init_mask(int *signum_ar
 			return -1;
 		}
 	}
+	memcpy(&child_block_sigset, &block_sigset, sizeof(block_sigset));
+	if (0 > sigaddset(&child_block_sigset, SIGTERM) ||
+	    0 > sigaddset(&child_block_sigset, SIGQUIT)) {
+		zlog(ZLOG_SYSERROR, "failed to prepare child signal block mask: sigaddset()");
+		return -1;
+	}
 	return 0;
 }
 /* }}} */
@@ -289,6 +301,16 @@ int fpm_signals_block() /* {{{ */
 		return -1;
 	}
 	return 0;
+}
+/* }}} */
+
+int fpm_signals_child_block() /* {{{ */
+{
+	if (0 > sigprocmask(SIG_BLOCK, &child_block_sigset, NULL)) {
+		zlog(ZLOG_SYSERROR, "failed to block child signals");
+		return -1;
+	}
+	return 0;
 }
 /* }}} */
 
Index: php-7.2.11/sapi/fpm/fpm/fpm_signals.h
===================================================================
--- php-7.2.11.orig/sapi/fpm/fpm/fpm_signals.h
+++ php-7.2.11/sapi/fpm/fpm/fpm_signals.h
@@ -12,6 +12,7 @@ int fpm_signals_init_child();
 int fpm_signals_get_fd();
 int fpm_signals_init_mask(int *signum_array, size_t size);
 int fpm_signals_block();
+int fpm_signals_child_block();
 int fpm_signals_unblock();
 
 extern const char *fpm_signal_names[NSIG + 1];
 
PHP Copyright © 2001-2019 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 10:01:22 2019 UTC