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
This commit is contained in:
David Carlier 2024-04-03 18:59:40 +01:00
parent e08a5dce8f
commit ae4978a139
No known key found for this signature in database
GPG Key ID: CEF290BB40D2086B
8 changed files with 145 additions and 2 deletions

3
NEWS
View File

@ -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)

View File

@ -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,

View File

@ -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 <signal.h>])

View File

@ -46,6 +46,10 @@
#include <sched.h>
#endif
#ifdef HAVE_PIDFD_OPEN
#include <sys/syscall.h>
#endif
#ifdef HAVE_FORKX
#include <sys/fork.h>
#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();

View File

@ -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

View File

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

View File

@ -0,0 +1,24 @@
--TEST--
pcntl_setns()
--EXTENSIONS--
pcntl
posix
--SKIPIF--
<?php
if (!function_exists("pcntl_setns")) die("skip pcntl_setns is not available");
if (getenv('SKIP_ASAN')) die('skip Timeouts under ASAN');
?>
--FILE--
<?php
$pid = pcntl_fork();
if ($pid == -1) die("pcntl_fork failed");
if ($pid != 0) {
try {
pcntl_setns($pid, 0);
} catch (\ValueError $e) {
echo $e->getMessage();
}
}
?>
--EXPECTF--
pcntl_setns(): Argument #2 ($nstype) is an invalid nstype (%d)

View File

@ -0,0 +1,21 @@
--TEST--
pcntl_setns()
--EXTENSIONS--
pcntl
posix
--SKIPIF--
<?php
if (!function_exists("pcntl_setns")) die("skip pcntl_setns is not available");
if (posix_getuid() !== 0) die('skip Test needs root user');
if (getenv('SKIP_ASAN')) die('skip Timeouts under ASAN');
?>
--FILE--
<?php
$pid = pcntl_fork();
if ($pid == -1) die("pcntl_fork failed");
if ($pid != 0) {
var_dump(pcntl_setns($pid, CLONE_NEWPID));
}
?>
--EXPECT--
bool(true)