run_test.c revision 1.1.1.1 1 1.1 jmmv // Copyright 2012 Google Inc.
2 1.1 jmmv // All rights reserved.
3 1.1 jmmv //
4 1.1 jmmv // Redistribution and use in source and binary forms, with or without
5 1.1 jmmv // modification, are permitted provided that the following conditions are
6 1.1 jmmv // met:
7 1.1 jmmv //
8 1.1 jmmv // * Redistributions of source code must retain the above copyright
9 1.1 jmmv // notice, this list of conditions and the following disclaimer.
10 1.1 jmmv // * Redistributions in binary form must reproduce the above copyright
11 1.1 jmmv // notice, this list of conditions and the following disclaimer in the
12 1.1 jmmv // documentation and/or other materials provided with the distribution.
13 1.1 jmmv // * Neither the name of Google Inc. nor the names of its contributors
14 1.1 jmmv // may be used to endorse or promote products derived from this software
15 1.1 jmmv // without specific prior written permission.
16 1.1 jmmv //
17 1.1 jmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 1.1 jmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 1.1 jmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 1.1 jmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 1.1 jmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 1.1 jmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 1.1 jmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 1.1 jmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 1.1 jmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 1.1 jmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 1.1 jmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 1.1 jmmv
29 1.1 jmmv #if defined(HAVE_CONFIG_H)
30 1.1 jmmv # include "config.h"
31 1.1 jmmv #endif
32 1.1 jmmv
33 1.1 jmmv #include "run.h"
34 1.1 jmmv
35 1.1 jmmv #include <sys/resource.h>
36 1.1 jmmv #include <sys/stat.h>
37 1.1 jmmv #include <sys/wait.h>
38 1.1 jmmv
39 1.1 jmmv #include <err.h>
40 1.1 jmmv #include <errno.h>
41 1.1 jmmv #include <pwd.h>
42 1.1 jmmv #include <signal.h>
43 1.1 jmmv #include <stdbool.h>
44 1.1 jmmv #include <stdio.h>
45 1.1 jmmv #include <stdlib.h>
46 1.1 jmmv #include <unistd.h>
47 1.1 jmmv
48 1.1 jmmv #include <atf-c.h>
49 1.1 jmmv
50 1.1 jmmv #include "defs.h"
51 1.1 jmmv #include "env.h"
52 1.1 jmmv #include "error.h"
53 1.1 jmmv #include "fs.h"
54 1.1 jmmv
55 1.1 jmmv
56 1.1 jmmv /// Evalutes an expression and ensures it does not return an error.
57 1.1 jmmv ///
58 1.1 jmmv /// \param expr A expression that must evaluate to kyua_error_t.
59 1.1 jmmv #define RE(expr) ATF_REQUIRE(!kyua_error_is_set(expr))
60 1.1 jmmv
61 1.1 jmmv
62 1.1 jmmv static void check_env(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
63 1.1 jmmv KYUA_DEFS_NORETURN;
64 1.1 jmmv
65 1.1 jmmv
66 1.1 jmmv /// Subprocess that validates the cleanliness of the environment.
67 1.1 jmmv ///
68 1.1 jmmv /// \param unused_cookie NULL.
69 1.1 jmmv ///
70 1.1 jmmv /// \post Exits with success if the environment is clean; failure otherwise.
71 1.1 jmmv static void
72 1.1 jmmv check_env(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
73 1.1 jmmv {
74 1.1 jmmv bool failed = false;
75 1.1 jmmv
76 1.1 jmmv const char* empty[] = { "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
77 1.1 jmmv "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC",
78 1.1 jmmv "LC_TIME", NULL };
79 1.1 jmmv const char** iter;
80 1.1 jmmv for (iter = empty; *iter != NULL; ++iter) {
81 1.1 jmmv if (getenv(*iter) != NULL) {
82 1.1 jmmv failed = true;
83 1.1 jmmv printf("%s was not unset\n", *iter);
84 1.1 jmmv }
85 1.1 jmmv }
86 1.1 jmmv
87 1.1 jmmv if (strcmp(getenv("HOME"), ".") != 0) {
88 1.1 jmmv failed = true;
89 1.1 jmmv printf("HOME was not set to .\n");
90 1.1 jmmv }
91 1.1 jmmv if (strcmp(getenv("TZ"), "UTC") != 0) {
92 1.1 jmmv failed = true;
93 1.1 jmmv printf("TZ was not set to UTC\n");
94 1.1 jmmv }
95 1.1 jmmv if (strcmp(getenv("LEAVE_ME_ALONE"), "kill-some-day") != 0) {
96 1.1 jmmv failed = true;
97 1.1 jmmv printf("LEAVE_ME_ALONE was modified while it should not have been\n");
98 1.1 jmmv }
99 1.1 jmmv
100 1.1 jmmv exit(failed ? EXIT_FAILURE : EXIT_SUCCESS);
101 1.1 jmmv }
102 1.1 jmmv
103 1.1 jmmv
104 1.1 jmmv static void check_process_group(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
105 1.1 jmmv KYUA_DEFS_NORETURN;
106 1.1 jmmv
107 1.1 jmmv
108 1.1 jmmv /// Subprocess that validates that it has become the leader of a process group.
109 1.1 jmmv ///
110 1.1 jmmv /// \param unused_cookie NULL.
111 1.1 jmmv ///
112 1.1 jmmv /// \post Exits with success if the process lives in its own process group;
113 1.1 jmmv /// failure otherwise.
114 1.1 jmmv static void
115 1.1 jmmv check_process_group(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
116 1.1 jmmv {
117 1.1 jmmv exit(getpgid(getpid()) == getpid() ? EXIT_SUCCESS : EXIT_FAILURE);
118 1.1 jmmv }
119 1.1 jmmv
120 1.1 jmmv
121 1.1 jmmv static void check_signals(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
122 1.1 jmmv KYUA_DEFS_NORETURN;
123 1.1 jmmv
124 1.1 jmmv
125 1.1 jmmv /// Subprocess that validates that signals have been reset to their defaults.
126 1.1 jmmv ///
127 1.1 jmmv /// \param unused_cookie NULL.
128 1.1 jmmv ///
129 1.1 jmmv /// \post Exits with success if the process has its signals reset to their
130 1.1 jmmv /// default values; failure otherwise.
131 1.1 jmmv static void
132 1.1 jmmv check_signals(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
133 1.1 jmmv {
134 1.1 jmmv int signo;
135 1.1 jmmv for (signo = 1; signo <= LAST_SIGNO; signo++) {
136 1.1 jmmv if (signo == SIGKILL || signo == SIGSTOP) {
137 1.1 jmmv // Don't attempt to check immutable signals, as this results in
138 1.1 jmmv // an unconditional error in some systems. E.g. Mac OS X 10.8
139 1.1 jmmv // reports 'Invalid argument' when querying SIGKILL.
140 1.1 jmmv continue;
141 1.1 jmmv }
142 1.1 jmmv
143 1.1 jmmv struct sigaction old_sa;
144 1.1 jmmv if (sigaction(signo, NULL, &old_sa) == -1) {
145 1.1 jmmv err(EXIT_FAILURE, "Failed to query signal information for %d",
146 1.1 jmmv signo);
147 1.1 jmmv }
148 1.1 jmmv if (old_sa.sa_handler != SIG_DFL) {
149 1.1 jmmv errx(EXIT_FAILURE, "Signal %d not reset to its default handler",
150 1.1 jmmv signo);
151 1.1 jmmv }
152 1.1 jmmv printf("Signal %d has its default handler set\n", signo);
153 1.1 jmmv }
154 1.1 jmmv
155 1.1 jmmv exit(EXIT_SUCCESS);
156 1.1 jmmv }
157 1.1 jmmv
158 1.1 jmmv
159 1.1 jmmv static void check_umask(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
160 1.1 jmmv KYUA_DEFS_NORETURN;
161 1.1 jmmv
162 1.1 jmmv
163 1.1 jmmv /// Subprocess that validates that the umask has been reset.
164 1.1 jmmv ///
165 1.1 jmmv /// \param unused_cookie NULL.
166 1.1 jmmv ///
167 1.1 jmmv /// \post Exits with success if the umask matches the expected value; failure
168 1.1 jmmv /// otherwise.
169 1.1 jmmv static void
170 1.1 jmmv check_umask(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
171 1.1 jmmv {
172 1.1 jmmv exit(umask(0) == 0022 ? EXIT_SUCCESS : EXIT_FAILURE);
173 1.1 jmmv }
174 1.1 jmmv
175 1.1 jmmv
176 1.1 jmmv static void check_work_directory(const void* cookie) KYUA_DEFS_NORETURN;
177 1.1 jmmv
178 1.1 jmmv
179 1.1 jmmv /// Subprocess that validates that the umask has been reset.
180 1.1 jmmv ///
181 1.1 jmmv /// \param cookie The name of a file to expect in the current directory.
182 1.1 jmmv ///
183 1.1 jmmv /// \post Exits with success if the umask matches the expected value; failure
184 1.1 jmmv /// otherwise.
185 1.1 jmmv static void
186 1.1 jmmv check_work_directory(const void* cookie)
187 1.1 jmmv {
188 1.1 jmmv const char* exp_file = (const char*)cookie;
189 1.1 jmmv exit(atf_utils_file_exists(exp_file) ? EXIT_SUCCESS : EXIT_FAILURE);
190 1.1 jmmv }
191 1.1 jmmv
192 1.1 jmmv
193 1.1 jmmv static void check_uid_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
194 1.1 jmmv KYUA_DEFS_NORETURN;
195 1.1 jmmv
196 1.1 jmmv
197 1.1 jmmv /// Subprocess that validates that the UID is not root.
198 1.1 jmmv ///
199 1.1 jmmv /// \param unused_cookie NULL.
200 1.1 jmmv ///
201 1.1 jmmv /// \post Exits with success if the UID is not root; failure otherwise.
202 1.1 jmmv static void
203 1.1 jmmv check_uid_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
204 1.1 jmmv {
205 1.1 jmmv exit(getuid() != 0 ? EXIT_SUCCESS : EXIT_FAILURE);
206 1.1 jmmv }
207 1.1 jmmv
208 1.1 jmmv
209 1.1 jmmv static void check_gid_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
210 1.1 jmmv KYUA_DEFS_NORETURN;
211 1.1 jmmv
212 1.1 jmmv
213 1.1 jmmv /// Subprocess that validates that the GID is not root.
214 1.1 jmmv ///
215 1.1 jmmv /// \param unused_cookie NULL.
216 1.1 jmmv ///
217 1.1 jmmv /// \post Exits with success if the GID is not root; failure otherwise.
218 1.1 jmmv static void
219 1.1 jmmv check_gid_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
220 1.1 jmmv {
221 1.1 jmmv exit(getgid() != 0 ? EXIT_SUCCESS : EXIT_FAILURE);
222 1.1 jmmv }
223 1.1 jmmv
224 1.1 jmmv
225 1.1 jmmv static void check_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
226 1.1 jmmv KYUA_DEFS_NORETURN;
227 1.1 jmmv
228 1.1 jmmv
229 1.1 jmmv /// Subprocess that validates that the UID and GID are not root.
230 1.1 jmmv ///
231 1.1 jmmv /// \param unused_cookie NULL.
232 1.1 jmmv ///
233 1.1 jmmv /// \post Exits with success if the UID and GID are not root; failure otherwise.
234 1.1 jmmv static void
235 1.1 jmmv check_not_root(const void* KYUA_DEFS_UNUSED_PARAM(cookie))
236 1.1 jmmv {
237 1.1 jmmv exit(getuid() != 0 && getgid() != 0 ? EXIT_SUCCESS : EXIT_FAILURE);
238 1.1 jmmv }
239 1.1 jmmv
240 1.1 jmmv
241 1.1 jmmv /// Uses kyua_fork, kyua_exec and kyua_wait to execute a subprocess.
242 1.1 jmmv ///
243 1.1 jmmv /// \param program Path to the program to run.
244 1.1 jmmv /// \param args Arguments to the program.
245 1.1 jmmv /// \param [out] exitstatus The exit status of the subprocess, if it exits
246 1.1 jmmv /// successfully without timing out nor receiving a signal.
247 1.1 jmmv ///
248 1.1 jmmv /// \return Returns the error code of kyua_run_wait (which should have the
249 1.1 jmmv /// error representation of the exec call in the subprocess).
250 1.1 jmmv static kyua_error_t
251 1.1 jmmv exec_check(const char* program, const char* const* args, int* exitstatus)
252 1.1 jmmv {
253 1.1 jmmv kyua_run_params_t run_params;
254 1.1 jmmv kyua_run_params_init(&run_params);
255 1.1 jmmv
256 1.1 jmmv pid_t pid;
257 1.1 jmmv kyua_error_t error = kyua_run_fork(&run_params, &pid);
258 1.1 jmmv if (!kyua_error_is_set(error) && pid == 0)
259 1.1 jmmv kyua_run_exec(program, args);
260 1.1 jmmv ATF_REQUIRE(!kyua_error_is_set(error));
261 1.1 jmmv int status; bool timed_out;
262 1.1 jmmv error = kyua_run_wait(pid, &status, &timed_out);
263 1.1 jmmv if (!kyua_error_is_set(error)) {
264 1.1 jmmv ATF_REQUIRE(!timed_out);
265 1.1 jmmv ATF_REQUIRE_MSG(WIFEXITED(status),
266 1.1 jmmv "Subprocess expected to exit successfully");
267 1.1 jmmv *exitstatus = WEXITSTATUS(status);
268 1.1 jmmv }
269 1.1 jmmv return error;
270 1.1 jmmv }
271 1.1 jmmv
272 1.1 jmmv
273 1.1 jmmv /// Uses kyua_fork and kyua_wait to spawn a subprocess.
274 1.1 jmmv ///
275 1.1 jmmv /// \param run_params The parameters to configure the subprocess. Can be NULL
276 1.1 jmmv /// to indicate to use the default set of parameters.
277 1.1 jmmv /// \param hook Any of the check_* functions provided in this module.
278 1.1 jmmv /// \param cookie The data to pass to the hook.
279 1.1 jmmv ///
280 1.1 jmmv /// \return True if the subprocess exits successfully; false otherwise.
281 1.1 jmmv static bool
282 1.1 jmmv fork_check(const kyua_run_params_t* run_params,
283 1.1 jmmv void (*hook)(const void*), const void* cookie)
284 1.1 jmmv {
285 1.1 jmmv kyua_run_params_t default_run_params;
286 1.1 jmmv if (run_params == NULL) {
287 1.1 jmmv kyua_run_params_init(&default_run_params);
288 1.1 jmmv run_params = &default_run_params;
289 1.1 jmmv }
290 1.1 jmmv
291 1.1 jmmv pid_t pid;
292 1.1 jmmv kyua_error_t error = kyua_run_fork(run_params, &pid);
293 1.1 jmmv if (!kyua_error_is_set(error) && pid == 0)
294 1.1 jmmv hook(cookie);
295 1.1 jmmv ATF_REQUIRE(!kyua_error_is_set(error));
296 1.1 jmmv int status; bool timed_out;
297 1.1 jmmv error = kyua_run_wait(pid, &status, &timed_out);
298 1.1 jmmv if (kyua_error_is_set(error))
299 1.1 jmmv atf_tc_fail("wait failed; unexpected problem during exec?");
300 1.1 jmmv ATF_REQUIRE(!timed_out);
301 1.1 jmmv return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS;
302 1.1 jmmv }
303 1.1 jmmv
304 1.1 jmmv
305 1.1 jmmv ATF_TC_WITHOUT_HEAD(run_params_init__defaults);
306 1.1 jmmv ATF_TC_BODY(run_params_init__defaults, tc)
307 1.1 jmmv {
308 1.1 jmmv kyua_run_params_t run_params;
309 1.1 jmmv kyua_run_params_init(&run_params);
310 1.1 jmmv
311 1.1 jmmv ATF_REQUIRE_EQ(60, run_params.timeout_seconds);
312 1.1 jmmv ATF_REQUIRE_EQ(getuid(), run_params.unprivileged_user);
313 1.1 jmmv ATF_REQUIRE_EQ(getgid(), run_params.unprivileged_group);
314 1.1 jmmv ATF_REQUIRE_STREQ(".", run_params.work_directory);
315 1.1 jmmv }
316 1.1 jmmv
317 1.1 jmmv
318 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_exec_wait__ok);
319 1.1 jmmv ATF_TC_BODY(fork_exec_wait__ok, tc)
320 1.1 jmmv {
321 1.1 jmmv const char* const args[] = {"sh", "-c", "exit 42", NULL};
322 1.1 jmmv int exitstatus = -1; // Shut up GCC warning.
323 1.1 jmmv const kyua_error_t error = exec_check("/bin/sh", args, &exitstatus);
324 1.1 jmmv ATF_REQUIRE(!kyua_error_is_set(error));
325 1.1 jmmv ATF_REQUIRE_EQ(42, exitstatus);
326 1.1 jmmv }
327 1.1 jmmv
328 1.1 jmmv
329 1.1 jmmv ATF_TC(fork_exec_wait__eacces);
330 1.1 jmmv ATF_TC_HEAD(fork_exec_wait__eacces, tc)
331 1.1 jmmv {
332 1.1 jmmv atf_tc_set_md_var(tc, "require.user", "unprivileged");
333 1.1 jmmv }
334 1.1 jmmv ATF_TC_BODY(fork_exec_wait__eacces, tc)
335 1.1 jmmv {
336 1.1 jmmv ATF_REQUIRE(mkdir("dir", 0000) != -1);
337 1.1 jmmv
338 1.1 jmmv const char* const args[] = {"foo", NULL};
339 1.1 jmmv int unused_exitstatus;
340 1.1 jmmv const kyua_error_t error = exec_check("./dir/foo", args,
341 1.1 jmmv &unused_exitstatus);
342 1.1 jmmv ATF_REQUIRE(kyua_error_is_set(error));
343 1.1 jmmv ATF_REQUIRE(kyua_error_is_type(error, "libc"));
344 1.1 jmmv ATF_REQUIRE_EQ(EACCES, kyua_libc_error_errno(error));
345 1.1 jmmv }
346 1.1 jmmv
347 1.1 jmmv
348 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_exec_wait__enoent);
349 1.1 jmmv ATF_TC_BODY(fork_exec_wait__enoent, tc)
350 1.1 jmmv {
351 1.1 jmmv const char* const args[] = {"foo", NULL};
352 1.1 jmmv int unused_exitstatus;
353 1.1 jmmv const kyua_error_t error = exec_check("./foo", args, &unused_exitstatus);
354 1.1 jmmv ATF_REQUIRE(kyua_error_is_set(error));
355 1.1 jmmv ATF_REQUIRE(kyua_error_is_type(error, "libc"));
356 1.1 jmmv ATF_REQUIRE_EQ(ENOENT, kyua_libc_error_errno(error));
357 1.1 jmmv }
358 1.1 jmmv
359 1.1 jmmv
360 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_wait__core_size);
361 1.1 jmmv ATF_TC_BODY(fork_wait__core_size, tc)
362 1.1 jmmv {
363 1.1 jmmv struct rlimit rl;
364 1.1 jmmv rl.rlim_cur = 0;
365 1.1 jmmv rl.rlim_max = RLIM_INFINITY;
366 1.1 jmmv if (setrlimit(RLIMIT_CORE, &rl) == -1)
367 1.1 jmmv atf_tc_skip("Failed to lower the core size limit");
368 1.1 jmmv
369 1.1 jmmv kyua_run_params_t run_params;
370 1.1 jmmv kyua_run_params_init(&run_params);
371 1.1 jmmv
372 1.1 jmmv pid_t pid;
373 1.1 jmmv kyua_error_t error = kyua_run_fork(&run_params, &pid);
374 1.1 jmmv if (!kyua_error_is_set(error) && pid == 0)
375 1.1 jmmv abort();
376 1.1 jmmv
377 1.1 jmmv ATF_REQUIRE(!kyua_error_is_set(error));
378 1.1 jmmv int status; bool timed_out;
379 1.1 jmmv error = kyua_run_wait(pid, &status, &timed_out);
380 1.1 jmmv if (kyua_error_is_set(error))
381 1.1 jmmv atf_tc_fail("wait failed; unexpected problem during exec?");
382 1.1 jmmv
383 1.1 jmmv ATF_REQUIRE(!timed_out);
384 1.1 jmmv ATF_REQUIRE(WIFSIGNALED(status));
385 1.1 jmmv ATF_REQUIRE_MSG(WCOREDUMP(status), "Core not dumped as expected");
386 1.1 jmmv }
387 1.1 jmmv
388 1.1 jmmv
389 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_wait__env);
390 1.1 jmmv ATF_TC_BODY(fork_wait__env, tc)
391 1.1 jmmv {
392 1.1 jmmv kyua_env_set("HOME", "/non-existent/directory");
393 1.1 jmmv kyua_env_set("LANG", "C");
394 1.1 jmmv kyua_env_set("LC_ALL", "C");
395 1.1 jmmv kyua_env_set("LC_COLLATE", "C");
396 1.1 jmmv kyua_env_set("LC_CTYPE", "C");
397 1.1 jmmv kyua_env_set("LC_MESSAGES", "C");
398 1.1 jmmv kyua_env_set("LC_MONETARY", "C");
399 1.1 jmmv kyua_env_set("LC_NUMERIC", "C");
400 1.1 jmmv kyua_env_set("LC_TIME", "C");
401 1.1 jmmv kyua_env_set("LEAVE_ME_ALONE", "kill-some-day");
402 1.1 jmmv kyua_env_set("TZ", "EST+5");
403 1.1 jmmv
404 1.1 jmmv ATF_REQUIRE_MSG(fork_check(NULL, check_env, NULL),
405 1.1 jmmv "Unclean environment in subprocess");
406 1.1 jmmv }
407 1.1 jmmv
408 1.1 jmmv
409 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_wait__process_group);
410 1.1 jmmv ATF_TC_BODY(fork_wait__process_group, tc)
411 1.1 jmmv {
412 1.1 jmmv ATF_REQUIRE_MSG(fork_check(NULL, check_process_group, NULL),
413 1.1 jmmv "Subprocess not in its own process group");
414 1.1 jmmv }
415 1.1 jmmv
416 1.1 jmmv
417 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_wait__signals);
418 1.1 jmmv ATF_TC_BODY(fork_wait__signals, tc)
419 1.1 jmmv {
420 1.1 jmmv ATF_REQUIRE_MSG(LAST_SIGNO > 10, "LAST_SIGNO as detected by configure is "
421 1.1 jmmv "suspiciously low");
422 1.1 jmmv
423 1.1 jmmv int signo;
424 1.1 jmmv for (signo = 1; signo <= LAST_SIGNO; signo++) {
425 1.1 jmmv if (signo == SIGKILL || signo == SIGSTOP) {
426 1.1 jmmv // Ignore immutable signals.
427 1.1 jmmv continue;
428 1.1 jmmv }
429 1.1 jmmv if (signo == SIGCHLD) {
430 1.1 jmmv // If we were to reset SIGCHLD to SIG_IGN (which is different than
431 1.1 jmmv // not touching the signal at all, leaving it at its default value),
432 1.1 jmmv // our child process will not become a zombie and the call to
433 1.1 jmmv // kyua_run_wait will fail. Avoid this.
434 1.1 jmmv continue;
435 1.1 jmmv }
436 1.1 jmmv
437 1.1 jmmv struct sigaction sa;
438 1.1 jmmv sa.sa_handler = SIG_IGN;
439 1.1 jmmv sigemptyset(&sa.sa_mask);
440 1.1 jmmv sa.sa_flags = 0;
441 1.1 jmmv printf("Ignoring signal %d\n", signo);
442 1.1 jmmv ATF_REQUIRE(sigaction(signo, &sa, NULL) != -1);
443 1.1 jmmv }
444 1.1 jmmv
445 1.1 jmmv ATF_REQUIRE_MSG(fork_check(NULL, check_signals, NULL),
446 1.1 jmmv "Signals not reset to their default state");
447 1.1 jmmv }
448 1.1 jmmv
449 1.1 jmmv
450 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_wait__timeout);
451 1.1 jmmv ATF_TC_BODY(fork_wait__timeout, tc)
452 1.1 jmmv {
453 1.1 jmmv kyua_run_params_t run_params;
454 1.1 jmmv kyua_run_params_init(&run_params);
455 1.1 jmmv run_params.timeout_seconds = 1;
456 1.1 jmmv
457 1.1 jmmv pid_t pid;
458 1.1 jmmv kyua_error_t error = kyua_run_fork(&run_params, &pid);
459 1.1 jmmv if (!kyua_error_is_set(error) && pid == 0) {
460 1.1 jmmv sigset_t mask;
461 1.1 jmmv sigemptyset(&mask);
462 1.1 jmmv for (;;)
463 1.1 jmmv sigsuspend(&mask);
464 1.1 jmmv }
465 1.1 jmmv ATF_REQUIRE(!kyua_error_is_set(error));
466 1.1 jmmv int status; bool timed_out;
467 1.1 jmmv kyua_run_wait(pid, &status, &timed_out);
468 1.1 jmmv ATF_REQUIRE(timed_out);
469 1.1 jmmv ATF_REQUIRE(WIFSIGNALED(status));
470 1.1 jmmv ATF_REQUIRE_EQ(SIGKILL, WTERMSIG(status));
471 1.1 jmmv }
472 1.1 jmmv
473 1.1 jmmv
474 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_wait__umask);
475 1.1 jmmv ATF_TC_BODY(fork_wait__umask, tc)
476 1.1 jmmv {
477 1.1 jmmv (void)umask(0222);
478 1.1 jmmv ATF_REQUIRE_MSG(fork_check(NULL, check_umask, NULL),
479 1.1 jmmv "Subprocess does not have the predetermined 0022 umask");
480 1.1 jmmv }
481 1.1 jmmv
482 1.1 jmmv
483 1.1 jmmv ATF_TC(fork_wait__unprivileged_user);
484 1.1 jmmv ATF_TC_HEAD(fork_wait__unprivileged_user, tc)
485 1.1 jmmv {
486 1.1 jmmv atf_tc_set_md_var(tc, "require.config", "unprivileged-user");
487 1.1 jmmv atf_tc_set_md_var(tc, "require.user", "root");
488 1.1 jmmv }
489 1.1 jmmv ATF_TC_BODY(fork_wait__unprivileged_user, tc)
490 1.1 jmmv {
491 1.1 jmmv const struct passwd* pw = getpwnam(atf_tc_get_config_var(
492 1.1 jmmv tc, "unprivileged-user"));
493 1.1 jmmv ATF_REQUIRE_MSG(pw != NULL, "Cannot find unprivileged user");
494 1.1 jmmv
495 1.1 jmmv kyua_run_params_t run_params;
496 1.1 jmmv kyua_run_params_init(&run_params);
497 1.1 jmmv run_params.unprivileged_user = pw->pw_uid;
498 1.1 jmmv
499 1.1 jmmv ATF_REQUIRE_MSG(fork_check(&run_params, check_uid_not_root, NULL),
500 1.1 jmmv "Subprocess is still running with UID set to root");
501 1.1 jmmv }
502 1.1 jmmv
503 1.1 jmmv
504 1.1 jmmv ATF_TC(fork_wait__unprivileged_group);
505 1.1 jmmv ATF_TC_HEAD(fork_wait__unprivileged_group, tc)
506 1.1 jmmv {
507 1.1 jmmv atf_tc_set_md_var(tc, "require.config", "unprivileged-user");
508 1.1 jmmv atf_tc_set_md_var(tc, "require.user", "root");
509 1.1 jmmv }
510 1.1 jmmv ATF_TC_BODY(fork_wait__unprivileged_group, tc)
511 1.1 jmmv {
512 1.1 jmmv const struct passwd* pw = getpwnam(atf_tc_get_config_var(
513 1.1 jmmv tc, "unprivileged-user"));
514 1.1 jmmv ATF_REQUIRE_MSG(pw != NULL, "Cannot find unprivileged user");
515 1.1 jmmv
516 1.1 jmmv kyua_run_params_t run_params;
517 1.1 jmmv kyua_run_params_init(&run_params);
518 1.1 jmmv run_params.unprivileged_group = pw->pw_gid;
519 1.1 jmmv
520 1.1 jmmv ATF_REQUIRE_MSG(fork_check(&run_params, check_gid_not_root, NULL),
521 1.1 jmmv "Subprocess is still running with GID set to root");
522 1.1 jmmv }
523 1.1 jmmv
524 1.1 jmmv
525 1.1 jmmv ATF_TC(fork_wait__unprivileged_both);
526 1.1 jmmv ATF_TC_HEAD(fork_wait__unprivileged_both, tc)
527 1.1 jmmv {
528 1.1 jmmv atf_tc_set_md_var(tc, "require.config", "unprivileged-user");
529 1.1 jmmv atf_tc_set_md_var(tc, "require.user", "root");
530 1.1 jmmv }
531 1.1 jmmv ATF_TC_BODY(fork_wait__unprivileged_both, tc)
532 1.1 jmmv {
533 1.1 jmmv const struct passwd* pw = getpwnam(atf_tc_get_config_var(
534 1.1 jmmv tc, "unprivileged-user"));
535 1.1 jmmv ATF_REQUIRE_MSG(pw != NULL, "Cannot find unprivileged user");
536 1.1 jmmv
537 1.1 jmmv kyua_run_params_t run_params;
538 1.1 jmmv kyua_run_params_init(&run_params);
539 1.1 jmmv run_params.unprivileged_user = pw->pw_uid;
540 1.1 jmmv run_params.unprivileged_group = pw->pw_gid;
541 1.1 jmmv
542 1.1 jmmv ATF_REQUIRE_MSG(fork_check(&run_params, check_not_root, NULL),
543 1.1 jmmv "Subprocess is still running with root privileges");
544 1.1 jmmv }
545 1.1 jmmv
546 1.1 jmmv
547 1.1 jmmv ATF_TC_WITHOUT_HEAD(fork_wait__work_directory);
548 1.1 jmmv ATF_TC_BODY(fork_wait__work_directory, tc)
549 1.1 jmmv {
550 1.1 jmmv ATF_REQUIRE(mkdir("the-work-directory", 0755) != -1);
551 1.1 jmmv atf_utils_create_file("the-work-directory/data-file", "%s", "");
552 1.1 jmmv
553 1.1 jmmv kyua_run_params_t run_params;
554 1.1 jmmv kyua_run_params_init(&run_params);
555 1.1 jmmv run_params.work_directory = "./the-work-directory";
556 1.1 jmmv ATF_REQUIRE_MSG(fork_check(&run_params, check_work_directory, "data-file"),
557 1.1 jmmv "Subprocess not in its own process group");
558 1.1 jmmv }
559 1.1 jmmv
560 1.1 jmmv
561 1.1 jmmv ATF_TC_WITHOUT_HEAD(work_directory__builtin_tmpdir);
562 1.1 jmmv ATF_TC_BODY(work_directory__builtin_tmpdir, tc)
563 1.1 jmmv {
564 1.1 jmmv char* tmpdir;
565 1.1 jmmv RE(kyua_fs_make_absolute("worktest", &tmpdir));
566 1.1 jmmv ATF_REQUIRE(mkdir(tmpdir, 0755) != -1);
567 1.1 jmmv RE(kyua_env_unset("TMPDIR"));
568 1.1 jmmv kyua_run_tmpdir = tmpdir;
569 1.1 jmmv
570 1.1 jmmv char* work_directory;
571 1.1 jmmv RE(kyua_run_work_directory_enter("template.XXXXXX", getuid(), getgid(),
572 1.1 jmmv &work_directory));
573 1.1 jmmv
574 1.1 jmmv {
575 1.1 jmmv char* template_test;
576 1.1 jmmv RE(kyua_fs_concat(&template_test, atf_tc_get_config_var(tc, "srcdir"),
577 1.1 jmmv "worktest", "template.XXXXXX", NULL));
578 1.1 jmmv ATF_REQUIRE(access(template_test, X_OK) == -1);
579 1.1 jmmv free(template_test);
580 1.1 jmmv }
581 1.1 jmmv
582 1.1 jmmv ATF_REQUIRE(access(work_directory, X_OK) != -1);
583 1.1 jmmv
584 1.1 jmmv ATF_REQUIRE(rmdir(tmpdir) == -1); // Not yet empty.
585 1.1 jmmv RE(kyua_run_work_directory_leave(&work_directory));
586 1.1 jmmv ATF_REQUIRE(rmdir(tmpdir) != -1);
587 1.1 jmmv free(tmpdir);
588 1.1 jmmv }
589 1.1 jmmv
590 1.1 jmmv
591 1.1 jmmv ATF_TC_WITHOUT_HEAD(work_directory__env_tmpdir);
592 1.1 jmmv ATF_TC_BODY(work_directory__env_tmpdir, tc)
593 1.1 jmmv {
594 1.1 jmmv char* tmpdir;
595 1.1 jmmv RE(kyua_fs_make_absolute("worktest", &tmpdir));
596 1.1 jmmv ATF_REQUIRE(mkdir(tmpdir, 0755) != -1);
597 1.1 jmmv RE(kyua_env_set("TMPDIR", tmpdir));
598 1.1 jmmv
599 1.1 jmmv char* work_directory;
600 1.1 jmmv RE(kyua_run_work_directory_enter("template.XXXXXX", getuid(), getgid(),
601 1.1 jmmv &work_directory));
602 1.1 jmmv
603 1.1 jmmv {
604 1.1 jmmv char* template_test;
605 1.1 jmmv RE(kyua_fs_concat(&template_test, atf_tc_get_config_var(tc, "srcdir"),
606 1.1 jmmv "worktest", "template.XXXXXX", NULL));
607 1.1 jmmv ATF_REQUIRE(access(template_test, X_OK) == -1);
608 1.1 jmmv free(template_test);
609 1.1 jmmv }
610 1.1 jmmv
611 1.1 jmmv ATF_REQUIRE(access(work_directory, X_OK) != -1);
612 1.1 jmmv
613 1.1 jmmv ATF_REQUIRE(rmdir(tmpdir) == -1); // Not yet empty.
614 1.1 jmmv RE(kyua_run_work_directory_leave(&work_directory));
615 1.1 jmmv ATF_REQUIRE(rmdir(tmpdir) != -1);
616 1.1 jmmv free(tmpdir);
617 1.1 jmmv }
618 1.1 jmmv
619 1.1 jmmv
620 1.1 jmmv ATF_TC(work_directory__permissions);
621 1.1 jmmv ATF_TC_HEAD(work_directory__permissions, tc)
622 1.1 jmmv {
623 1.1 jmmv atf_tc_set_md_var(tc, "require.config", "unprivileged-user");
624 1.1 jmmv atf_tc_set_md_var(tc, "require.user", "root");
625 1.1 jmmv }
626 1.1 jmmv ATF_TC_BODY(work_directory__permissions, tc)
627 1.1 jmmv {
628 1.1 jmmv const struct passwd* pw = getpwnam(atf_tc_get_config_var(
629 1.1 jmmv tc, "unprivileged-user"));
630 1.1 jmmv
631 1.1 jmmv printf("%d %d %d %d\n", getuid(), getgid(), pw->pw_uid, pw->pw_gid);
632 1.1 jmmv
633 1.1 jmmv char* work_directory;
634 1.1 jmmv RE(kyua_run_work_directory_enter("template.XXXXXX", pw->pw_uid, pw->pw_gid,
635 1.1 jmmv &work_directory));
636 1.1 jmmv
637 1.1 jmmv struct stat sb;
638 1.1 jmmv ATF_REQUIRE(stat(work_directory, &sb) != -1);
639 1.1 jmmv ATF_REQUIRE_EQ(pw->pw_uid, sb.st_uid);
640 1.1 jmmv ATF_REQUIRE_EQ(pw->pw_gid, sb.st_gid);
641 1.1 jmmv
642 1.1 jmmv RE(kyua_run_work_directory_leave(&work_directory));
643 1.1 jmmv }
644 1.1 jmmv
645 1.1 jmmv
646 1.1 jmmv ATF_TC(work_directory__mkdtemp_error);
647 1.1 jmmv ATF_TC_HEAD(work_directory__mkdtemp_error, tc)
648 1.1 jmmv {
649 1.1 jmmv atf_tc_set_md_var(tc, "require.user", "unprivileged");
650 1.1 jmmv }
651 1.1 jmmv ATF_TC_BODY(work_directory__mkdtemp_error, tc)
652 1.1 jmmv {
653 1.1 jmmv char* tmpdir;
654 1.1 jmmv RE(kyua_fs_make_absolute("worktest", &tmpdir));
655 1.1 jmmv ATF_REQUIRE(mkdir(tmpdir, 0555) != -1);
656 1.1 jmmv RE(kyua_env_set("TMPDIR", tmpdir));
657 1.1 jmmv
658 1.1 jmmv char* work_directory;
659 1.1 jmmv const kyua_error_t error = kyua_run_work_directory_enter(
660 1.1 jmmv "template.XXXXXX", getuid(), getgid(), &work_directory);
661 1.1 jmmv ATF_REQUIRE(kyua_error_is_set(error));
662 1.1 jmmv ATF_REQUIRE(kyua_error_is_type(error, "libc"));
663 1.1 jmmv ATF_REQUIRE_EQ(EACCES, kyua_libc_error_errno(error));
664 1.1 jmmv kyua_error_free(error);
665 1.1 jmmv
666 1.1 jmmv ATF_REQUIRE(rmdir(tmpdir) != -1); // Empty; subdirectory not created.
667 1.1 jmmv free(tmpdir);
668 1.1 jmmv }
669 1.1 jmmv
670 1.1 jmmv
671 1.1 jmmv ATF_TC(work_directory__permissions_error);
672 1.1 jmmv ATF_TC_HEAD(work_directory__permissions_error, tc)
673 1.1 jmmv {
674 1.1 jmmv atf_tc_set_md_var(tc, "require.user", "unprivileged");
675 1.1 jmmv }
676 1.1 jmmv ATF_TC_BODY(work_directory__permissions_error, tc)
677 1.1 jmmv {
678 1.1 jmmv char* tmpdir;
679 1.1 jmmv RE(kyua_fs_make_absolute("worktest", &tmpdir));
680 1.1 jmmv ATF_REQUIRE(mkdir(tmpdir, 0755) != -1);
681 1.1 jmmv RE(kyua_env_set("TMPDIR", tmpdir));
682 1.1 jmmv
683 1.1 jmmv char* work_directory;
684 1.1 jmmv const kyua_error_t error = kyua_run_work_directory_enter(
685 1.1 jmmv "template.XXXXXX", getuid() + 1, getgid(), &work_directory);
686 1.1 jmmv ATF_REQUIRE(kyua_error_is_set(error));
687 1.1 jmmv ATF_REQUIRE(kyua_error_is_type(error, "libc"));
688 1.1 jmmv ATF_REQUIRE_EQ(EPERM, kyua_libc_error_errno(error));
689 1.1 jmmv kyua_error_free(error);
690 1.1 jmmv
691 1.1 jmmv ATF_REQUIRE(rmdir(tmpdir) != -1); // Empty; subdirectory not created.
692 1.1 jmmv free(tmpdir);
693 1.1 jmmv }
694 1.1 jmmv
695 1.1 jmmv
696 1.1 jmmv /// Performs a signal delivery test to the work directory handling code.
697 1.1 jmmv ///
698 1.1 jmmv /// \param signo The signal to deliver.
699 1.1 jmmv static void
700 1.1 jmmv work_directory_signal_check(const int signo)
701 1.1 jmmv {
702 1.1 jmmv char* tmpdir;
703 1.1 jmmv RE(kyua_fs_make_absolute("worktest", &tmpdir));
704 1.1 jmmv ATF_REQUIRE(mkdir(tmpdir, 0755) != -1);
705 1.1 jmmv RE(kyua_env_set("TMPDIR", tmpdir));
706 1.1 jmmv
707 1.1 jmmv char* work_directory;
708 1.1 jmmv RE(kyua_run_work_directory_enter("template.XXXXXX", getuid(), getgid(),
709 1.1 jmmv &work_directory));
710 1.1 jmmv
711 1.1 jmmv kyua_run_params_t run_params;
712 1.1 jmmv kyua_run_params_init(&run_params);
713 1.1 jmmv run_params.work_directory = work_directory;
714 1.1 jmmv
715 1.1 jmmv pid_t pid;
716 1.1 jmmv RE(kyua_run_fork(&run_params, &pid));
717 1.1 jmmv if (pid == 0) {
718 1.1 jmmv sleep(run_params.timeout_seconds * 2);
719 1.1 jmmv abort();
720 1.1 jmmv }
721 1.1 jmmv
722 1.1 jmmv // This should cause the handled installed by the work_directory management
723 1.1 jmmv // code to terminate the subprocess so that we get a chance to run the
724 1.1 jmmv // cleanup code ourselves.
725 1.1 jmmv kill(getpid(), signo);
726 1.1 jmmv
727 1.1 jmmv int status; bool timed_out;
728 1.1 jmmv RE(kyua_run_wait(pid, &status, &timed_out));
729 1.1 jmmv ATF_REQUIRE(!timed_out);
730 1.1 jmmv ATF_REQUIRE(WIFSIGNALED(status));
731 1.1 jmmv ATF_REQUIRE_EQ(SIGKILL, WTERMSIG(status));
732 1.1 jmmv
733 1.1 jmmv ATF_REQUIRE(rmdir(tmpdir) == -1); // Not yet empty.
734 1.1 jmmv RE(kyua_run_work_directory_leave(&work_directory));
735 1.1 jmmv ATF_REQUIRE(rmdir(tmpdir) != -1);
736 1.1 jmmv free(tmpdir);
737 1.1 jmmv }
738 1.1 jmmv
739 1.1 jmmv
740 1.1 jmmv ATF_TC_WITHOUT_HEAD(work_directory__sighup);
741 1.1 jmmv ATF_TC_BODY(work_directory__sighup, tc)
742 1.1 jmmv {
743 1.1 jmmv work_directory_signal_check(SIGHUP);
744 1.1 jmmv }
745 1.1 jmmv
746 1.1 jmmv
747 1.1 jmmv ATF_TC_WITHOUT_HEAD(work_directory__sigint);
748 1.1 jmmv ATF_TC_BODY(work_directory__sigint, tc)
749 1.1 jmmv {
750 1.1 jmmv work_directory_signal_check(SIGINT);
751 1.1 jmmv }
752 1.1 jmmv
753 1.1 jmmv
754 1.1 jmmv ATF_TC_WITHOUT_HEAD(work_directory__sigterm);
755 1.1 jmmv ATF_TC_BODY(work_directory__sigterm, tc)
756 1.1 jmmv {
757 1.1 jmmv work_directory_signal_check(SIGTERM);
758 1.1 jmmv }
759 1.1 jmmv
760 1.1 jmmv
761 1.1 jmmv ATF_TP_ADD_TCS(tp)
762 1.1 jmmv {
763 1.1 jmmv ATF_TP_ADD_TC(tp, run_params_init__defaults);
764 1.1 jmmv
765 1.1 jmmv ATF_TP_ADD_TC(tp, fork_exec_wait__ok);
766 1.1 jmmv ATF_TP_ADD_TC(tp, fork_exec_wait__eacces);
767 1.1 jmmv ATF_TP_ADD_TC(tp, fork_exec_wait__enoent);
768 1.1 jmmv
769 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__core_size);
770 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__env);
771 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__process_group);
772 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__signals);
773 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__timeout);
774 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__umask);
775 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__unprivileged_user);
776 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__unprivileged_group);
777 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__unprivileged_both);
778 1.1 jmmv ATF_TP_ADD_TC(tp, fork_wait__work_directory);
779 1.1 jmmv
780 1.1 jmmv ATF_TP_ADD_TC(tp, work_directory__builtin_tmpdir);
781 1.1 jmmv ATF_TP_ADD_TC(tp, work_directory__env_tmpdir);
782 1.1 jmmv ATF_TP_ADD_TC(tp, work_directory__permissions);
783 1.1 jmmv ATF_TP_ADD_TC(tp, work_directory__permissions_error);
784 1.1 jmmv ATF_TP_ADD_TC(tp, work_directory__mkdtemp_error);
785 1.1 jmmv ATF_TP_ADD_TC(tp, work_directory__sighup);
786 1.1 jmmv ATF_TP_ADD_TC(tp, work_directory__sigint);
787 1.1 jmmv ATF_TP_ADD_TC(tp, work_directory__sigterm);
788 1.1 jmmv
789 1.1 jmmv return atf_no_error();
790 1.1 jmmv }
791