Skip to content

Commit ae4978a

Browse files
committed
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 phpGH-13878
1 parent e08a5dc commit ae4978a

File tree

8 files changed

+145
-2
lines changed

8 files changed

+145
-2
lines changed

NEWS

+3
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ PHP NEWS
122122
. Fixed bug with url_rewriter.hosts not used by output_add_rewrite_var().
123123
(haszi)
124124

125+
- PCNTL:
126+
. Added pcntl_setns for Linux. (David Carlier)
127+
125128
- PCRE:
126129
. Upgrade bundled pcre2lib to version 10.43. (nielsdos)
127130
. Add "/r" modifier. (Ayesh)

UPGRADING

+4
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,10 @@ PHP 8.4 UPGRADE NOTES
442442
. If JIT is enabled, PHP will now exit with a fatal error on startup in case
443443
of JIT startup initialization issues.
444444

445+
- PCNTL:
446+
. Added pcntl_setns allowing a process to be reassociated with a namespace in order
447+
to share resources with other processes within this context.
448+
445449
- Sodium:
446450
. Added the sodium_crypto_aead_aegis128l_*() and sodium_crypto_aead_aegis256l_*()
447451
functions to support the AEGIS family of authenticated encryption algorithms,

ext/pcntl/config.m4

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if test "$PHP_PCNTL" != "no"; then
77
AC_CHECK_FUNCS([fork], [], [AC_MSG_ERROR([pcntl: fork() not supported by this platform])])
88
AC_CHECK_FUNCS([waitpid], [], [AC_MSG_ERROR([pcntl: waitpid() not supported by this platform])])
99
AC_CHECK_FUNCS([sigaction], [], [AC_MSG_ERROR([pcntl: sigaction() not supported by this platform])])
10-
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx])
10+
AC_CHECK_FUNCS([getpriority setpriority wait3 wait4 sigwaitinfo sigtimedwait unshare rfork forkx pidfd_open])
1111

