From f26e14ccc1d5d0d843d91ebb084b50e120643f7b Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 3 Apr 2024 18:59:40 +0100 Subject: [PATCH 1/3] 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. --- ext/pcntl/config.m4 | 2 +- ext/pcntl/pcntl.c | 68 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/ext/pcntl/config.m4 b/ext/pcntl/config.m4 index 881a0da688c87..7e7a389615a88 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 79b51967f3eaf..45dfa3c5b170c 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,70 @@ 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; + } + 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; + } + RETURN_FALSE; + } else { + RETURN_TRUE; + } +} +#endif + static void pcntl_interrupt_function(zend_execute_data *execute_data) { pcntl_signal_dispatch(); From 20d648ac26ca8bed855e61f17e61b9fe57ff7189 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 3 Apr 2024 19:28:53 +0100 Subject: [PATCH 2/3] stub --- ext/pcntl/pcntl.stub.php | 4 ++++ ext/pcntl/pcntl_arginfo.h | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ext/pcntl/pcntl.stub.php b/ext/pcntl/pcntl.stub.php index 7a1aabaacdeeb..09e318307d054 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 8cb96afee8d7f..c86c1d815b5e7 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 }; From 878f6ec86d926f8405bbe621a30b0133f73ac846 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 3 Apr 2024 20:16:51 +0100 Subject: [PATCH 3/3] test --- ext/pcntl/pcntl.c | 6 ++++++ ext/pcntl/tests/pcntl_setns_basic.phpt | 24 ++++++++++++++++++++++++ ext/pcntl/tests/pcntl_setns_newpid.phpt | 21 +++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 ext/pcntl/tests/pcntl_setns_basic.phpt create mode 100644 ext/pcntl/tests/pcntl_setns_newpid.phpt diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 45dfa3c5b170c..b19edb0ddb928 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -1442,6 +1442,9 @@ PHP_FUNCTION(pcntl_setns) 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; } @@ -1462,6 +1465,9 @@ PHP_FUNCTION(pcntl_setns) 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 { diff --git a/ext/pcntl/tests/pcntl_setns_basic.phpt b/ext/pcntl/tests/pcntl_setns_basic.phpt new file mode 100644 index 0000000000000..a4f2ea773c57f --- /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 0000000000000..79d8a426f041f --- /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)