1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 static const stress_help_t help[] = {
28 	{ NULL,	"pidfd N",	"start N workers exercising pidfd system call" },
29 	{ NULL,	"pidfd-ops N",	"stop after N pidfd bogo operations" },
30 	{ NULL,	NULL,		NULL }
31 };
32 
33 #if defined(HAVE_PIDFD_SEND_SIGNAL)
34 
stress_pidfd_open(pid_t pid,unsigned int flag)35 static int stress_pidfd_open(pid_t pid, unsigned int flag)
36 {
37 	int fd;
38 	const pid_t bad_pid = stress_get_unused_pid_racy(false);
39 
40 	/* Exercise pidfd_open with non-existent PID */
41 	fd = shim_pidfd_open(bad_pid, 0);
42 	if (fd >= 0)
43 		(void)close(fd);
44 
45 	/* Exercise pidfd_open with illegal flags */
46 	(void)shim_pidfd_open(pid, ~(1U));
47 
48 	/* Exercise pidfd_open with illegal PID */
49 	(void)shim_pidfd_open((pid_t)-1, 0);
50 
51 	fd = -1;
52 	/* Randomly try pidfd_open first */
53 	if (stress_mwc1()) {
54 		fd = shim_pidfd_open(pid, flag);
55 	}
56 	/* ..or fallback to open on /proc/$PID */
57 	if (fd < 0) {
58 		char buffer[1024];
59 		int o_flags = O_DIRECTORY | O_CLOEXEC;
60 
61 		(void)snprintf(buffer, sizeof(buffer), "/proc/%" PRIdMAX, (intmax_t)pid);
62 #if defined(PIDFD_NONBLOCK)
63 		if (flag & PIDFD_NONBLOCK)
64 			o_flags |= O_NONBLOCK;
65 #endif
66 		fd = open(buffer, o_flags);
67 	}
68 	return fd;
69 }
70 
stress_pidfd_supported(const char * name)71 static int stress_pidfd_supported(const char *name)
72 {
73 	int pidfd, ret;
74 	const pid_t pid = getpid();
75 	siginfo_t info;
76 
77 	pidfd = stress_pidfd_open(pid, 0);
78 	if (pidfd < 0) {
79 		pr_inf_skip("%s stressor will be skipped, cannot open proc entry on procfs\n",
80 			name);
81 		return -1;
82 	}
83 	ret = shim_pidfd_send_signal(pidfd, 0, NULL, 0);
84 	if (ret < 0) {
85 		if (errno == ENOSYS) {
86 			pr_inf_skip("pidfd stressor will be skipped, system call not implemented\n");
87 			(void)close(pidfd);
88 			return -1;
89 		}
90 		/* Something went wrong, but don't let stressor fail on that */
91 	}
92 
93 	/* initialized info to be safe */
94 	(void)memset(&info, 0, sizeof(info));
95 
96 	/*
97 	 * Exercise pidfd_send_signal with
98 	 * non-null pointer to info variable
99 	 */
100 	(void)shim_pidfd_send_signal(pidfd, 0, &info, 0);
101 
102 	/* Exercise pidfd_send_signal with illegal flags */
103 	(void)shim_pidfd_send_signal(pidfd, 0, NULL, ~(1U));
104 
105 	(void)close(pidfd);
106 	return 0;
107 }
108 
stress_pidfd_reap(pid_t pid,int pidfd)109 static void stress_pidfd_reap(pid_t pid, int pidfd)
110 {
111 	int status;
112 
113 	if (pid) {
114 		(void)kill(pid, SIGKILL);
115 		(void)shim_process_mrelease(pidfd, 0);
116 		(void)shim_waitpid(pid, &status, 0);
117 	}
118 	if (pidfd >= 0)
119 		(void)close(pidfd);
120 }
121 
122 /*
123  *  stress_pidfd
124  *	stress signalfd reads
125  */
stress_pidfd(const stress_args_t * args)126 static int stress_pidfd(const stress_args_t *args)
127 {
128 	const int bad_fd = stress_get_bad_fd();
129 
130 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
131 
132 	while (keep_stressing(args)) {
133 		pid_t pid;
134 
135 again:
136 		pid = fork();
137 		if (pid < 0) {
138 			if (stress_redo_fork(errno))
139 				goto again;
140 			if (!keep_stressing(args))
141 				goto finish;
142 			pr_fail("%s: fork failed, errno=%d (%s)\n",
143 				args->name, errno, strerror(errno));
144 			return EXIT_FAILURE;
145 		} else if (pid == 0) {
146 			(void)setpgid(0, g_pgrp);
147 			(void)pause();
148 			_exit(0);
149 		} else {
150 			/* Parent */
151 			int pidfd, ret;
152 			struct stat statbuf;
153 			void *ptr;
154 
155 #if defined(PIDFD_NONBLOCK)
156 			pidfd = stress_pidfd_open(pid, PIDFD_NONBLOCK);
157 			if (pidfd >= 0) {
158 #if defined(F_GETFL) && 0
159 				unsigned int flags;
160 
161 				flags = fcntl(pidfd, F_GETFL, 0);
162 				if ((flags & O_NONBLOCK) == 0)
163 					pr_fail("%s: pidfd_open opened using PIDFD_NONBLOCK "
164 						"but O_NONBLOCK is not set on the file\n",
165 						args->name);
166 #endif
167 				(void)close(pidfd);
168 			}
169 #endif
170 			pidfd = stress_pidfd_open(pid, 0);
171 			if (pidfd < 0) {
172 				/* Process not found, try again */
173 				stress_pidfd_reap(pid, pidfd);
174 				continue;
175 			}
176 
177 			/* Exercise fstat'ing the pidfd */
178 			ret = fstat(pidfd, &statbuf);
179 			(void)ret;
180 
181 			/* Exercise illegal mmap'ing the pidfd */
182 			ptr = mmap(NULL, args->page_size, PROT_READ,
183 				MAP_PRIVATE, pidfd, 0);
184 			if (ptr != MAP_FAILED)
185 				(void)munmap(ptr, args->page_size);
186 
187 			/* Try to get fd 0 on child pid */
188 			ret = shim_pidfd_getfd(pidfd, 0, 0);
189 			if (ret >= 0)
190 				(void)close(ret);
191 
192 			/* Exercise with invalid flags */
193 			ret = shim_pidfd_getfd(pidfd, 0, ~0U);
194 			if (ret >= 0)
195 				(void)close(ret);
196 
197 			/* Exercise with bad_fd */
198 			ret = shim_pidfd_getfd(pidfd, bad_fd, 0);
199 			if (ret >= 0)
200 				(void)close(ret);
201 
202 			ret = shim_pidfd_send_signal(pidfd, 0, NULL, 0);
203 			if (ret != 0) {
204 				if (errno == ENOSYS) {
205 					if (args->instance == 0)
206 						pr_inf_skip("%s: skipping stress test, system call is not implemented\n",
207 							args->name);
208 					stress_pidfd_reap(pid, pidfd);
209 					return EXIT_NOT_IMPLEMENTED;
210 				}
211 				pr_err("%s: pidfd_send_signal failed: errno=%d (%s)\n",
212 					args->name, errno, strerror(errno));
213 				stress_pidfd_reap(pid, pidfd);
214 				break;
215 			}
216 			ret = shim_pidfd_send_signal(pidfd, SIGSTOP, NULL, 0);
217 			if (ret != 0) {
218 				pr_err("%s: pidfd_send_signal (SIGSTOP), failed: errno=%d (%s)\n",
219 					args->name, errno, strerror(errno));
220 			}
221 			ret = shim_pidfd_send_signal(pidfd, SIGCONT, NULL, 0);
222 			if (ret != 0) {
223 				pr_err("%s: pidfd_send_signal (SIGCONT), failed: errno=%d (%s)\n",
224 					args->name, errno, strerror(errno));
225 			}
226 			stress_pidfd_reap(pid, pidfd);
227 		}
228 		inc_counter(args);
229 	}
230 finish:
231 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
232 
233 	return EXIT_SUCCESS;
234 }
235 
236 stressor_info_t stress_pidfd_info = {
237 	.stressor = stress_pidfd,
238 	.class = CLASS_INTERRUPT | CLASS_OS,
239 	.supported = stress_pidfd_supported,
240 	.help = help
241 };
242 #else
243 
stress_pidfd_supported(const char * name)244 static int stress_pidfd_supported(const char *name)
245 {
246 	pr_inf_skip("%s: stressor will be skipped, system call not supported at build time\n", name);
247 	return -1;
248 }
249 
250 stressor_info_t stress_pidfd_info = {
251 	.stressor = stress_not_implemented,
252 	.class = CLASS_INTERRUPT | CLASS_OS,
253 	.supported = stress_pidfd_supported,
254 	.help = help
255 };
256 #endif
257