Use PDEATHSIG to kill cli-server workers if parent exists

Closes GH-9476
This commit is contained in:
Ilija Tovilo 2022-09-02 20:17:27 +02:00
parent c200623f28
commit ecc3fc180f
No known key found for this signature in database
GPG key ID: A4F5D403F118200A
8 changed files with 114 additions and 32 deletions

View file

@ -17,7 +17,7 @@
#include "zend_portability.h" #include "zend_portability.h"
#ifdef __linux__ #ifdef HAVE_PRCTL
# include <sys/prctl.h> # include <sys/prctl.h>
/* fallback definitions if our libc is older than the kernel */ /* fallback definitions if our libc is older than the kernel */
@ -27,7 +27,7 @@
# ifndef PR_SET_VMA_ANON_NAME # ifndef PR_SET_VMA_ANON_NAME
# define PR_SET_VMA_ANON_NAME 0 # define PR_SET_VMA_ANON_NAME 0
# endif # endif
#endif // __linux__ #endif // HAVE_PRCTL
/** /**
* Set a name for the specified memory area. * Set a name for the specified memory area.
@ -36,7 +36,7 @@
*/ */
static zend_always_inline void zend_mmap_set_name(const void *start, size_t len, const char *name) static zend_always_inline void zend_mmap_set_name(const void *start, size_t len, const char *name)
{ {
#ifdef __linux__ #ifdef HAVE_PRCTL
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)start, len, (unsigned long)name); prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, (unsigned long)start, len, (unsigned long)name);
#endif #endif
} }

View file

@ -2748,3 +2748,29 @@ AC_DEFUN([PHP_PATCH_CONFIG_HEADERS], [
$SED -e 's/^#undef PACKAGE_[^ ]*/\/\* & \*\//g' < $srcdir/$1 \ $SED -e 's/^#undef PACKAGE_[^ ]*/\/\* & \*\//g' < $srcdir/$1 \
> $srcdir/$1.tmp && mv $srcdir/$1.tmp $srcdir/$1 > $srcdir/$1.tmp && mv $srcdir/$1.tmp $srcdir/$1
]) ])
dnl Check if we have prctl
AC_DEFUN([PHP_CHECK_PRCTL],
[
AC_MSG_CHECKING([for prctl])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/prctl.h>]], [[prctl(0, 0, 0, 0, 0);]])], [
AC_DEFINE([HAVE_PRCTL], 1, [do we have prctl?])
AC_MSG_RESULT([yes])
], [
AC_MSG_RESULT([no])
])
])
dnl Check if we have procctl
AC_DEFUN([PHP_CHECK_PROCCTL],
[
AC_MSG_CHECKING([for procctl])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/procctl.h>]], [[procctl(0, 0, 0, 0);]])], [
AC_DEFINE([HAVE_PROCCTL], 1, [do we have procctl?])
AC_MSG_RESULT([yes])
], [
AC_MSG_RESULT([no])
])
])

View file

