diff --git a/NEWS b/NEWS index daf52ae14d95..a131072cef16 100644 --- a/NEWS +++ b/NEWS @@ -108,6 +108,9 @@ PHP NEWS values. (Marcel Hernandez) . Fixed null pointer dereference in fpm_get_status() when the scoreboard copy fails. (Marcel Hernandez) + . Fixed master process crash when a worker exits with + catch_workers_output enabled and events.mechanism set to select or poll. + (Ilia Alshanetsky) - Fileinfo: . Fixed bug GH-24289 (finfo::file() reports the wrong argument number for diff --git a/sapi/fpm/fpm/events/poll.c b/sapi/fpm/fpm/events/poll.c index 30ebbc785d94..56175045883e 100644 --- a/sapi/fpm/fpm/events/poll.c +++ b/sapi/fpm/fpm/events/poll.c @@ -150,6 +150,7 @@ static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long in /* trigger POLLIN events */ q = queue; while (q) { + struct fpm_event_queue_s *next = q->next; /* ensure ev->index is valid */ if (q->ev && q->ev->index >= 0 && q->ev->index < npollfds && q->ev->fd == active_pollfds[q->ev->index].fd) { @@ -165,7 +166,7 @@ static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long in } } } - q = q->next; /* iterate */ + q = next; } } diff --git a/sapi/fpm/fpm/events/select.c b/sapi/fpm/fpm/events/select.c index 66e8107f07c6..7a0b63103f23 100644 --- a/sapi/fpm/fpm/events/select.c +++ b/sapi/fpm/fpm/events/select.c @@ -109,6 +109,7 @@ static int fpm_event_select_wait(struct fpm_event_queue_s *queue, unsigned long /* trigger POLLIN events */ q = queue; while (q) { + struct fpm_event_queue_s *next = q->next; if (q->ev) { /* sanity check */ /* check if the event has been triggered */ @@ -123,7 +124,7 @@ static int fpm_event_select_wait(struct fpm_event_queue_s *queue, unsigned long } } } - q = q->next; /* iterate */ + q = next; } } return ret; diff --git a/sapi/fpm/tests/events-poll-child-killed-with-output.phpt b/sapi/fpm/tests/events-poll-child-killed-with-output.phpt new file mode 100644 index 000000000000..9e316362135a --- /dev/null +++ b/sapi/fpm/tests/events-poll-child-killed-with-output.phpt @@ -0,0 +1,62 @@ +--TEST-- +FPM: master survives a worker killed with unread output (poll) +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +// Keep the master stopped so the worker output and the pipe EOF are both +// pending when it wakes up. +$tester->signal('STOP'); +$childPid = (int) $tester->request()->getBody(); +if ($childPid <= 0) { + $tester->signal('CONT'); + $tester->terminate(); + $tester->close(); + die("ERROR: no worker PID in the response\n"); +} +$tester->signal('KILL', $childPid); +for ($i = 0; $i < 100 && !str_starts_with((string) exec("ps -o stat= -p $childPid 2>/dev/null"), 'Z'); $i++) { + usleep(10000); +} +$tester->signal('CONT'); +$tester->expectLogLine('unread output'); +$tester->ping('{{ADDR}}'); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- + diff --git a/sapi/fpm/tests/events-select-child-killed-with-output.phpt b/sapi/fpm/tests/events-select-child-killed-with-output.phpt new file mode 100644 index 000000000000..ea4d4f1daf71 --- /dev/null +++ b/sapi/fpm/tests/events-select-child-killed-with-output.phpt @@ -0,0 +1,62 @@ +--TEST-- +FPM: master survives a worker killed with unread output (select) +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +// Keep the master stopped so the worker output and the pipe EOF are both +// pending when it wakes up. +$tester->signal('STOP'); +$childPid = (int) $tester->request()->getBody(); +if ($childPid <= 0) { + $tester->signal('CONT'); + $tester->terminate(); + $tester->close(); + die("ERROR: no worker PID in the response\n"); +} +$tester->signal('KILL', $childPid); +for ($i = 0; $i < 100 && !str_starts_with((string) exec("ps -o stat= -p $childPid 2>/dev/null"), 'Z'); $i++) { + usleep(10000); +} +$tester->signal('CONT'); +$tester->expectLogLine('unread output'); +$tester->ping('{{ADDR}}'); +$tester->terminate(); +$tester->expectLogTerminatingNotices(); +$tester->close(); + +?> +Done +--EXPECT-- +Done +--CLEAN-- +