xref: /linux/tools/testing/selftests/pid_namespace/pidns_dead_ancestor.c (revision 018cf9bf786d501a27963dca6777f169f978e2a5)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <sched.h>
7 #include <signal.h>
8 #include <stdio.h>
9 #include <sys/mman.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <unistd.h>
13 
14 #include "kselftest_harness.h"
15 
16 #define STACK_SIZE (1024 * 1024)
17 
18 struct pidns_init_args {
19 	int ready_read;
20 	int ready_write;
21 	int release_read;
22 	int release_write;
23 };
24 
pidns_init(void * data)25 static int pidns_init(void *data)
26 {
27 	struct pidns_init_args *args = data;
28 	char release;
29 	int error = 0;
30 
31 	close(args->ready_read);
32 	close(args->release_write);
33 	if (unshare(CLONE_NEWPID))
34 		error = errno;
35 	if (write(args->ready_write, &error, sizeof(error)) != sizeof(error))
36 		return 1;
37 	close(args->ready_write);
38 	if (error)
39 		return 0;
40 	if (read(args->release_read, &release, sizeof(release)) != sizeof(release))
41 		return 1;
42 	close(args->release_read);
43 	return 0;
44 }
45 
create_unborn_pidns(void)46 static int create_unborn_pidns(void)
47 {
48 	struct pidns_init_args args;
49 	char path[64], release = 0;
50 	void *stack;
51 	pid_t child;
52 	int ready[2], release_pipe[2], error, status, nsfd = -1;
53 	ssize_t n;
54 
55 	if (pipe(ready))
56 		return -1;
57 	if (pipe(release_pipe)) {
58 		error = errno;
59 		close(ready[0]);
60 		close(ready[1]);
61 		errno = error;
62 		return -1;
63 	}
64 
65 	stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
66 		     MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
67 	if (stack == MAP_FAILED) {
68 		error = errno;
69 		close(ready[0]);
70 		close(ready[1]);
71 		close(release_pipe[0]);
72 		close(release_pipe[1]);
73 		errno = error;
74 		return -1;
75 	}
76 
77 	args = (struct pidns_init_args) {
78 		.ready_read = ready[0],
79 		.ready_write = ready[1],
80 		.release_read = release_pipe[0],
81 		.release_write = release_pipe[1],
82 	};
83 	child = clone(pidns_init, stack + STACK_SIZE, CLONE_NEWPID | SIGCHLD,
84 		      &args);
85 	if (child < 0)
86 		goto err_munmap;
87 
88 	close(ready[1]);
89 	close(release_pipe[0]);
90 	n = read(ready[0], &error, sizeof(error));
91 	if (n != sizeof(error)) {
92 		error = n < 0 ? errno : EIO;
93 		goto release_child;
94 	}
95 	if (error)
96 		goto release_child;
97 
98 	snprintf(path, sizeof(path), "/proc/%d/ns/pid_for_children", child);
99 	nsfd = open(path, O_RDONLY | O_CLOEXEC);
100 	if (nsfd < 0) {
101 		error = errno;
102 		goto release_child;
103 	}
104 
105 release_child:
106 	write(release_pipe[1], &release, sizeof(release));
107 	close(release_pipe[1]);
108 	close(ready[0]);
109 	if (waitpid(child, &status, 0) != child || !WIFEXITED(status) ||
110 	    WEXITSTATUS(status)) {
111 		if (!error)
112 			error = ECHILD;
113 		if (nsfd >= 0)
114 			close(nsfd);
115 		nsfd = -1;
116 	}
117 	munmap(stack, STACK_SIZE);
118 	if (error) {
119 		errno = error;
120 		return -1;
121 	}
122 	return nsfd;
123 
124 err_munmap:
125 	error = errno;
126 	munmap(stack, STACK_SIZE);
127 	close(ready[0]);
128 	close(ready[1]);
129 	close(release_pipe[0]);
130 	close(release_pipe[1]);
131 	errno = error;
132 	return -1;
133 }
134 
TEST(pidns_dead_ancestor_rejects_fork)135 TEST(pidns_dead_ancestor_rejects_fork)
136 {
137 	int original_fd, target_fd, fork_errno, ret;
138 	pid_t child;
139 
140 	target_fd = create_unborn_pidns();
141 	if (target_fd < 0) {
142 		if (errno == ENOENT)
143 			SKIP(return, "PID namespace handle unavailable through procfs\n");
144 		if (errno == EPERM || errno == EACCES || errno == EINVAL)
145 			SKIP(return, "PID namespace creation requires privileges\n");
146 		ASSERT_GE(target_fd, 0);
147 	}
148 
149 	original_fd = open("/proc/self/ns/pid_for_children", O_RDONLY | O_CLOEXEC);
150 	ASSERT_GE(original_fd, 0);
151 	ret = setns(target_fd, CLONE_NEWPID);
152 	if (ret) {
153 		if (errno == EPERM || errno == EACCES)
154 			SKIP(return, "setns into PID namespace requires privileges\n");
155 	}
156 	ASSERT_EQ(0, ret);
157 
158 	child = fork();
159 	fork_errno = errno;
160 	if (child == 0)
161 		_exit(0);
162 
163 	if (child > 0)
164 		ASSERT_EQ(child, waitpid(child, NULL, 0));
165 	ret = setns(original_fd, CLONE_NEWPID);
166 	close(original_fd);
167 	close(target_fd);
168 	ASSERT_EQ(0, ret);
169 
170 	ASSERT_EQ(-1, child);
171 	ASSERT_EQ(ENOMEM, fork_errno);
172 }
173 
174 TEST_HARNESS_MAIN
175