xref: /netbsd-src/external/bsd/kyua-testers/dist/run.c (revision 754f425fc237c181450c91977727274098801c74)
1 // Copyright 2012 Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 //   notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 //   notice, this list of conditions and the following disclaimer in the
12 //   documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 //   may be used to endorse or promote products derived from this software
15 //   without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #if defined(HAVE_CONFIG_H)
30 #   include "config.h"
31 #endif
32 
33 #include "run.h"
34 
35 #include <sys/resource.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 #include <sys/wait.h>
39 
40 #include <assert.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <signal.h>
45 #include <stdbool.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #include "defs.h"
52 #include "env.h"
53 #include "error.h"
54 #include "fs.h"
55 #include "text.h"
56 
57 
58 /// Path to the temporary work directory to use.
59 const char* kyua_run_tmpdir = KYUA_TMPDIR;
60 #undef KYUA_TMPDIR  // We really want to use the variable, not the macro.
61 
62 
63 /// Whether we got a signal or not.
64 static volatile bool signal_fired = false;
65 
66 
67 /// Whether the process timed out or not.
68 static volatile bool process_timed_out = false;
69 
70 
71 /// If not -1, PID of the process to forcibly kill when we get a signal.
72 ///
73 /// Must be protected by protect() and unprotect().
74 static volatile pid_t pid_to_kill = -1;
75 
76 
77 /// Whether we are holding signals or not.
78 static bool protected = false;
79 
80 
81 /// Magic exit code to denote an error while preparing the subprocess.
82 static const int exit_setup_child = 124;
83 /// Magic exit code to denote an error in exec(3) that we do not handle.
84 static const int exit_exec_unknown = 123;
85 /// Magic exit code to denote an EACCES error in exec(3).
86 static const int exit_exec_eacces = 122;
87 /// Magic exit code to denote an ENOENT error in exec(3).
88 static const int exit_exec_enoent = 121;
89 
90 
91 /// Area to save the original SIGHUP handler.
92 static struct sigaction old_sighup;
93 /// Area to save the original SIGINT handler.
94 static struct sigaction old_sigint;
95 /// Area to save the original SIGTERM handler.
96 static struct sigaction old_sigterm;
97 /// Area to save the original SIGALRM handler.
98 static struct sigaction old_sigalrm;
99 /// Area to save the original realtime timer.
100 static struct itimerval old_timer;
101 
102 
103 /// Masks or unmasks all the signals programmed by this module.
104 ///
105 /// \param operation One of SIG_BLOCK or SIG_UNBLOCK.
106 static void
mask_handlers(const int operation)107 mask_handlers(const int operation)
108 {
109     sigset_t mask;
110     sigemptyset(&mask);
111     sigaddset(&mask, SIGALRM);
112     sigaddset(&mask, SIGINT);
113     sigaddset(&mask, SIGHUP);
114     sigaddset(&mask, SIGTERM);
115     const int ret = sigprocmask(operation, &mask, NULL);
116     assert(ret != -1);
117 }
118 
119 
120 /// Masks all signals programmed by this module.
121 static void
protect(void)122 protect(void)
123 {
124     mask_handlers(SIG_BLOCK);
125     protected = true;
126 }
127 
128 
129 /// Unmasks all signals programmed by this module.
130 static void
unprotect(void)131 unprotect(void)
132 {
133     protected = false;
134     mask_handlers(SIG_UNBLOCK);
135 }
136 
137 
138 /// Handler for signals that should abort execution.
139 ///
140 /// When called the first time, this handler kills any running subprocess so
141 /// that the cleanup routines can proceed.  Calling this a second time aborts
142 /// execution of the program.
143 ///
144 /// \param unused_signo Number of the captured signal.
145 static void
cleanup_handler(const int KYUA_DEFS_UNUSED_PARAM (signo))146 cleanup_handler(const int KYUA_DEFS_UNUSED_PARAM(signo))
147 {
148     static const char* clean_message = "Signal caught; cleaning up...\n";
149     static const char* abort_message = "Double signal caught; aborting...\n";
150 
151     protect();
152     if (!signal_fired) {
153         signal_fired = true;
154         if (write(STDERR_FILENO, clean_message, strlen(clean_message)) == -1) {
155             // Ignore.
156         }
157         if (pid_to_kill != -1) {
158             kill(pid_to_kill, SIGKILL);
159             pid_to_kill = -1;
160         }
161         unprotect();
162     } else {
163         if (write(STDERR_FILENO, abort_message, strlen(abort_message)) == -1) {
164             // Ignore.
165         }
166         if (pid_to_kill != -1) {
167             kill(pid_to_kill, SIGKILL);
168             pid_to_kill = -1;
169         }
170         abort();
171     }
172 }
173 
174 
175 /// Handler for signals that should terminate the active subprocess.
176 ///
177 /// \param unused_signo Number of the captured signal.
178 static void
timeout_handler(const int KYUA_DEFS_UNUSED_PARAM (signo))179 timeout_handler(const int KYUA_DEFS_UNUSED_PARAM(signo))
180 {
181     static const char* message = "Subprocess timed out; sending KILL "
182         "signal...\n";
183 
184     protect();
185     process_timed_out = true;
186     if (write(STDERR_FILENO, message, strlen(message)) == -1) {
187         // Ignore.
188     }
189     if (pid_to_kill != -1) {
190         kill(pid_to_kill, SIGKILL);
191         pid_to_kill = -1;
192     }
193     unprotect();
194 }
195 
196 
197 /// Installs a signal handler.
198 ///
199 /// \param signo Number of the signal to program.
200 /// \param handler Handler for the signal.
201 /// \param [out] old_sa Pointer to the sigaction structure in which to save the
202 ///     current signal handler data.
203 static void
setup_signal(const int signo,void (* handler)(const int),struct sigaction * old_sa)204 setup_signal(const int signo, void (*handler)(const int),
205              struct sigaction* old_sa)
206 {
207     struct sigaction sa;
208     sa.sa_handler = handler;
209     sigemptyset(&sa.sa_mask);
210     sa.sa_flags = SA_RESTART;
211 
212     const int ret = sigaction(signo, &sa, old_sa);
213     assert(ret != -1);
214 }
215 
216 
217 /// Installs a timer.
218 ///
219 /// \param seconds Deadline for the timer.
220 /// \param [out] old_itimerval Pointer to the itimerval structure in which to
221 ///     save the current timer data into.
222 static void
setup_timer(const int seconds,struct itimerval * old_itimerval)223 setup_timer(const int seconds, struct itimerval* old_itimerval)
224 {
225     struct itimerval new_timer;
226     new_timer.it_interval.tv_sec = 0;
227     new_timer.it_interval.tv_usec = 0;
228     new_timer.it_value.tv_sec = seconds;
229     new_timer.it_value.tv_usec = 0;
230     const int ret = setitimer(ITIMER_REAL, &new_timer, old_itimerval);
231     assert(ret != -1);
232 }
233 
234 
235 /// Resets the environment of the process to a known state.
236 ///
237 /// \param work_directory Path to the work directory being used.
238 ///
239 /// \return An error if there is a problem configuring the environment
240 /// variables, or OK if successful.  Note that if this returns an error, we have
241 /// left the environment in an inconsistent state.
242 static kyua_error_t
prepare_environment(const char * work_directory)243 prepare_environment(const char* work_directory)
244 {
245     kyua_error_t error;
246 
247     // TODO(jmmv): It might be better to do the opposite: just pass a good known
248     // set of variables to the child (aka HOME, PATH, ...).  But how do we
249     // determine this minimum set?
250 
251     const char* to_unset[] = { "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
252                                "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC",
253                                "LC_TIME", NULL };
254     const char** iter;
255     for (iter = to_unset; *iter != NULL; ++iter) {
256         error = kyua_env_unset(*iter);
257         if (kyua_error_is_set(error))
258             return error;
259     }
260 
261     error = kyua_env_set("HOME", work_directory);
262     if (kyua_error_is_set(error))
263         return error;
264 
265     error = kyua_env_set("TZ", "UTC");
266     if (kyua_error_is_set(error))
267         return error;
268 
269     error = kyua_env_set("__RUNNING_INSIDE_ATF_RUN", "internal-yes-value");
270     if (kyua_error_is_set(error))
271         return error;
272 
273     assert(!kyua_error_is_set(error));
274     return error;
275 }
276 
277 
278 /// Resets all signals to their default handlers.
279 static void
reset_signals(void)280 reset_signals(void)
281 {
282     int signo;
283 
284     for (signo = 1; signo <= LAST_SIGNO; ++signo) {
285         if (signo == SIGKILL || signo == SIGSTOP) {
286             // Don't attempt to reset immutable signals.
287             continue;
288         }
289 
290         struct sigaction sa;
291         sa.sa_handler = SIG_DFL;
292         sigemptyset(&sa.sa_mask);
293         sa.sa_flags = 0;
294         (void)sigaction(signo, &sa, NULL);
295     }
296 }
297 
298 
299 /// Raises core size limit to its possible maximum.
300 ///
301 /// This is a best-effort operation.  There is no guarantee that the operation
302 /// will yield a large-enough limit to generate any possible core file.
303 static void
unlimit_core_size(void)304 unlimit_core_size(void)
305 {
306     struct rlimit rl;
307     {
308         const int ret = getrlimit(RLIMIT_CORE, &rl);
309         assert(ret != -1);
310     }
311     rl.rlim_cur = rl.rlim_max;
312     const int ret = setrlimit(RLIMIT_CORE, &rl) != -1;
313     assert(ret != -1);
314 }
315 
316 
317 /// Cleans up the container process to run a new child.
318 ///
319 /// If there is any error during the setup, the new process is terminated
320 /// with an error code.
321 ///
322 /// \param run_params End-user parameters that describe how to isolate the
323 ///     newly-created process.
324 static void
setup_child(const kyua_run_params_t * run_params)325 setup_child(const kyua_run_params_t* run_params)
326 {
327     setpgid(getpid(), getpid());
328 
329     if (chdir(run_params->work_directory) == -1)
330         err(exit_setup_child, "chdir(%s) failed", run_params->work_directory);
331 
332     unlimit_core_size();
333     reset_signals();
334 
335     const kyua_error_t error = prepare_environment(run_params->work_directory);
336     if (kyua_error_is_set(error))
337         kyua_error_err(exit_setup_child, error, "Failed to configure the "
338                        "environment");
339 
340     (void)umask(0022);
341 
342     if (run_params->unprivileged_group != getgid()) {
343         if (setgid(run_params->unprivileged_group) == -1)
344             err(exit_setup_child, "setgid(%ld) failed; uid is %ld and gid "
345                 "is %ld", (long int)run_params->unprivileged_group,
346                 (long int)getuid(), (long int)getgid());
347     }
348     if (run_params->unprivileged_user != getuid()) {
349         if (setuid(run_params->unprivileged_user) == -1)
350             err(exit_setup_child, "setuid(%ld) failed; uid is %ld and gid "
351                 "is %ld", (long int)run_params->unprivileged_user,
352                 (long int)getuid(), (long int)getgid());
353     }
354 }
355 
356 
357 /// Constructs a path to a work directory based on a template.
358 ///
359 /// \param template Template of the work directory to create.  Should be a
360 ///      basename and must include the XXXXXX placeholder string.  The directory
361 ///      is created within the temporary directory specified by the TMPDIR
362 ///      environment variable or the builtin value in kyua_run_tmpdir.
363 /// \param [out] work_directory Pointer to a dynamically-allocated string
364 ///      containing the full path to the work directory.  The caller is
365 ///      responsible for releasing this string.
366 ///
367 /// \return An error if there is a problem (most likely OOM), or OK otherwise.
368 static kyua_error_t
build_work_directory_path(const char * template,char ** work_directory)369 build_work_directory_path(const char* template, char** work_directory)
370 {
371     assert(strstr(template, "XXXXXX") != NULL);
372 
373     const char* tmpdir = getenv("TMPDIR");
374     if (tmpdir == NULL)
375         return kyua_text_printf(work_directory, "%s/%s", kyua_run_tmpdir,
376                                 template);
377     else
378         return kyua_text_printf(work_directory, "%s/%s", tmpdir, template);
379 }
380 
381 
382 /// Initializes the run_params parameters with default values.
383 ///
384 /// \param [out] run_params The object to initialize.
385 void
kyua_run_params_init(kyua_run_params_t * run_params)386 kyua_run_params_init(kyua_run_params_t* run_params)
387 {
388     run_params->timeout_seconds = 60;
389     run_params->unprivileged_user = getuid();
390     run_params->unprivileged_group = getgid();
391     run_params->work_directory = ".";
392 }
393 
394 
395 /// Executes a program and exits if there is a problem.
396 ///
397 /// This routine is supposed to be used in conjunction with kyua_run_fork and
398 /// kyua_run_wait so that the various return codes of the exec system call are
399 /// properly propagated to the parent process.
400 ///
401 /// \param program Path to the program to run.
402 /// \param args Arguments to the program.
403 void
kyua_run_exec(const char * program,const char * const * args)404 kyua_run_exec(const char* program, const char* const* args)
405 {
406     (void)execvp(program, KYUA_DEFS_UNCONST(args));
407     switch (errno) {
408     case EACCES:
409         exit(exit_exec_eacces);
410     case ENOENT:
411         exit(exit_exec_enoent);
412     default:
413         err(exit_exec_unknown, "execvp failed");
414     }
415 }
416 
417 
418 /// Forks and isolates the child process.
419 ///
420 /// The created subprocess must be waited for with kyua_run_wait().
421 ///
422 /// \param run_params Parameters that describe how to isolate the newly-created
423 ///     process.
424 /// \param [out] out_pid The PID of the child in the parent, or 0 in the child.
425 ///     The value left here should only be accessed if this function does not
426 ///     return an error.
427 ///
428 /// \return An error object if fork(2) fails.
429 kyua_error_t
kyua_run_fork(const kyua_run_params_t * run_params,pid_t * const out_pid)430 kyua_run_fork(const kyua_run_params_t* run_params, pid_t* const out_pid)
431 {
432     protect();
433     pid_t pid = fork();
434     if (pid == -1) {
435         unprotect();
436         *out_pid = pid;  // Not necessary, but avoid mistakes in the caller.
437         return kyua_libc_error_new(errno, "fork failed");
438     } else if (pid == 0) {
439         unprotect();
440         setup_child(run_params);
441         *out_pid = pid;
442         return kyua_error_ok();
443     } else {
444         pid_to_kill = pid;
445         unprotect();
446 
447         setup_signal(SIGALRM, timeout_handler, &old_sigalrm);
448         process_timed_out = false;
449         setup_timer(run_params->timeout_seconds, &old_timer);
450 
451         *out_pid = pid;
452         return kyua_error_ok();
453     }
454 }
455 
456 
457 /// Waits for a process started via kyua_run_fork.
458 ///
459 /// \param pid The PID of the child to wait for.
460 /// \param [out] status The exit status of the awaited process.
461 /// \param [out] timed_out Whether the process timed out or not.
462 ///
463 /// \return An error if the process failed due to an problem in kyua_run_exec.
464 /// However, note that the wait for the process itself is expected to have been
465 /// successful.
466 kyua_error_t
kyua_run_wait(const pid_t pid,int * status,bool * timed_out)467 kyua_run_wait(const pid_t pid, int* status, bool* timed_out)
468 {
469     int tmp_status;
470     const pid_t waited_pid = waitpid(pid, &tmp_status, 0);
471     assert(pid == waited_pid);
472 
473     protect();
474     (void)setitimer(ITIMER_REAL, &old_timer, NULL);
475     (void)sigaction(SIGALRM, &old_sigalrm, NULL);
476     pid_to_kill = -1;
477     unprotect();
478 
479     killpg(pid, SIGKILL);
480 
481     if (WIFEXITED(tmp_status)) {
482         if (WEXITSTATUS(tmp_status) == exit_setup_child) {
483             return kyua_generic_error_new("Failed to isolate subprocess; "
484                                           "see stderr for details");
485         } else if (WEXITSTATUS(tmp_status) == exit_exec_eacces) {
486             return kyua_libc_error_new(EACCES, "execvp failed");
487         } else if (WEXITSTATUS(tmp_status) == exit_exec_enoent) {
488             return kyua_libc_error_new(ENOENT, "execvp failed");
489         } else if (WEXITSTATUS(tmp_status) == exit_exec_unknown) {
490             return kyua_generic_error_new("execvp failed; see stderr for "
491                                           "details");
492         } else {
493             // Fall-through.
494         }
495     }
496     *status = tmp_status;
497     *timed_out = process_timed_out;
498     return kyua_error_ok();
499 }
500 
501 
502 /// Creates a temporary directory for use by a subprocess.
503 ///
504 /// The temporary directory must be deleted with kyua_run_work_directory_leave.
505 ///
506 /// \param template Template of the work directory to create.  Should be a
507 ///      basename and must include the XXXXXX placeholder string.  The directory
508 ///      is created within the temporary directory specified by the TMPDIR
509 ///      environment variable or the builtin value.
510 /// \param uid User to set the owner of the directory to.
511 /// \param gid Group to set the owner of the directory to.
512 /// \param [out] out_work_directory Updated with a pointer to a dynamic string
513 ///      holding the path to the created work directory.  This must be passed as
514 ///      is to kyua_run_work_directory_leave, which takes care of freeing the
515 ///      memory.
516 ///
517 /// \return An error code if there is a problem creating the directory.
518 kyua_error_t
kyua_run_work_directory_enter(const char * template,const uid_t uid,const gid_t gid,char ** out_work_directory)519 kyua_run_work_directory_enter(const char* template, const uid_t uid,
520                               const gid_t gid, char** out_work_directory)
521 {
522     kyua_error_t error = kyua_error_ok();
523 
524     signal_fired = false;
525     setup_signal(SIGHUP, cleanup_handler, &old_sighup);
526     setup_signal(SIGINT, cleanup_handler, &old_sigint);
527     setup_signal(SIGTERM, cleanup_handler, &old_sigterm);
528 
529     char* work_directory;
530     error = build_work_directory_path(template, &work_directory);
531     if (kyua_error_is_set(error))
532         goto err_signals;
533 
534     if (mkdtemp(work_directory) == NULL) {
535         error = kyua_libc_error_new(errno, "mkdtemp(%s) failed",
536                                     work_directory);
537         goto err_work_directory_variable;
538     }
539 
540     if (uid != getuid() || gid != getgid()) {
541         if (chown(work_directory, uid, gid) == -1) {
542             error = kyua_libc_error_new(errno,
543                 "chown(%s, %ld, %ld) failed; uid is %ld and gid is %ld",
544                 work_directory, (long int)uid, (long int)gid,
545                 (long int)getuid(), (long int)getgid());
546             goto err_work_directory_file;
547         }
548     }
549 
550     *out_work_directory = work_directory;
551     assert(!kyua_error_is_set(error));
552     goto out;
553 
554 err_work_directory_file:
555     (void)rmdir(work_directory);
556 err_work_directory_variable:
557     free(work_directory);
558 err_signals:
559     (void)sigaction(SIGTERM, &old_sigterm, NULL);
560     (void)sigaction(SIGINT, &old_sigint, NULL);
561     (void)sigaction(SIGHUP, &old_sighup, NULL);
562 out:
563     return error;
564 }
565 
566 
567 /// Deletes a temporary directory created by kyua_run_work_directory_enter().
568 ///
569 /// \param [in,out] work_directory The pointer to the work_directory string as
570 ///     originally returned by kyua_run_work_directory_leave().  This is
571 ///     explicitly invalidated by this function to clearly denote that this
572 ///     performs the memory releasing.
573 ///
574 /// \return An error code if the cleanup of the directory fails.  Note that any
575 /// intermediate errors during the cleanup are sent to stderr.
576 kyua_error_t
kyua_run_work_directory_leave(char ** work_directory)577 kyua_run_work_directory_leave(char** work_directory)
578 {
579     kyua_error_t error = kyua_fs_cleanup(*work_directory);
580 
581     free(*work_directory);
582     *work_directory = NULL;
583 
584     (void)sigaction(SIGTERM, &old_sigterm, NULL);
585     (void)sigaction(SIGHUP, &old_sighup, NULL);
586     (void)sigaction(SIGINT, &old_sigint, NULL);
587 
588     // At this point, we have cleaned up the work directory and we could
589     // (should?)  re-deliver the signal to ourselves so that we terminated with
590     // the right code.  However, we just let this return and allow the caller
591     // code to perform any other cleanups instead.
592 
593     return error;
594 }
595