Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion sapi/fpm/fpm/events/poll.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {

Expand All @@ -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;
}
}

Expand Down
3 changes: 2 additions & 1 deletion sapi/fpm/fpm/events/select.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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;
Expand Down
62 changes: 62 additions & 0 deletions sapi/fpm/tests/events-poll-child-killed-with-output.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
--TEST--
FPM: master survives a worker killed with unread output (poll)
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

require_once "tester.inc";

$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
pid = {{FILE:PID}}
events.mechanism = poll
[unconfined]
listen = {{ADDR}}
ping.path = /ping
ping.response = pong
pm = static
pm.max_children = 1
catch_workers_output = yes
EOT;

$code = <<<'EOT'
<?php
file_put_contents('php://stderr', "unread output\n");
echo getmypid();
EOT;

$tester = new FPM\Tester($cfg, $code);
$tester->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--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>
62 changes: 62 additions & 0 deletions sapi/fpm/tests/events-select-child-killed-with-output.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
--TEST--
FPM: master survives a worker killed with unread output (select)
--SKIPIF--
<?php include "skipif.inc"; ?>
--FILE--
<?php

require_once "tester.inc";

$cfg = <<<EOT
[global]
error_log = {{FILE:LOG}}
pid = {{FILE:PID}}
events.mechanism = select
[unconfined]
listen = {{ADDR}}
ping.path = /ping
ping.response = pong
pm = static
pm.max_children = 1
catch_workers_output = yes
EOT;

$code = <<<'EOT'
<?php
file_put_contents('php://stderr', "unread output\n");
echo getmypid();
EOT;

$tester = new FPM\Tester($cfg, $code);
$tester->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--
<?php
require_once "tester.inc";
FPM\Tester::clean();
?>
Loading