From ae4978a139aa0ac060931bc9ea23a03b437cd9b6 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 3 Apr 2024 18:59:40 +0100 Subject: [PATCH] ext/pcntl: adding pcntl_setns for Linux >= 5.3 allows a given process to join an existing Linux namespace, relatively complementary to the existing pcntl_unshare. Close GH-13878 --- NEWS | 3 + UPGRADING | 4 ++ ext/pcntl/config.m4 | 2 +- ext/pcntl/pcntl.c | 74 +++++++++++++++++++++++++ ext/pcntl/pcntl.stub.php | 4 ++ ext/pcntl/pcntl_arginfo.h | 15 ++++- ext/pcntl/tests/pcntl_setns_basic.phpt | 24 ++++++++ ext/pcntl/tests/pcntl_setns_newpid.phpt | 21 +++++++ 8 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 ext/pcntl/tests/pcntl_setns_basic.phpt create mode 100644 ext/pcntl/tests/pcntl_setns_newpid.phpt diff --git a/NEWS b/NEWS index 913b1c24169..bb229286ccd 100644 --- a/NEWS +++ b/NEWS @@ -122,6 +122,9 @@ PHP NEWS . Fixed bug with url_rewriter.hosts not used by output_add_rewrite_var(). (haszi) +- PCNTL: + . Added pcntl_setns for Linux. (David Carlier) + - PCRE: . Upgrade bundled pcre2lib to version 10.43. (nielsdos) . Add "/r" modifier. (Ayesh) diff --git a/UPGRADING b/UPGRADING index 96b99dc393b..3dbc31994d3 100644 --- a/UPGRADING +++ b/UPGRADING @@ -442,6 +442,10 @@ PHP 8.4 UPGRADE NOTES . If JIT is enabled, PHP will now exit with a fatal error on startup in case of JIT startup initialization issues. +- PCNTL: + . Added pcntl_setns allowing a process to be reassociated with a namespace in order + to share resources with other processes within this context. + - Sodium: . Added the sodium_crypto_aead_aegis128l_*() and sodium_crypto_aead_aegis256l_*() functions to support the AEGIS family of authenticated encryption algorithms, diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index 881a0da688c..7e7a389615a 100644 --- a/ext/pcntl/config.m4 +++ b/ext/pcntl/config.m4 @@ -7,7 +7,7 @@ if test "$PHP_PCNTL" != "no"; then AC_CHECK_FUNCS([fork], [], [AC_MSG_ERROR([pcntl: fork() not supported by this platform])]) AC_CHECK_FUNCS([waitpid], [], [AC_MSG_ERROR([pcntl: waitpid() not supported by this platform])]) AC_CHECK_FUNCS([sigaction], [], [AC_MSG_ERROR([pcntl: sigaction() not supported by this platform])]) - AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx]) + AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open]) AC_CHECK_TYPE([siginfo_t],[PCNTL_CFLAGS="-DHAVE_STRUCT_SIGINFO_T"],,[#include ]) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 79b51967f3e..b19edb0ddb9 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -46,6 +46,10 @@ #include #endif +#ifdef HAVE_PIDFD_OPEN +#include +#endif + #ifdef HAVE_FORKX #include #endif @@ -1402,6 +1406,76 @@ PHP_FUNCTION(pcntl_forkx) #endif /* }}} */ +#ifdef HAVE_PIDFD_OPEN +// The `pidfd_open` syscall is available since 5.3 +// and `setns` since 3.0. +PHP_FUNCTION(pcntl_setns) +{ + zend_long pid, nstype = CLONE_NEWNET; + bool pid_is_null = 1; + int fd, ret; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_LONG_OR_NULL(pid, pid_is_null) + Z_PARAM_LONG(nstype) + ZEND_PARSE_PARAMETERS_END(); + + pid = pid_is_null ? getpid() : pid; + fd = syscall(SYS_pidfd_open, pid, 0); + if (errno) { + PCNTL_G(last_error) = errno; + switch (errno) { + case EINVAL: + case ESRCH: + zend_argument_value_error(1, "is not a valid process (%d)", pid); + RETURN_THROWS(); + + case ENFILE: + php_error_docref(NULL, E_WARNING, "Error %d: File descriptors per-process limit reached", errno); + break; + + case ENODEV: + php_error_docref(NULL, E_WARNING, "Error %d: Anonymous inode fs unsupported", errno); + break; + + case ENOMEM: + php_error_docref(NULL, E_WARNING, "Error %d: Insufficient memory for pidfd_open", errno); + break; + + default: + php_error_docref(NULL, E_WARNING, "Error %d", errno); + } + RETURN_FALSE; + } + ret = setns(fd, (int)nstype); + close(fd); + + if (ret == -1) { + PCNTL_G(last_error) = errno; + switch (errno) { + case ESRCH: + zend_argument_value_error(1, "process no longer available (%d)", pid); + RETURN_THROWS(); + + case EINVAL: + zend_argument_value_error(2, "is an invalid nstype (%d)", nstype); + RETURN_THROWS(); + + case EPERM: + php_error_docref(NULL, E_WARNING, "Error %d: No required capability for this process", errno); + break; + + default: + php_error_docref(NULL, E_WARNING, "Error %d", errno); + } + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +#endif + static void pcntl_interrupt_function(zend_execute_data *execute_data) { pcntl_signal_dispatch(); diff --git a/ext/pcntl/pcntl.stub.php b/ext/pcntl/pcntl.stub.php index 7a1aabaacde..09e318307d0 100644 --- a/ext/pcntl/pcntl.stub.php +++ b/ext/pcntl/pcntl.stub.php @@ -990,3 +990,7 @@ function pcntl_rfork(int $flags, int $signal = 0): int{} #ifdef HAVE_FORKX function pcntl_forkx(int $flags): int{} #endif + +#ifdef HAVE_PIDFD_OPEN +function pcntl_setns(int $process_id = null, int $nstype = CLONE_NEWNET): bool {} +#endif diff --git a/ext/pcntl/pcntl_arginfo.h b/ext/pcntl/pcntl_arginfo.h index 8cb96afee8d..c86c1d815b5 100644 --- a/ext/pcntl/pcntl_arginfo.h +++ b/ext/pcntl/pcntl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 3b03373d1bb68de779baaa62db14d98ca9018339 */ + * Stub hash: 614bd67bb4cfcdc68d37141ff9dfad0a49c318b5 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_fork, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -132,6 +132,13 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_forkx, 0, 1, IS_LONG, 0) ZEND_END_ARG_INFO() #endif +#if defined(HAVE_PIDFD_OPEN) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pcntl_setns, 0, 0, _IS_BOOL, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, process_id, IS_LONG, 0, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, nstype, IS_LONG, 0, "CLONE_NEWNET") +ZEND_END_ARG_INFO() +#endif + ZEND_FUNCTION(pcntl_fork); ZEND_FUNCTION(pcntl_waitpid); ZEND_FUNCTION(pcntl_wait); @@ -176,6 +183,9 @@ ZEND_FUNCTION(pcntl_rfork); #if defined(HAVE_FORKX) ZEND_FUNCTION(pcntl_forkx); #endif +#if defined(HAVE_PIDFD_OPEN) +ZEND_FUNCTION(pcntl_setns); +#endif static const zend_function_entry ext_functions[] = { ZEND_FE(pcntl_fork, arginfo_pcntl_fork) @@ -222,6 +232,9 @@ static const zend_function_entry ext_functions[] = { #endif #if defined(HAVE_FORKX) ZEND_FE(pcntl_forkx, arginfo_pcntl_forkx) +#endif +#if defined(HAVE_PIDFD_OPEN) + ZEND_FE(pcntl_setns, arginfo_pcntl_setns) #endif ZEND_FE_END }; diff --git a/ext/pcntl/tests/pcntl_setns_basic.phpt b/ext/pcntl/tests/pcntl_setns_basic.phpt new file mode 100644 index 00000000000..a4f2ea773c5 --- /dev/null +++ b/ext/pcntl/tests/pcntl_setns_basic.phpt @@ -0,0 +1,24 @@ +--TEST-- +pcntl_setns() +--EXTENSIONS-- +pcntl +posix +--SKIPIF-- + +--FILE-- +getMessage(); + } +} +?> +--EXPECTF-- +pcntl_setns(): Argument #2 ($nstype) is an invalid nstype (%d) diff --git a/ext/pcntl/tests/pcntl_setns_newpid.phpt b/ext/pcntl/tests/pcntl_setns_newpid.phpt new file mode 100644 index 00000000000..79d8a426f04 --- /dev/null +++ b/ext/pcntl/tests/pcntl_setns_newpid.phpt @@ -0,0 +1,21 @@ +--TEST-- +pcntl_setns() +--EXTENSIONS-- +pcntl +posix +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true)