#define fpm_event_set_timeout(ev, now) timeradd(&(now), &(ev)->frequency, &(ev)->timeout);
static void fpm_event_cleanup(int which, void *arg);
+static void fpm_postponed_children_bury(struct fpm_event_s *ev, short which, void *arg);
static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg);
static struct fpm_event_s *fpm_event_queue_isset(struct fpm_event_queue_s *queue, struct fpm_event_s *ev);
static int fpm_event_queue_add(struct fpm_event_queue_s **queue, struct fpm_event_s *ev);
static struct fpm_event_module_s *module;
static struct fpm_event_queue_s *fpm_event_queue_timer = NULL;
static struct fpm_event_queue_s *fpm_event_queue_fd = NULL;
+static struct fpm_event_s children_bury_timer;
static void fpm_event_cleanup(int which, void *arg) /* {{{ */
{
}
/* }}} */
+static void fpm_postponed_children_bury(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
+{
+ fpm_children_bury();
+}
+/* }}} */
+
static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
{
char c;
switch (c) {
case 'C' : /* SIGCHLD */
zlog(ZLOG_DEBUG, "received SIGCHLD");
- fpm_children_bury();
+ /* epoll_wait() may report signal fd before read events for a finished child
+ * in the same bunch of events. Prevent immediate free of the child structure
+ * and so the fpm_event_s instance. Otherwise use after free happens during
+ * attemp to process following read event. */
+ fpm_event_set_timer(&children_bury_timer, 0, &fpm_postponed_children_bury, NULL);
+ fpm_event_add(&children_bury_timer, 0);
break;
case 'I' : /* SIGINT */
zlog(ZLOG_DEBUG, "received SIGINT");
--- /dev/null
+--TEST--
+FPM: bug77185 - Reload robustness
+--SKIPIF--
+<?php include "skipif.inc"; ?>
+--FILE--
+<?php
+
+require_once "tester.inc";
+
+$workers = 10;
+$loops = 10;
+
+$cfg = <<<EOT
+[global]
+error_log = {{FILE:LOG}}
+pid = {{FILE:PID}}
+[unconfined]
+listen = {{ADDR}}
+pm = static
+pm.max_children = $workers
+catch_workers_output = true
+EOT;
+
+$tester = new FPM\Tester($cfg);
+$tester->start();
+$tester->expectLogStartNotices();
+for ($i = 0; $i < $loops; $i++) {
+ $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->terminate();
+$tester->expectLogTerminatingNotices();
+$tester->close();
+
+?>
+Done
+--EXPECT--
+Done
+--CLEAN--
+<?php
+require_once "tester.inc";
+FPM\Tester::clean();
+?>