1212
AC_CHECK_TYPE([siginfo_t],[PCNTL_CFLAGS="-DHAVE_STRUCT_SIGINFO_T"],,[#include <signal.h>])
1313

ext/pcntl/pcntl.c

+74
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@
4646
#include <sched.h>
4747
#endif
4848

49+
#ifdef HAVE_PIDFD_OPEN
50+
#include <sys/syscall.h>
51+
#endif
52+
4953
#ifdef HAVE_FORKX
5054
#include <sys/fork.h>
5155
#endif
@@ -1402,6 +1406,76 @@ PHP_FUNCTION(pcntl_forkx)
14021406
#endif
14031407
/* }}} */
14041408

1409+
#ifdef HAVE_PIDFD_OPEN
1410+
// The `pidfd_open` syscall is available since 5.3
1411+
// and `setns` since 3.0.
1412+
PHP_FUNCTION(pcntl_setns)
1413+
{
1414+
zend_long pid, nstype = CLONE_NEWNET;
1415+
bool pid_is_null = 1;
1416+
int fd, ret;
1417+
1418+
ZEND_PARSE_PARAMETERS_START(0, 2)
1419+
Z_PARAM_OPTIONAL
1420+
Z_PARAM_LONG_OR_NULL(pid, pid_is_null)
1421+
Z_PARAM_LONG(nstype)
1422+
ZEND_PARSE_PARAMETERS_END();
1423+
1424+
pid = pid_is_null ? getpid() : pid;
1425+
fd = syscall(SYS_pidfd_open, pid, 0);
1426+
if (errno) {
1427+
PCNTL_G(last_error) = errno;
1428+
switch (errno) {
1429+
case EINVAL:
1430+
case ESRCH:
1431+
zend_argument_value_error(1, "is not a valid process (%d)", pid);
1432+
RETURN_THROWS();
1433+
1434+
case ENFILE:
1435+
php_error_docref(NULL, E_WARNING, "Error %d: File descriptors per-process limit reached", errno);
1436+
break;
1437+
1438+
case ENODEV:
1439+
php_error_docref(NULL, E_WARNING, "Error %d: Anonymous inode fs unsupported", errno);
1440+
break;
1441+
1442+
case ENOMEM:
1443+
php_error_docref(NULL, E_WARNING, "Error %d: Insufficient memory for pidfd_open", errno);
1444+
break;
1445+
1446+
default:
1447+
php_error_docref(NULL, E_WARNING, "Error %d", errno);
1448+
}
1449+
RETURN_FALSE;
1450+
}
1451+
ret = setns(fd, (int)nstype);
1452+
close(fd);
1453+
1454+
if (ret == -1) {
1455+
PCNTL_G(last_error) = errno;
1456+
switch (errno) {
1457+
case ESRCH:
1458+
zend_argument_value_error(1, "process no longer available (%d)", pid);
1459+
RETURN_THROWS();
1460+
1461+
case EINVAL:
1462+
zend_argument_value_error(2, "is an invalid nstype (%d)", nstype);
1463+
RETURN_THROWS();
1464+
1465+
case EPERM:
1466+
php_error_docref(NULL, E_WARNING, "Error %d: No required capability for this process", errno);
1467+
break;
1468+
1469+
default:
1470+
php_error_docref(NULL, E_WARNING, "Error %d", errno);
1471+
}
1472+
RETURN_FALSE;
1473+
} else {
1474+
RETURN_TRUE;
1475+
}
1476+
}
1477+
#endif
1478+
14051479
static void pcntl_interrupt_function(zend_execute_data *execute_data)
14061480
{
14071481
pcntl_signal_dispatch();

ext/pcntl/pcntl.stub.php

+4
Original file line numberDiff line numberDiff line change
@@ -990,3 +990,7 @@ function pcntl_rfork(int $flags, int $signal = 0): int{}
990990
#ifdef HAVE_FORKX
991991
function pcntl_forkx(int $flags): int{}
992992
#endif
993+
994+
#ifdef HAVE_PIDFD_OPEN
995+
function pcntl_setns(int $process_id = null, int $nstype = CLONE_NEWNET): bool {}
996+
#endif

ext/pcntl/pcntl_arginfo.h

+14-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
pcntl_setns()
3+
--EXTENSIONS--
4+
pcntl
5+
posix
6+
--SKIPIF--
7+
<?php
8+
if (!function_exists("pcntl_setns")) die("skip pcntl_setns is not available");
9+
if (getenv('SKIP_ASAN')) die('skip Timeouts under ASAN');
10+
?>
11+
--FILE--
12+
<?php
13+
$pid = pcntl_fork();
14+
if ($pid == -1) die("pcntl_fork failed");
15+
if ($pid != 0) {
16+
try {
17+
pcntl_setns($pid, 0);
18+
} catch (\ValueError $e) {
19+
echo $e->getMessage();
20+
}
21+
}
22+
?>
23+
--EXPECTF--
24+
pcntl_setns(): Argument #2 ($nstype) is an invalid nstype (%d)
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
pcntl_setns()
3+
--EXTENSIONS--
4+
pcntl
5+
posix
6+
--SKIPIF--
7+
<?php
8+
if (!function_exists("pcntl_setns")) die("skip pcntl_setns is not available");
9+
if (posix_getuid() !== 0) die('skip Test needs root user');
10+
if (getenv('SKIP_ASAN')) die('skip Timeouts under ASAN');
11+
?>
12+
--FILE--
13+
<?php
14+
$pid = pcntl_fork();
15+
if ($pid == -1) die("pcntl_fork failed");
16+
if ($pid != 0) {
17+
var_dump(pcntl_setns($pid, CLONE_NEWPID));
18+
}
19+
?>
20+
--EXPECT--
21+
bool(true)

0 commit comments

Comments
 (0)