summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Zelenka <bukka@php.net>2019-10-20 16:59:30 +0100
committerJakub Zelenka <bukka@php.net>2019-10-20 16:59:30 +0100
commit878f8b0b235d6815ab441b9019f02d611b47d422 (patch)
tree46628b3bf0b8c50bc1e5102539757d4193b8b59c
parentcdacad8e5074857f7f0993cb271999e6d1a2cf2b (diff)
parent666fb9a1946a6eee3019583fc705894a300b0f6d (diff)
downloadphp-git-878f8b0b235d6815ab441b9019f02d611b47d422.tar.gz
Merge branch 'PHP-7.4'
-rw-r--r--sapi/fpm/fpm/fpm_main.c10
-rw-r--r--sapi/fpm/fpm/fpm_process_ctl.c4
-rw-r--r--sapi/fpm/fpm/fpm_signals.c53
-rw-r--r--sapi/fpm/fpm/fpm_signals.h3
-rw-r--r--sapi/fpm/tests/bug74083-concurrent-reload.phpt77
5 files changed, 147 insertions, 0 deletions
diff --git a/sapi/fpm/fpm/fpm_main.c b/sapi/fpm/fpm/fpm_main.c
index 929481c1d5..5e7d2c1447 100644
--- a/sapi/fpm/fpm/fpm_main.c
+++ b/sapi/fpm/fpm/fpm_main.c
@@ -84,6 +84,7 @@ int __riscosify_control = __RISCOSIFY_STRICT_UNIX_SPECS;
#include "fpm.h"
#include "fpm_request.h"
#include "fpm_status.h"
+#include "fpm_signals.h"
#include "fpm_conf.h"
#include "fpm_php.h"
#include "fpm_log.h"
@@ -1568,6 +1569,15 @@ int main(int argc, char *argv[])
closes it. in apache|apxs mode apache
does that for us! thies@thieso.net
20000419 */
+
+ /* Subset of signals from fpm_signals_init_main() to avoid unexpected death during early init
+ or during reload just after execvp() or fork */
+ int init_signal_array[] = { SIGUSR1, SIGUSR2, SIGCHLD };
+ if (0 > fpm_signals_init_mask(init_signal_array, sizeof(init_signal_array)/sizeof(init_signal_array[0])) ||
+ 0 > fpm_signals_block()) {
+ zlog(ZLOG_WARNING, "Could die in the case of too early reload signal");
+ }
+ zlog(ZLOG_DEBUG, "Blocked some signals");
#endif
#ifdef ZTS
diff --git a/sapi/fpm/fpm/fpm_process_ctl.c b/sapi/fpm/fpm/fpm_process_ctl.c
index 17fbe8d3e0..2bc00178ea 100644
--- a/sapi/fpm/fpm/fpm_process_ctl.c
+++ b/sapi/fpm/fpm/fpm_process_ctl.c
@@ -77,6 +77,10 @@ static void fpm_pctl_exit() /* {{{ */
static void fpm_pctl_exec() /* {{{ */
{
+ zlog(ZLOG_DEBUG, "Blocking some signals before reexec");
+ if (0 > fpm_signals_block()) {
+ zlog(ZLOG_WARNING, "concurrent reloads may be unstable");
+ }
zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
"%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
diff --git a/sapi/fpm/fpm/fpm_signals.c b/sapi/fpm/fpm/fpm_signals.c
index caf41e8a3f..922d4b1a9c 100644
--- a/sapi/fpm/fpm/fpm_signals.c
+++ b/sapi/fpm/fpm/fpm_signals.c
@@ -19,6 +19,7 @@
#include "zlog.h"
static int sp[2];
+static sigset_t block_sigset;
const char *fpm_signal_names[NSIG + 1] = {
#ifdef SIGHUP
@@ -210,6 +211,11 @@ int fpm_signals_init_main() /* {{{ */
zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()");
return -1;
}
+
+ zlog(ZLOG_DEBUG, "Unblocking all signals");
+ if (0 > fpm_signals_unblock()) {
+ return -1;
+ }
return 0;
}
/* }}} */
@@ -250,3 +256,50 @@ int fpm_signals_get_fd() /* {{{ */
return sp[0];
}
/* }}} */
+
+int fpm_signals_init_mask(int *signum_array, size_t size) /* {{{ */
+{
+ size_t i = 0;
+ if (0 > sigemptyset(&block_sigset)) {
+ zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigemptyset()");
+ return -1;
+ }
+ for (i = 0; i < size; ++i) {
+ int sig_i = signum_array[i];
+ if (0 > sigaddset(&block_sigset, sig_i)) {
+ if (sig_i <= NSIG && fpm_signal_names[sig_i] != NULL) {
+ zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigaddset(%s)",
+ fpm_signal_names[sig_i]);
+ } else {
+ zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigaddset(%d)", sig_i);
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_signals_block() /* {{{ */
+{
+ if (0 > sigprocmask(SIG_BLOCK, &block_sigset, NULL)) {
+ zlog(ZLOG_SYSERROR, "failed to block signals");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
+
+int fpm_signals_unblock() /* {{{ */
+{
+ /* Ensure that during reload after upgrade all signals are unblocked.
+ block_sigset could have different value before execve() */
+ sigset_t all_signals;
+ sigfillset(&all_signals);
+ if (0 > sigprocmask(SIG_UNBLOCK, &all_signals, NULL)) {
+ zlog(ZLOG_SYSERROR, "failed to unblock signals");
+ return -1;
+ }
+ return 0;
+}
+/* }}} */
diff --git a/sapi/fpm/fpm/fpm_signals.h b/sapi/fpm/fpm/fpm_signals.h
index 7202816218..6ce7277cb2 100644
--- a/sapi/fpm/fpm/fpm_signals.h
+++ b/sapi/fpm/fpm/fpm_signals.h
@@ -8,6 +8,9 @@
int fpm_signals_init_main();
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_unblock();
extern const char *fpm_signal_names[NSIG + 1];
diff --git a/sapi/fpm/tests/bug74083-concurrent-reload.phpt b/sapi/fpm/tests/bug74083-concurrent-reload.phpt
new file mode 100644
index 0000000000..8b4b690e96
--- /dev/null
+++ b/sapi/fpm/tests/bug74083-concurrent-reload.phpt
@@ -0,0 +1,77 @@
+--TEST--
+Concurrent reload signals should not kill PHP-FPM master process. (Bug: #74083)
+--SKIPIF--
+<?php
+include "skipif.inc";
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+?>
+--FILE--
+<?php
+
+require_once "tester.inc";
+
+$cfg = <<<EOT
+[global]
+error_log = {{FILE:LOG}}
+pid = {{FILE:PID}}
+process_control_timeout=1
+[unconfined]
+listen = {{ADDR}}
+ping.path = /ping
+ping.response = pong
+pm = dynamic
+pm.max_children = 5
+pm.start_servers = 1
+pm.min_spare_servers = 1
+pm.max_spare_servers = 1
+EOT;
+
+$code = <<<EOT
+<?php
+/* empty */
+EOT;
+
+$tester = new FPM\Tester($cfg, $code);
+$tester->start();
+$tester->expectLogStartNotices();
+$tester->ping('{{ADDR}}');
+
+/* Vary interval between concurrent reload requests
+ since performance of test instance is not known in advance */
+$max_interval = 25000;
+$step = 1000;
+$pid = $tester->getPid();
+for ($interval = 0; $interval < $max_interval; $interval += $step) {
+ exec("kill -USR2 $pid", $out, $killExitCode);
+ if ($killExitCode) {
+ echo "ERROR: master process is dead\n";
+ break;
+ }
+ usleep($interval);
+}
+echo "Reached interval $interval us with $step us steps\n";
+$tester->expectLogNotice('Reloading in progress ...');
+/* Consume mix of 'Reloading in progress ...' and 'reloading: .*' */
+$tester->getLogLines(2000);
+
+$tester->signal('USR2');
+$tester->expectLogNotice('Reloading in progress ...');
+$tester->expectLogNotice('reloading: .*');
+$tester->expectLogNotice('using inherited socket fd=\d+, "127.0.0.1:\d+"');
+$tester->expectLogStartNotices();
+$tester->ping('{{ADDR}}');
+
+$tester->terminate();
+$tester->expectLogTerminatingNotices();
+$tester->close();
+
+?>
+Done
+--EXPECT--
+Reached interval 25000 us with 1000 us steps
+Done
+--CLEAN--
+<?php
+require_once "tester.inc";
+FPM\Tester::clean();
+?>