diff options
-rw-r--r-- | sapi/fpm/fpm/fpm_events.c | 15 | ||||
-rw-r--r-- | sapi/fpm/tests/bug77185-reload-robustness.phpt | 46 |
2 files changed, 60 insertions, 1 deletions
diff --git a/sapi/fpm/fpm/fpm_events.c b/sapi/fpm/fpm/fpm_events.c index 1750fab103..81db9f8867 100644 --- a/sapi/fpm/fpm/fpm_events.c +++ b/sapi/fpm/fpm/fpm_events.c @@ -34,6 +34,7 @@ #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); @@ -43,6 +44,7 @@ static void fpm_event_queue_destroy(struct fpm_event_queue_s **queue); 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) /* {{{ */ { @@ -51,6 +53,12 @@ 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; @@ -72,7 +80,12 @@ static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{ 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"); diff --git a/sapi/fpm/tests/bug77185-reload-robustness.phpt b/sapi/fpm/tests/bug77185-reload-robustness.phpt new file mode 100644 index 0000000000..3780176416 --- /dev/null +++ b/sapi/fpm/tests/bug77185-reload-robustness.phpt @@ -0,0 +1,46 @@ +--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(); +?> |