| // SPDX-License-Identifier: GPL-2.0 | |
| #define _GNU_SOURCE | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <limits.h> | |
| #include <linux/types.h> | |
| #include <sched.h> | |
| #include <signal.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <syscall.h> | |
| #include <sys/prctl.h> | |
| #include <sys/wait.h> | |
| #include <unistd.h> | |
| #include <sys/socket.h> | |
| #include <sys/stat.h> | |
| #include "pidfd.h" | |
| #include "kselftest_harness.h" | |
| enum { | |
| PIDFD_NS_USER, | |
| PIDFD_NS_MNT, | |
| PIDFD_NS_PID, | |
| PIDFD_NS_UTS, | |
| PIDFD_NS_IPC, | |
| PIDFD_NS_NET, | |
| PIDFD_NS_CGROUP, | |
| PIDFD_NS_PIDCLD, | |
| PIDFD_NS_TIME, | |
| PIDFD_NS_TIMECLD, | |
| PIDFD_NS_MAX | |
| }; | |
| const struct ns_info { | |
| const char *name; | |
| int flag; | |
| unsigned int pidfd_ioctl; | |
| } ns_info[] = { | |
| [PIDFD_NS_USER] = { "user", CLONE_NEWUSER, PIDFD_GET_USER_NAMESPACE, }, | |
| [PIDFD_NS_MNT] = { "mnt", CLONE_NEWNS, PIDFD_GET_MNT_NAMESPACE, }, | |
| [PIDFD_NS_PID] = { "pid", CLONE_NEWPID, PIDFD_GET_PID_NAMESPACE, }, | |
| [PIDFD_NS_UTS] = { "uts", CLONE_NEWUTS, PIDFD_GET_UTS_NAMESPACE, }, | |
| [PIDFD_NS_IPC] = { "ipc", CLONE_NEWIPC, PIDFD_GET_IPC_NAMESPACE, }, | |
| [PIDFD_NS_NET] = { "net", CLONE_NEWNET, PIDFD_GET_NET_NAMESPACE, }, | |
| [PIDFD_NS_CGROUP] = { "cgroup", CLONE_NEWCGROUP, PIDFD_GET_CGROUP_NAMESPACE, }, | |
| [PIDFD_NS_TIME] = { "time", CLONE_NEWTIME, PIDFD_GET_TIME_NAMESPACE, }, | |
| [PIDFD_NS_PIDCLD] = { "pid_for_children", 0, PIDFD_GET_PID_FOR_CHILDREN_NAMESPACE, }, | |
| [PIDFD_NS_TIMECLD] = { "time_for_children", 0, PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE, }, | |
| }; | |
| FIXTURE(current_nsset) | |
| { | |
| pid_t pid; | |
| int pidfd; | |
| int nsfds[PIDFD_NS_MAX]; | |
| int child_pidfd_derived_nsfds[PIDFD_NS_MAX]; | |
| pid_t child_pid_exited; | |
| int child_pidfd_exited; | |
| pid_t child_pid1; | |
| int child_pidfd1; | |
| int child_nsfds1[PIDFD_NS_MAX]; | |
| int child_pidfd_derived_nsfds1[PIDFD_NS_MAX]; | |
| pid_t child_pid2; | |
| int child_pidfd2; | |
| int child_nsfds2[PIDFD_NS_MAX]; | |
| int child_pidfd_derived_nsfds2[PIDFD_NS_MAX]; | |
| }; | |
| static bool switch_timens(void) | |
| { | |
| int fd, ret; | |
| if (unshare(CLONE_NEWTIME)) | |
| return false; | |
| fd = open("/proc/self/ns/time_for_children", O_RDONLY | O_CLOEXEC); | |
| if (fd < 0) | |
| return false; | |
| ret = setns(fd, CLONE_NEWTIME); | |
| close(fd); | |
| return ret == 0; | |
| } | |
| FIXTURE_SETUP(current_nsset) | |
| { | |
| int i, proc_fd, ret; | |
| int ipc_sockets[2]; | |
| char c; | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| self->nsfds[i] = -EBADF; | |
| self->child_nsfds1[i] = -EBADF; | |
| self->child_nsfds2[i] = -EBADF; | |
| self->child_pidfd_derived_nsfds[i] = -EBADF; | |
| self->child_pidfd_derived_nsfds1[i] = -EBADF; | |
| self->child_pidfd_derived_nsfds2[i] = -EBADF; | |
| } | |
| proc_fd = open("/proc/self/ns", O_DIRECTORY | O_CLOEXEC); | |
| ASSERT_GE(proc_fd, 0) { | |
| TH_LOG("%m - Failed to open /proc/self/ns"); | |
| } | |
| self->pid = getpid(); | |
| self->pidfd = sys_pidfd_open(self->pid, 0); | |
| EXPECT_GT(self->pidfd, 0) { | |
| TH_LOG("%m - Failed to open pidfd for process %d", self->pid); | |
| } | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| self->nsfds[i] = openat(proc_fd, info->name, O_RDONLY | O_CLOEXEC); | |
| if (self->nsfds[i] < 0) { | |
| EXPECT_EQ(errno, ENOENT) { | |
| TH_LOG("%m - Failed to open %s namespace for process %d", | |
| info->name, self->pid); | |
| } | |
| } | |
| self->child_pidfd_derived_nsfds[i] = ioctl(self->pidfd, info->pidfd_ioctl, 0); | |
| if (self->child_pidfd_derived_nsfds[i] < 0) { | |
| EXPECT_EQ(errno, EOPNOTSUPP) { | |
| TH_LOG("%m - Failed to derive %s namespace from pidfd of process %d", | |
| info->name, self->pid); | |
| } | |
| } | |
| } | |
| /* Create task that exits right away. */ | |
| self->child_pid_exited = create_child(&self->child_pidfd_exited, 0); | |
| EXPECT_GE(self->child_pid_exited, 0); | |
| if (self->child_pid_exited == 0) { | |
| if (self->nsfds[PIDFD_NS_USER] >= 0 && unshare(CLONE_NEWUSER) < 0) | |
| _exit(EXIT_FAILURE); | |
| if (self->nsfds[PIDFD_NS_NET] >= 0 && unshare(CLONE_NEWNET) < 0) | |
| _exit(EXIT_FAILURE); | |
| _exit(EXIT_SUCCESS); | |
| } | |
| ASSERT_EQ(sys_waitid(P_PID, self->child_pid_exited, NULL, WEXITED | WNOWAIT), 0); | |
| self->pidfd = sys_pidfd_open(self->pid, 0); | |
| EXPECT_GE(self->pidfd, 0) { | |
| TH_LOG("%m - Failed to open pidfd for process %d", self->pid); | |
| } | |
| ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); | |
| EXPECT_EQ(ret, 0); | |
| /* Create tasks that will be stopped. */ | |
| if (self->nsfds[PIDFD_NS_USER] >= 0 && self->nsfds[PIDFD_NS_PID] >= 0) | |
| self->child_pid1 = create_child(&self->child_pidfd1, CLONE_NEWUSER | CLONE_NEWPID); | |
| else if (self->nsfds[PIDFD_NS_PID] >= 0) | |
| self->child_pid1 = create_child(&self->child_pidfd1, CLONE_NEWPID); | |
| else if (self->nsfds[PIDFD_NS_USER] >= 0) | |
| self->child_pid1 = create_child(&self->child_pidfd1, CLONE_NEWUSER); | |
| else | |
| self->child_pid1 = create_child(&self->child_pidfd1, 0); | |
| EXPECT_GE(self->child_pid1, 0); | |
| if (self->child_pid1 == 0) { | |
| close(ipc_sockets[0]); | |
| if (self->nsfds[PIDFD_NS_MNT] >= 0 && unshare(CLONE_NEWNS) < 0) { | |
| TH_LOG("%m - Failed to unshare mount namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_CGROUP] >= 0 && unshare(CLONE_NEWCGROUP) < 0) { | |
| TH_LOG("%m - Failed to unshare cgroup namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_IPC] >= 0 && unshare(CLONE_NEWIPC) < 0) { | |
| TH_LOG("%m - Failed to unshare ipc namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_UTS] >= 0 && unshare(CLONE_NEWUTS) < 0) { | |
| TH_LOG("%m - Failed to unshare uts namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_NET] >= 0 && unshare(CLONE_NEWNET) < 0) { | |
| TH_LOG("%m - Failed to unshare net namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_TIME] >= 0 && !switch_timens()) { | |
| TH_LOG("%m - Failed to unshare time namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (write_nointr(ipc_sockets[1], "1", 1) < 0) | |
| _exit(EXIT_FAILURE); | |
| close(ipc_sockets[1]); | |
| pause(); | |
| _exit(EXIT_SUCCESS); | |
| } | |
| close(ipc_sockets[1]); | |
| ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); | |
| close(ipc_sockets[0]); | |
| ret = socketpair(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, ipc_sockets); | |
| EXPECT_EQ(ret, 0); | |
| if (self->nsfds[PIDFD_NS_USER] >= 0 && self->nsfds[PIDFD_NS_PID] >= 0) | |
| self->child_pid2 = create_child(&self->child_pidfd2, CLONE_NEWUSER | CLONE_NEWPID); | |
| else if (self->nsfds[PIDFD_NS_PID] >= 0) | |
| self->child_pid2 = create_child(&self->child_pidfd2, CLONE_NEWPID); | |
| else if (self->nsfds[PIDFD_NS_USER] >= 0) | |
| self->child_pid2 = create_child(&self->child_pidfd2, CLONE_NEWUSER); | |
| else | |
| self->child_pid2 = create_child(&self->child_pidfd2, 0); | |
| EXPECT_GE(self->child_pid2, 0); | |
| if (self->child_pid2 == 0) { | |
| close(ipc_sockets[0]); | |
| if (self->nsfds[PIDFD_NS_MNT] >= 0 && unshare(CLONE_NEWNS) < 0) { | |
| TH_LOG("%m - Failed to unshare mount namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_CGROUP] >= 0 && unshare(CLONE_NEWCGROUP) < 0) { | |
| TH_LOG("%m - Failed to unshare cgroup namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_IPC] >= 0 && unshare(CLONE_NEWIPC) < 0) { | |
| TH_LOG("%m - Failed to unshare ipc namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_UTS] >= 0 && unshare(CLONE_NEWUTS) < 0) { | |
| TH_LOG("%m - Failed to unshare uts namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_NET] >= 0 && unshare(CLONE_NEWNET) < 0) { | |
| TH_LOG("%m - Failed to unshare net namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (self->nsfds[PIDFD_NS_TIME] >= 0 && !switch_timens()) { | |
| TH_LOG("%m - Failed to unshare time namespace for process %d", self->pid); | |
| _exit(EXIT_FAILURE); | |
| } | |
| if (write_nointr(ipc_sockets[1], "1", 1) < 0) | |
| _exit(EXIT_FAILURE); | |
| close(ipc_sockets[1]); | |
| pause(); | |
| _exit(EXIT_SUCCESS); | |
| } | |
| close(ipc_sockets[1]); | |
| ASSERT_EQ(read_nointr(ipc_sockets[0], &c, 1), 1); | |
| close(ipc_sockets[0]); | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| char p[100]; | |
| const struct ns_info *info = &ns_info[i]; | |
| self->nsfds[i] = openat(proc_fd, info->name, O_RDONLY | O_CLOEXEC); | |
| if (self->nsfds[i] < 0) { | |
| EXPECT_EQ(errno, ENOENT) { | |
| TH_LOG("%m - Failed to open %s namespace for process %d", | |
| info->name, self->pid); | |
| } | |
| } | |
| ret = snprintf(p, sizeof(p), "/proc/%d/ns/%s", | |
| self->child_pid1, info->name); | |
| EXPECT_GT(ret, 0); | |
| EXPECT_LT(ret, sizeof(p)); | |
| self->child_nsfds1[i] = open(p, O_RDONLY | O_CLOEXEC); | |
| if (self->child_nsfds1[i] < 0) { | |
| EXPECT_EQ(errno, ENOENT) { | |
| TH_LOG("%m - Failed to open %s namespace for process %d", | |
| info->name, self->child_pid1); | |
| } | |
| } | |
| ret = snprintf(p, sizeof(p), "/proc/%d/ns/%s", | |
| self->child_pid2, info->name); | |
| EXPECT_GT(ret, 0); | |
| EXPECT_LT(ret, sizeof(p)); | |
| self->child_nsfds2[i] = open(p, O_RDONLY | O_CLOEXEC); | |
| if (self->child_nsfds2[i] < 0) { | |
| EXPECT_EQ(errno, ENOENT) { | |
| TH_LOG("%m - Failed to open %s namespace for process %d", | |
| info->name, self->child_pid1); | |
| } | |
| } | |
| self->child_pidfd_derived_nsfds1[i] = ioctl(self->child_pidfd1, info->pidfd_ioctl, 0); | |
| if (self->child_pidfd_derived_nsfds1[i] < 0) { | |
| EXPECT_EQ(errno, EOPNOTSUPP) { | |
| TH_LOG("%m - Failed to derive %s namespace from pidfd of process %d", | |
| info->name, self->child_pid1); | |
| } | |
| } | |
| self->child_pidfd_derived_nsfds2[i] = ioctl(self->child_pidfd2, info->pidfd_ioctl, 0); | |
| if (self->child_pidfd_derived_nsfds2[i] < 0) { | |
| EXPECT_EQ(errno, EOPNOTSUPP) { | |
| TH_LOG("%m - Failed to derive %s namespace from pidfd of process %d", | |
| info->name, self->child_pid2); | |
| } | |
| } | |
| } | |
| close(proc_fd); | |
| } | |
| FIXTURE_TEARDOWN(current_nsset) | |
| { | |
| int i; | |
| ASSERT_EQ(sys_pidfd_send_signal(self->child_pidfd1, | |
| SIGKILL, NULL, 0), 0); | |
| ASSERT_EQ(sys_pidfd_send_signal(self->child_pidfd2, | |
| SIGKILL, NULL, 0), 0); | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| if (self->nsfds[i] >= 0) | |
| close(self->nsfds[i]); | |
| if (self->child_nsfds1[i] >= 0) | |
| close(self->child_nsfds1[i]); | |
| if (self->child_nsfds2[i] >= 0) | |
| close(self->child_nsfds2[i]); | |
| if (self->child_pidfd_derived_nsfds[i] >= 0) | |
| close(self->child_pidfd_derived_nsfds[i]); | |
| if (self->child_pidfd_derived_nsfds1[i] >= 0) | |
| close(self->child_pidfd_derived_nsfds1[i]); | |
| if (self->child_pidfd_derived_nsfds2[i] >= 0) | |
| close(self->child_pidfd_derived_nsfds2[i]); | |
| } | |
| if (self->child_pidfd1 >= 0) | |
| EXPECT_EQ(0, close(self->child_pidfd1)); | |
| if (self->child_pidfd2 >= 0) | |
| EXPECT_EQ(0, close(self->child_pidfd2)); | |
| ASSERT_EQ(sys_waitid(P_PID, self->child_pid_exited, NULL, WEXITED), 0); | |
| ASSERT_EQ(sys_waitid(P_PID, self->child_pid1, NULL, WEXITED), 0); | |
| ASSERT_EQ(sys_waitid(P_PID, self->child_pid2, NULL, WEXITED), 0); | |
| } | |
| static int preserve_ns(const int pid, const char *ns) | |
| { | |
| int ret; | |
| char path[50]; | |
| ret = snprintf(path, sizeof(path), "/proc/%d/ns/%s", pid, ns); | |
| if (ret < 0 || (size_t)ret >= sizeof(path)) | |
| return -EIO; | |
| return open(path, O_RDONLY | O_CLOEXEC); | |
| } | |
| static int in_same_namespace(int ns_fd1, pid_t pid2, const char *ns) | |
| { | |
| int ns_fd2 = -EBADF; | |
| int ret = -1; | |
| struct stat ns_st1, ns_st2; | |
| ret = fstat(ns_fd1, &ns_st1); | |
| if (ret < 0) | |
| return -1; | |
| ns_fd2 = preserve_ns(pid2, ns); | |
| if (ns_fd2 < 0) | |
| return -1; | |
| ret = fstat(ns_fd2, &ns_st2); | |
| close(ns_fd2); | |
| if (ret < 0) | |
| return -1; | |
| /* processes are in the same namespace */ | |
| if ((ns_st1.st_dev == ns_st2.st_dev) && | |
| (ns_st1.st_ino == ns_st2.st_ino)) | |
| return 1; | |
| /* processes are in different namespaces */ | |
| return 0; | |
| } | |
| /* Test that we can't pass garbage to the kernel. */ | |
| TEST_F(current_nsset, invalid_flags) | |
| { | |
| ASSERT_NE(setns(self->pidfd, 0), 0); | |
| EXPECT_EQ(errno, EINVAL); | |
| ASSERT_NE(setns(self->pidfd, -1), 0); | |
| EXPECT_EQ(errno, EINVAL); | |
| ASSERT_NE(setns(self->pidfd, CLONE_VM), 0); | |
| EXPECT_EQ(errno, EINVAL); | |
| ASSERT_NE(setns(self->pidfd, CLONE_NEWUSER | CLONE_VM), 0); | |
| EXPECT_EQ(errno, EINVAL); | |
| } | |
| /* Test that we can't attach to a task that has already exited. */ | |
| TEST_F(current_nsset, pidfd_exited_child) | |
| { | |
| int i; | |
| pid_t pid; | |
| ASSERT_NE(setns(self->child_pidfd_exited, CLONE_NEWUSER | CLONE_NEWNET), | |
| 0); | |
| EXPECT_EQ(errno, ESRCH); | |
| pid = getpid(); | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| /* Verify that we haven't changed any namespaces. */ | |
| if (self->nsfds[i] >= 0) | |
| ASSERT_EQ(in_same_namespace(self->nsfds[i], pid, info->name), 1); | |
| } | |
| } | |
| TEST_F(current_nsset, pidfd_incremental_setns) | |
| { | |
| int i; | |
| pid_t pid; | |
| pid = getpid(); | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| int nsfd; | |
| if (self->child_nsfds1[i] < 0) | |
| continue; | |
| if (info->flag) { | |
| ASSERT_EQ(setns(self->child_pidfd1, info->flag), 0) { | |
| TH_LOG("%m - Failed to setns to %s namespace of %d via pidfd %d", | |
| info->name, self->child_pid1, | |
| self->child_pidfd1); | |
| } | |
| } | |
| /* Verify that we have changed to the correct namespaces. */ | |
| if (info->flag == CLONE_NEWPID) | |
| nsfd = self->nsfds[i]; | |
| else | |
| nsfd = self->child_nsfds1[i]; | |
| ASSERT_EQ(in_same_namespace(nsfd, pid, info->name), 1) { | |
| TH_LOG("setns failed to place us correctly into %s namespace of %d via pidfd %d", | |
| info->name, self->child_pid1, | |
| self->child_pidfd1); | |
| } | |
| TH_LOG("Managed to correctly setns to %s namespace of %d via pidfd %d", | |
| info->name, self->child_pid1, self->child_pidfd1); | |
| } | |
| } | |
| TEST_F(current_nsset, nsfd_incremental_setns) | |
| { | |
| int i; | |
| pid_t pid; | |
| pid = getpid(); | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| int nsfd; | |
| if (self->child_nsfds1[i] < 0) | |
| continue; | |
| if (info->flag) { | |
| ASSERT_EQ(setns(self->child_nsfds1[i], info->flag), 0) { | |
| TH_LOG("%m - Failed to setns to %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid1, | |
| self->child_nsfds1[i]); | |
| } | |
| } | |
| /* Verify that we have changed to the correct namespaces. */ | |
| if (info->flag == CLONE_NEWPID) | |
| nsfd = self->nsfds[i]; | |
| else | |
| nsfd = self->child_nsfds1[i]; | |
| ASSERT_EQ(in_same_namespace(nsfd, pid, info->name), 1) { | |
| TH_LOG("setns failed to place us correctly into %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid1, | |
| self->child_nsfds1[i]); | |
| } | |
| TH_LOG("Managed to correctly setns to %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid1, self->child_nsfds1[i]); | |
| } | |
| } | |
| TEST_F(current_nsset, pidfd_derived_nsfd_incremental_setns) | |
| { | |
| int i; | |
| pid_t pid; | |
| pid = getpid(); | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| int nsfd; | |
| if (self->child_pidfd_derived_nsfds1[i] < 0) | |
| continue; | |
| if (info->flag) { | |
| ASSERT_EQ(setns(self->child_pidfd_derived_nsfds1[i], info->flag), 0) { | |
| TH_LOG("%m - Failed to setns to %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid1, | |
| self->child_pidfd_derived_nsfds1[i]); | |
| } | |
| } | |
| /* Verify that we have changed to the correct namespaces. */ | |
| if (info->flag == CLONE_NEWPID) | |
| nsfd = self->child_pidfd_derived_nsfds[i]; | |
| else | |
| nsfd = self->child_pidfd_derived_nsfds1[i]; | |
| ASSERT_EQ(in_same_namespace(nsfd, pid, info->name), 1) { | |
| TH_LOG("setns failed to place us correctly into %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid1, | |
| self->child_pidfd_derived_nsfds1[i]); | |
| } | |
| TH_LOG("Managed to correctly setns to %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid1, self->child_pidfd_derived_nsfds1[i]); | |
| } | |
| } | |
| TEST_F(current_nsset, pidfd_one_shot_setns) | |
| { | |
| unsigned flags = 0; | |
| int i; | |
| pid_t pid; | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| if (self->child_nsfds1[i] < 0) | |
| continue; | |
| flags |= info->flag; | |
| TH_LOG("Adding %s namespace of %d to list of namespaces to attach to", | |
| info->name, self->child_pid1); | |
| } | |
| ASSERT_EQ(setns(self->child_pidfd1, flags), 0) { | |
| TH_LOG("%m - Failed to setns to namespaces of %d", | |
| self->child_pid1); | |
| } | |
| pid = getpid(); | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| int nsfd; | |
| if (self->child_nsfds1[i] < 0) | |
| continue; | |
| /* Verify that we have changed to the correct namespaces. */ | |
| if (info->flag == CLONE_NEWPID) | |
| nsfd = self->nsfds[i]; | |
| else | |
| nsfd = self->child_nsfds1[i]; | |
| ASSERT_EQ(in_same_namespace(nsfd, pid, info->name), 1) { | |
| TH_LOG("setns failed to place us correctly into %s namespace of %d", | |
| info->name, self->child_pid1); | |
| } | |
| TH_LOG("Managed to correctly setns to %s namespace of %d", | |
| info->name, self->child_pid1); | |
| } | |
| } | |
| TEST_F(current_nsset, no_foul_play) | |
| { | |
| unsigned flags = 0; | |
| int i; | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| if (self->child_nsfds1[i] < 0) | |
| continue; | |
| flags |= info->flag; | |
| if (info->flag) /* No use logging pid_for_children. */ | |
| TH_LOG("Adding %s namespace of %d to list of namespaces to attach to", | |
| info->name, self->child_pid1); | |
| } | |
| ASSERT_EQ(setns(self->child_pidfd1, flags), 0) { | |
| TH_LOG("%m - Failed to setns to namespaces of %d vid pidfd %d", | |
| self->child_pid1, self->child_pidfd1); | |
| } | |
| /* | |
| * Can't setns to a user namespace outside of our hierarchy since we | |
| * don't have caps in there and didn't create it. That means that under | |
| * no circumstances should we be able to setns to any of the other | |
| * ones since they aren't owned by our user namespace. | |
| */ | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| if (self->child_nsfds2[i] < 0 || !info->flag) | |
| continue; | |
| ASSERT_NE(setns(self->child_pidfd2, info->flag), 0) { | |
| TH_LOG("Managed to setns to %s namespace of %d via pidfd %d", | |
| info->name, self->child_pid2, | |
| self->child_pidfd2); | |
| } | |
| TH_LOG("%m - Correctly failed to setns to %s namespace of %d via pidfd %d", | |
| info->name, self->child_pid2, | |
| self->child_pidfd2); | |
| ASSERT_NE(setns(self->child_nsfds2[i], info->flag), 0) { | |
| TH_LOG("Managed to setns to %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid2, | |
| self->child_nsfds2[i]); | |
| } | |
| TH_LOG("%m - Correctly failed to setns to %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid2, | |
| self->child_nsfds2[i]); | |
| } | |
| /* | |
| * Can't setns to a user namespace outside of our hierarchy since we | |
| * don't have caps in there and didn't create it. That means that under | |
| * no circumstances should we be able to setns to any of the other | |
| * ones since they aren't owned by our user namespace. | |
| */ | |
| for (i = 0; i < PIDFD_NS_MAX; i++) { | |
| const struct ns_info *info = &ns_info[i]; | |
| if (self->child_pidfd_derived_nsfds2[i] < 0 || !info->flag) | |
| continue; | |
| ASSERT_NE(setns(self->child_pidfd_derived_nsfds2[i], info->flag), 0) { | |
| TH_LOG("Managed to setns to %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid2, | |
| self->child_pidfd_derived_nsfds2[i]); | |
| } | |
| TH_LOG("%m - Correctly failed to setns to %s namespace of %d via nsfd %d", | |
| info->name, self->child_pid2, | |
| self->child_pidfd_derived_nsfds2[i]); | |
| } | |
| } | |
| TEST(setns_einval) | |
| { | |
| int fd; | |
| fd = sys_memfd_create("rostock", 0); | |
| EXPECT_GT(fd, 0); | |
| ASSERT_NE(setns(fd, 0), 0); | |
| EXPECT_EQ(errno, EINVAL); | |
| close(fd); | |
| } | |
| TEST_HARNESS_MAIN |