1 /* 2 * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/resource.h> 31 #include <sys/time.h> 32 #include <sys/types.h> 33 #include <sys/wait.h> 34 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <signal.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <stdint.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <pwd.h> 44 45 #include <err.h> 46 47 #include <libprop/proplib.h> 48 49 #include "testcase.h" 50 #include "runlist.h" 51 #include "userland.h" 52 #include <dfregress.h> 53 54 static void 55 clean_child(pid_t pid) 56 { 57 kill(pid, SIGKILL); 58 } 59 60 static void 61 sig_handle(int sig __unused) 62 { 63 return; 64 } 65 66 int 67 run_userland(const char *binary, int argc, const char **argv, const char *interpreter, 68 int need_setuid, uid_t uid, struct timeval *timeout, int rc, int unify_output, 69 char *errbuf, size_t errbuf_sz, struct testcase_result *tr) 70 { 71 struct itimerval itim; 72 struct sigaction sa; 73 pid_t pid = -1, r_pid; 74 int r, status; 75 int fd_stdout = -1, fd_stderr = -1; 76 size_t sz_stdout, sz_stderr; 77 char stdout_file[256]; 78 char stderr_file[256]; 79 char **argv_copy; 80 81 /* Set sane defaults */ 82 bzero(tr, sizeof(*tr)); 83 tr->result = RESULT_NOTRUN; 84 85 strcpy(stdout_file, "/tmp/dfregress.XXXXXXXXXXXX"); 86 strcpy(stderr_file, "/tmp/dfregress.XXXXXXXXXXXX"); 87 fd_stdout = mkostemp(stdout_file, O_SYNC); 88 if (fd_stdout == -1) { 89 if (errbuf) 90 snprintf(errbuf, errbuf_sz, "Could not mkostemp(): " 91 "%s\n", strerror(errno)); 92 return -1; 93 } 94 95 if (!unify_output) { 96 fd_stderr = mkostemp(stderr_file, O_SYNC); 97 if (fd_stderr == -1) { 98 if (errbuf) 99 snprintf(errbuf, errbuf_sz, "Could not mkostemp(): " 100 "%s\n", strerror(errno)); 101 return -1; 102 } 103 } 104 105 106 if ((pid = fork()) == -1) { 107 if (errbuf) 108 snprintf(errbuf, errbuf_sz, "Could not fork to run " 109 "binary %s: %s\n", binary, strerror(errno)); 110 111 goto err_out; 112 } else if (pid > 0) { 113 /* parent */ 114 115 if (timeout != NULL) { 116 /* Ignore SIGALRM */ 117 bzero(&sa, sizeof(sa)); 118 sa.sa_handler = sig_handle; 119 sigaction(SIGALRM, &sa, NULL); 120 121 /* Set up timeout */ 122 itim.it_interval.tv_sec = 0; 123 itim.it_interval.tv_usec = 0; 124 itim.it_value = *timeout; 125 r = setitimer(ITIMER_REAL, &itim, NULL); 126 if (r == -1) { 127 if (errbuf) 128 snprintf(errbuf, errbuf_sz, "Could not " 129 "set up timer: %s", strerror(errno)); 130 131 /* Clean up child process! */ 132 goto err_out; 133 } 134 } 135 136 r_pid = wait4(pid, &status, 0, &tr->rusage); 137 if (r_pid == -1) { 138 if (errno == EINTR) { 139 /* Alarm timed out */ 140 tr->result = RESULT_TIMEOUT; 141 142 /* Clean up child process! */ 143 clean_child(pid); 144 } else if (errno == ECHILD) { 145 /* Child already exited somehow */ 146 tr->result = RESULT_UNKNOWN; 147 } else { 148 /* EFAULT */ 149 if (errbuf) 150 snprintf(errbuf, errbuf_sz, "Could not " 151 "wait4(): %s", strerror(errno)); 152 153 goto err_out; 154 } 155 } else { 156 if (WIFEXITED(status)) { 157 tr->result = (WEXITSTATUS(status) == rc) ? 158 RESULT_PASS : 159 (WEXITSTATUS(status) == EXIT_NOTRUN) ? 160 RESULT_NOTRUN : RESULT_FAIL; 161 162 tr->exit_value = WEXITSTATUS(status); 163 } else if (WIFSIGNALED(status)) { 164 tr->result = RESULT_SIGNALLED; 165 tr->signal = WTERMSIG(status); 166 tr->core_dumped = (WCOREDUMP(status)) ? 1 : 0; 167 } else { 168 tr->result = RESULT_UNKNOWN; 169 } 170 } 171 172 if (timeout != NULL) { 173 /* Disable timer */ 174 itim.it_value.tv_sec = 0; 175 itim.it_value.tv_usec = 0; 176 setitimer(ITIMER_REAL, &itim, NULL); 177 } 178 } else { 179 /* pid == 0, so we are the child */ 180 181 /* Redirect stdout and stderr */ 182 if (fd_stdout >= 0) { 183 dup2(fd_stdout, 1); 184 setvbuf(stdout, NULL, _IONBF, 0); 185 } 186 187 if ((fd_stderr >= 0) || (unify_output && fd_stdout >= 0)) { 188 dup2((unify_output) ? fd_stdout : fd_stderr, 2); 189 setvbuf((unify_output) ? stdout : stderr, 190 NULL, _IONBF, 0); 191 } 192 193 /* Set uid if necessary */ 194 if (need_setuid) { 195 r = setuid(uid); 196 if (r == -1) { 197 fprintf(stderr, "ERR: NOT RUN (setuid): %s", 198 strerror(errno)); 199 exit(EXIT_NOTRUN); 200 } 201 } 202 203 if (interpreter) { 204 /* 205 * Allocate argc + 3 arguments more as shown below: 206 * argv_copy[0] = interpreter 207 * argv_copy[1] = argv[0] 208 * argv_copy[argc+2] = NULL 209 * 210 * execvp requires the array to end with NULL. 211 */ 212 argv_copy = (char **)calloc(argc + 3, sizeof(char *)); 213 if (argv_copy == NULL) { 214 err(1, "could not calloc argv_copy memory"); 215 216 } 217 /* Insert the interpreter at pos 0 */ 218 argv_copy[0] = malloc(strlen(interpreter) + 1); 219 snprintf(argv_copy[0], strlen(interpreter) + 1, "%s", 220 interpreter); 221 222 /* We still need argv[0] when argc is 0 */ 223 for (int i = 0; i <= argc; i++) { 224 size_t len; 225 len = strlen(argv[i]) + 1; /* NULL-terminated */ 226 227 argv_copy[i + 1] = malloc(len); 228 if (argv_copy[i] == NULL) 229 err(1, "could not malloc memory"); 230 231 snprintf(argv_copy[i + 1], len, "%s", 232 argv[i]); 233 234 } 235 /* Null terminate the array */ 236 argv_copy[argc + 2] = NULL; 237 r = execvp(interpreter, argv_copy); 238 } else { 239 /* Try to exec() */ 240 r = execvp(binary, __DECONST(char **, argv)); 241 } 242 if (r == -1) { 243 /* 244 * If we couldn't exec(), notify parent that we didn't 245 * run. 246 */ 247 fprintf(stderr, "ERR: NOT RUN: %s", strerror(errno)); 248 exit(EXIT_NOTRUN); 249 } 250 } 251 252 /* Read stdout and stderr redirected file contents into memory */ 253 sz_stdout = (size_t)lseek(fd_stdout, 0, SEEK_END); 254 lseek(fd_stdout, 0, SEEK_SET); 255 256 tr->stdout_buf = malloc(sz_stdout + 1); 257 if (tr->stdout_buf == NULL) 258 err(1, "could not malloc fd buf memory"); 259 260 read(fd_stdout, tr->stdout_buf, sz_stdout); 261 tr->stdout_buf[sz_stdout] = '\0'; 262 263 close(fd_stdout); 264 unlink(stdout_file); 265 266 if (!unify_output) { 267 sz_stderr = (size_t)lseek(fd_stderr, 0, SEEK_END); 268 lseek(fd_stderr, 0, SEEK_SET); 269 270 tr->stderr_buf = malloc(sz_stderr + 1); 271 if (tr->stderr_buf == NULL) 272 err(1, "could not malloc fd buf memory"); 273 274 read(fd_stderr, tr->stderr_buf, sz_stderr); 275 tr->stderr_buf[sz_stderr] = '\0'; 276 277 close(fd_stderr); 278 unlink(stderr_file); 279 } 280 281 282 return 0; 283 /* NOTREACHED */ 284 285 err_out: 286 if (pid != -1) 287 clean_child(pid); 288 289 if (fd_stdout >= 0) { 290 close(fd_stdout); 291 unlink(stdout_file); 292 } 293 294 if (fd_stderr >= 0) { 295 close(fd_stderr); 296 unlink(stderr_file); 297 } 298 299 return -1; 300 } 301 302 int 303 run_simple_cmd(const char *binary, const char *arg, char *errbuf, 304 size_t errbuf_sz, struct testcase_result *tr) 305 { 306 const char *argv[3]; 307 char *s; 308 309 s = strrchr(binary, '/'); 310 311 argv[0] = (s == NULL) ? __DECONST(char *, binary) : s+1; 312 argv[1] = __DECONST(char *, arg); 313 argv[2] = NULL; 314 315 return run_userland(binary, /* executable */ 316 1, /* argc */ 317 argv, /* argv */ 318 NULL, /* interpreter */ 319 0, /* needs_setuid */ 320 0, /* runas_uid */ 321 NULL, /* timeout */ 322 0, /* rc */ 323 1, /* unify_output */ 324 errbuf, /* errbuf */ 325 errbuf_sz, /* errbuf_size */ 326 tr); /* testcase_result */ 327 } 328