@ -510,6 +510,10 @@ dnl Check __builtin_cpu_init
PHP_CHECK_BUILTIN_CPU_INIT PHP_CHECK_BUILTIN_CPU_INIT
dnl Check __builtin_cpu_supports dnl Check __builtin_cpu_supports
PHP_CHECK_BUILTIN_CPU_SUPPORTS PHP_CHECK_BUILTIN_CPU_SUPPORTS
dnl Check prctl
PHP_CHECK_PRCTL
dnl Check procctl
PHP_CHECK_PROCCTL
dnl Check for __alignof__ support in the compiler dnl Check for __alignof__ support in the compiler
AC_CACHE_CHECK(whether the compiler supports __alignof__, ac_cv_alignof_exists,[ AC_CACHE_CHECK(whether the compiler supports __alignof__, ac_cv_alignof_exists,[

View file

@ -19,6 +19,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <assert.h> #include <assert.h>
#include <signal.h>
#ifdef PHP_WIN32 #ifdef PHP_WIN32
# include <process.h> # include <process.h>
@ -49,6 +50,14 @@
#include <dlfcn.h> #include <dlfcn.h>
#endif #endif
#ifdef HAVE_PRCTL
# include <sys/prctl.h>
#endif
#ifdef HAVE_PROCCTL
# include <sys/procctl.h>
#endif
#include "SAPI.h" #include "SAPI.h"
#include "php.h" #include "php.h"
#include "php_ini.h" #include "php_ini.h"
@ -2432,6 +2441,24 @@ static char *php_cli_server_parse_addr(const char *addr, int *pport) {
return pestrndup(addr, end - addr, 1); return pestrndup(addr, end - addr, 1);
} }
#if defined(HAVE_PRCTL) || defined(HAVE_PROCCTL)
static void php_cli_server_worker_install_pdeathsig(void)
{
// Ignore failure to register PDEATHSIG, it's not available on all platforms anyway
#if defined(HAVE_PRCTL)
prctl(PR_SET_PDEATHSIG, SIGTERM);
#elif defined(HAVE_PROCCTL)
int signal = SIGTERM;
procctl(P_PID, 0, PROC_PDEATHSIG_CTL, &signal);
#endif
// Check if parent has exited just after the fork
if (getppid() != php_cli_server_master) {
exit(1);
}
}
#endif
static void php_cli_server_startup_workers(void) { static void php_cli_server_startup_workers(void) {
char *workers = getenv("PHP_CLI_SERVER_WORKERS"); char *workers = getenv("PHP_CLI_SERVER_WORKERS");
if (!workers) { if (!workers) {
@ -2459,6 +2486,9 @@ static void php_cli_server_startup_workers(void) {
php_cli_server_worker + 1; php_cli_server_worker + 1;
return; return;
} else if (pid == 0) { } else if (pid == 0) {
#if defined(HAVE_PRCTL) || defined(HAVE_PROCCTL)
php_cli_server_worker_install_pdeathsig();
#endif
return; return;
} else { } else {
php_cli_server_workers[php_cli_server_worker] = pid; php_cli_server_workers[php_cli_server_worker] = pid;

View file

@ -4,6 +4,7 @@
class CliServerInfo { class CliServerInfo {
public function __construct( public function __construct(
public string $docRoot, public string $docRoot,
public $processHandle,
) {} ) {}
} }
@ -113,7 +114,7 @@ function php_cli_server_start(
define("PHP_CLI_SERVER_PORT", $port); define("PHP_CLI_SERVER_PORT", $port);
define("PHP_CLI_SERVER_ADDRESS", PHP_CLI_SERVER_HOSTNAME.":".PHP_CLI_SERVER_PORT); define("PHP_CLI_SERVER_ADDRESS", PHP_CLI_SERVER_HOSTNAME.":".PHP_CLI_SERVER_PORT);
return new CliServerInfo($doc_root); return new CliServerInfo($doc_root, $handle);
} }
function php_cli_server_connect() { function php_cli_server_connect() {

View file

@ -0,0 +1,46 @@
--TEST--
Killing server should terminate all worker processes
--ENV--
PHP_CLI_SERVER_WORKERS=2
--SKIPIF--
<?php
include "skipif.inc";
if (!(str_contains(PHP_OS, 'Linux') || str_contains(PHP_OS, 'FreeBSD'))) {
die('skip PDEATHSIG is only supported on Linux and FreeBSD');
}
?>
--FILE--
<?php
function split_words(?string $lines): array {
return preg_split('(\s)', trim($lines ?? ''), flags: PREG_SPLIT_NO_EMPTY);
}
function find_workers_by_ppid(string $ppid) {
return split_words(shell_exec('pgrep -P ' . $ppid));
}
function find_workers_by_pids(array $pids) {
return split_words(shell_exec('ps -o pid= -p ' . join(',', $pids)));
}
include "php_cli_server.inc";
$cliServerInfo = php_cli_server_start('');
$master = proc_get_status($cliServerInfo->processHandle)['pid'];
$workers = find_workers_by_ppid($master);
if (count($workers) === 0) {
throw new \Exception('Could not find worker pids');
}
proc_terminate($cliServerInfo->processHandle, 9); // SIGKILL
$workers = find_workers_by_pids($workers);
if (count($workers) !== 0) {
throw new \Exception('Workers were not properly terminated');
}
echo 'Done';
?>
--EXPECT--
Done

View file

@ -13,30 +13,6 @@ AC_DEFUN([AC_FPM_STDLIBS],
AC_SEARCH_LIBS(inet_addr, nsl) AC_SEARCH_LIBS(inet_addr, nsl)
]) ])
AC_DEFUN([AC_FPM_PRCTL],
[
AC_MSG_CHECKING([for prctl])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/prctl.h>]], [[prctl(0, 0, 0, 0, 0);]])], [
AC_DEFINE([HAVE_PRCTL], 1, [do we have prctl?])
AC_MSG_RESULT([yes])
], [
AC_MSG_RESULT([no])
])
])
AC_DEFUN([AC_FPM_PROCCTL],
[
AC_MSG_CHECKING([for procctl])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/procctl.h>]], [[procctl(0, 0, 0, 0);]])], [
AC_DEFINE([HAVE_PROCCTL], 1, [do we have procctl?])
AC_MSG_RESULT([yes])
], [
AC_MSG_RESULT([no])
])
])
AC_DEFUN([AC_FPM_SETPFLAGS], AC_DEFUN([AC_FPM_SETPFLAGS],
[ [
AC_MSG_CHECKING([for setpflags]) AC_MSG_CHECKING([for setpflags])
@ -530,8 +506,6 @@ if test "$PHP_FPM" != "no"; then
AC_MSG_RESULT($PHP_FPM) AC_MSG_RESULT($PHP_FPM)
AC_FPM_STDLIBS AC_FPM_STDLIBS
AC_FPM_PRCTL
AC_FPM_PROCCTL
AC_FPM_SETPFLAGS AC_FPM_SETPFLAGS
AC_FPM_CLOCK AC_FPM_CLOCK
AC_FPM_TRACE AC_FPM_TRACE

View file

@ -74,10 +74,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <sys/un.h> #include <sys/un.h>
#include <php_config.h>
#include "lsapilib.h" #include "lsapilib.h"
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__) #ifdef HAVE_PRCTL
#include <sys/prctl.h> #include <sys/prctl.h>
#endif #endif
@ -381,7 +382,7 @@ static void lsapi_enable_core_dump(void)
#endif #endif
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__) #ifdef HAVE_PRCTL
if (prctl(PR_SET_DUMPABLE, s_enable_core_dump,0,0,0) == -1) if (prctl(PR_SET_DUMPABLE, s_enable_core_dump,0,0,0) == -1)
perror( "prctl: Failed to set dumpable, " perror( "prctl: Failed to set dumpable, "
"core dump may not be available!"); "core dump may not be available!");