1 //===-- klee-replay.c -----------------------------------------------------===//
2 //
3 //                     The KLEE Symbolic Virtual Machine
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "klee-replay.h"
11 
12 #include "klee/ADT/KTest.h"
13 
14 #include <assert.h>
15 #include <errno.h>
16 #include <getopt.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/wait.h>
22 #include <time.h>
23 #include <unistd.h>
24 
25 #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
26 #include <signal.h>
27 
28 #ifndef fgetc_unlocked
29 #define fgetc_unlocked(x) fgetc (x)
30 #endif
31 
32 #ifndef fputc_unlocked
33 #define fputc_unlocked(x,y) fputc (x,y)
34 #endif
35 
36 #else
37 #include <sys/signal.h>
38 #endif
39 
40 #ifdef HAVE_SYS_CAPABILITY_H
41 #include <sys/capability.h>
42 #endif
43 
44 static void __emit_error(const char *msg);
45 
46 static KTest* input;
47 static unsigned obj_index;
48 
49 static const char *progname = 0;
50 static unsigned monitored_pid = 0;
51 static unsigned monitored_timeout;
52 
53 static char *rootdir = NULL;
54 static struct option long_options[] = {
55   {"create-files-only", required_argument, 0, 'f'},
56   {"chroot-to-dir", required_argument, 0, 'r'},
57   {"help", no_argument, 0, 'h'},
58   {"keep-replay-dir", no_argument, 0, 'k'},
59   {0, 0, 0, 0},
60 };
61 
stop_monitored(int process)62 static void stop_monitored(int process) {
63   fputs("KLEE-REPLAY: NOTE: TIMEOUT: ATTEMPTING GDB EXIT\n", stderr);
64   int pid = fork();
65   if (pid < 0) {
66     fputs("KLEE-REPLAY: ERROR: gdb_exit: fork failed\n", stderr);
67   } else if (pid == 0) {
68     /* Run gdb in a child process. */
69     const char *gdbargs[] = {
70       "/usr/bin/gdb",
71       "--pid", "",
72       "-q",
73       "--batch",
74       "--eval-command=call exit(1)",
75       0,
76       0
77     };
78     char pids[64];
79     snprintf(pids, sizeof(pids), "%d", process);
80 
81     gdbargs[2] = pids;
82     /* Make sure gdb doesn't talk to the user */
83     close(0);
84 
85     fputs("KLEE-REPLAY: NOTE: RUNNING GDB: ", stderr);
86     unsigned i;
87     for (i = 0; i != 5; ++i)
88       fprintf(stderr, "%s ", gdbargs[i]);
89     fputc('\n', stderr);
90 
91     execvp(gdbargs[0], (char * const *) gdbargs);
92     perror("execvp");
93     _exit(66);
94   } else {
95     /* Parent process, wait for gdb to finish. */
96     int res, status;
97     do {
98       res = waitpid(pid, &status, 0);
99     } while (res < 0 && errno == EINTR);
100 
101     if (res < 0) {
102       perror("waitpid");
103       _exit(66);
104     }
105   }
106 }
107 
int_handler(int signal)108 static void int_handler(int signal) {
109   fprintf(stderr, "KLEE-REPLAY: NOTE: %s: Received signal %d.  Killing monitored process(es)\n",
110           progname, signal);
111   if (monitored_pid) {
112     stop_monitored(monitored_pid);
113     /* Kill the process group of monitored_pid.  Since we called
114        setpgrp() for pid, this will not kill us, or any of our
115        ancestors */
116     kill(-monitored_pid, SIGKILL);
117   } else {
118     _exit(99);
119   }
120 }
121 
timeout_handler(int signal)122 static void timeout_handler(int signal) {
123   fprintf(stderr, "KLEE-REPLAY: NOTE: EXIT STATUS: TIMED OUT (%d seconds)\n",
124           monitored_timeout);
125   if (monitored_pid) {
126     stop_monitored(monitored_pid);
127     /* Kill the process group of monitored_pid.  Since we called
128        setpgrp() for pid, this will not kill us, or any of our
129        ancestors */
130     kill(-monitored_pid, SIGKILL);
131   } else {
132     _exit(88);
133   }
134 }
135 
process_status(int status,time_t elapsed,const char * pfx)136 void process_status(int status, time_t elapsed, const char *pfx) {
137   if (pfx)
138     fprintf(stderr, "KLEE-REPLAY: NOTE: %s: ", pfx);
139   if (WIFSIGNALED(status)) {
140     fprintf(stderr, "KLEE-REPLAY: NOTE: EXIT STATUS: CRASHED signal %d (%d seconds)\n",
141             WTERMSIG(status), (int) elapsed);
142     _exit(77);
143   } else if (WIFEXITED(status)) {
144     int rc = WEXITSTATUS(status);
145 
146     char msg[64];
147     if (rc == 0) {
148       strcpy(msg, "NORMAL");
149     } else {
150       snprintf(msg, sizeof(msg), "ABNORMAL %d", rc);
151     }
152     fprintf(stderr, "KLEE-REPLAY: NOTE: EXIT STATUS: %s (%d seconds)\n", msg, (int) elapsed);
153     _exit(rc);
154   } else {
155     fprintf(stderr, "KLEE-REPLAY: NOTE: EXIT STATUS: NONE (%d seconds)\n", (int) elapsed);
156     _exit(0);
157   }
158 }
159 
160 /* This function assumes that executable is a path pointing to some existing
161  * binary and rootdir is a path pointing to some directory.
162  */
strip_root_dir(char * executable,char * rootdir)163 static inline char *strip_root_dir(char *executable, char *rootdir) {
164   return executable + strlen(rootdir);
165 }
166 
run_monitored(char * executable,int argc,char ** argv)167 static void run_monitored(char *executable, int argc, char **argv) {
168   int pid;
169   const char *t = getenv("KLEE_REPLAY_TIMEOUT");
170   if (!t)
171     t = "10000000";
172   monitored_timeout = atoi(t);
173 
174   if (monitored_timeout==0) {
175     fprintf(stderr, "KLEE-REPLAY: ERROR: invalid timeout (%s)\n", t);
176     _exit(1);
177   }
178 
179   /* Kill monitored process(es) on SIGINT and SIGTERM */
180   signal(SIGINT, int_handler);
181   signal(SIGTERM, int_handler);
182 
183   signal(SIGALRM, timeout_handler);
184   pid = fork();
185   if (pid < 0) {
186     perror("fork");
187     _exit(66);
188   } else if (pid == 0) {
189     /* This process actually executes the target program.
190      *
191      * Create a new process group for pid, and the process tree it may spawn. We
192      * do this, because later on we might want to kill pid _and_ all processes
193      * spawned by it and its descendants.
194      */
195 #if !defined(__FreeBSD__) && !defined(__DragonFly__)
196     setpgrp();
197 #else
198     setpgrp(0, 0);
199 #endif
200 
201     if (!rootdir) {
202       if (chdir(replay_dir) != 0) {
203         perror("chdir");
204         _exit(66);
205       }
206 
207       execv(executable, argv);
208       perror("execv");
209       _exit(66);
210     }
211 
212     fprintf(stderr, "KLEE-REPLAY: NOTE: rootdir: %s\n", rootdir);
213     const char *msg;
214     if ((msg = "chdir", chdir(rootdir) == 0) &&
215       (msg = "chroot", chroot(rootdir) == 0)) {
216       msg = "execv";
217       executable = strip_root_dir(executable, rootdir);
218       argv[0] = strip_root_dir(argv[0], rootdir);
219       execv(executable, argv);
220     }
221     perror(msg);
222     _exit(66);
223   } else {
224     /* Parent process which monitors the child. */
225     int res, status;
226     time_t start = time(0);
227     sigset_t masked;
228 
229     sigemptyset(&masked);
230     sigaddset(&masked, SIGALRM);
231 
232     monitored_pid = pid;
233     alarm(monitored_timeout);
234     do {
235       res = waitpid(pid, &status, 0);
236     } while (res < 0 && errno == EINTR);
237 
238     if (res < 0) {
239       perror("waitpid");
240       _exit(66);
241     }
242 
243     /* Just in case, kill the process group of pid.  Since we called setpgrp()
244        for pid, this will not kill us, or any of our ancestors */
245     kill(-pid, SIGKILL);
246     process_status(status, time(0) - start, 0);
247   }
248 }
249 
250 #ifdef HAVE_SYS_CAPABILITY_H
251 /* ensure this process has CAP_SYS_CHROOT capability. */
ensure_capsyschroot(const char * executable)252 void ensure_capsyschroot(const char *executable) {
253   cap_t caps = cap_get_proc();  // all current capabilities.
254   cap_flag_value_t chroot_permitted, chroot_effective;
255 
256   if (!caps)
257     perror("cap_get_proc");
258   /* effective and permitted flags should be set for CAP_SYS_CHROOT. */
259   cap_get_flag(caps, CAP_SYS_CHROOT, CAP_PERMITTED, &chroot_permitted);
260   cap_get_flag(caps, CAP_SYS_CHROOT, CAP_EFFECTIVE, &chroot_effective);
261   if (chroot_permitted != CAP_SET || chroot_effective != CAP_SET) {
262     fputs("KLEE-REPLAY: ERROR: chroot: No CAP_SYS_CHROOT capability.\n", stderr);
263     exit(1);
264   }
265   cap_free(caps);
266 }
267 #endif
268 
usage(void)269 static void usage(void) {
270   fprintf(stderr,
271     "Usage: %s [option]... <executable> <ktest-file>...\n"
272     "   or: %s --create-files-only <ktest-file>\n"
273     "\n"
274     "-r, --chroot-to-dir=DIR  use chroot jail, requires CAP_SYS_CHROOT\n"
275     "-k, --keep-replay-dir    do not delete replay directory\n"
276     "-h, --help               display this help and exit\n"
277     "\n"
278     "Use KLEE_REPLAY_TIMEOUT environment variable to set a timeout (in seconds).\n",
279     progname, progname);
280   exit(1);
281 }
282 
283 
284 int keep_temps = 0;
285 
main(int argc,char ** argv)286 int main(int argc, char** argv) {
287   int prg_argc;
288   char ** prg_argv;
289 
290   progname = argv[0];
291 
292   if (argc < 3)
293     usage();
294 
295   int c, opt_index;
296   while ((c = getopt_long(argc, argv, "f:r:k", long_options, &opt_index)) != -1) {
297     switch (c) {
298     case 'f': {
299       /* Special case hack for only creating files and not actually executing
300        * the program. */
301       if (argc != 3)
302         usage();
303 
304       char *input_fname = optarg;
305 
306       input = kTest_fromFile(input_fname);
307       if (!input) {
308         fprintf(stderr, "KLEE-REPLAY: ERROR: input file %s not valid.\n", input_fname);
309         exit(1);
310       }
311 
312       prg_argc = input->numArgs;
313       prg_argv = input->args;
314       prg_argv[0] = argv[1];
315       klee_init_env(&prg_argc, &prg_argv);
316 
317       replay_create_files(&__exe_fs);
318       return 0;
319     }
320 
321     case 'r':
322       rootdir = optarg;
323       break;
324 
325     case 'k':
326       keep_temps = 1;
327       break;
328     }
329   }
330 
331   // Executable needs to be converted to an absolute path, as klee-replay calls
332   // chdir just before executing it
333   char executable[PATH_MAX];
334   if (!realpath(argv[optind], executable)) {
335     snprintf(executable, PATH_MAX, "KLEE-REPLAY: ERROR: executable %s:",
336              argv[optind]);
337     perror(executable);
338     exit(1);
339   }
340   /* Normal execution path ... */
341 
342   /* make sure this process has the CAP_SYS_CHROOT capability, if possible. */
343 #ifdef HAVE_SYS_CAPABILITY_H
344   if (rootdir)
345     ensure_capsyschroot(progname);
346 #endif
347 
348   /* rootdir should be a prefix of executable's path. */
349   if (rootdir && strstr(executable, rootdir) != executable) {
350     fputs("KLEE-REPLAY: ERROR: chroot: root dir should be a parent dir of executable.\n", stderr);
351     exit(1);
352   }
353 
354   int idx = 0;
355   for (idx = optind + 1; idx != argc; ++idx) {
356     char* input_fname = argv[idx];
357     unsigned i;
358 
359     input = kTest_fromFile(input_fname);
360     if (!input) {
361       fprintf(stderr, "KLEE-REPLAY: ERROR: input file %s not valid.\n",
362               input_fname);
363       exit(1);
364     }
365 
366     obj_index = 0;
367     prg_argc = input->numArgs;
368     prg_argv = input->args;
369     prg_argv[0] = argv[optind];
370     klee_init_env(&prg_argc, &prg_argv);
371     if (idx > 2)
372       fputc('\n', stderr);
373     fprintf(stderr, "KLEE-REPLAY: NOTE: Test file: %s\n"
374                     "KLEE-REPLAY: NOTE: Arguments: ", input_fname);
375     for (i=0; i != (unsigned) prg_argc; ++i) {
376       char *s = prg_argv[i];
377       if (s[0]=='A' && s[1] && !s[2]) s[1] = '\0';
378       fprintf(stderr, "\"%s\" ", prg_argv[i]);
379     }
380     fputc('\n', stderr);
381 
382     /* Create the input files, pipes, etc. */
383     replay_create_files(&__exe_fs);
384 
385     /* Run the test case machinery in a subprocess, eventually this parent
386        process should be a script or something which shells out to the actual
387        execution tool. */
388     int pid = fork();
389     if (pid < 0) {
390       perror("fork");
391       _exit(66);
392     } else if (pid == 0) {
393       /* Run the executable */
394       run_monitored(executable, prg_argc, prg_argv);
395       _exit(0);
396     } else {
397       /* Wait for the executable to finish. */
398       int res, status;
399 
400       do {
401         res = waitpid(pid, &status, 0);
402       } while (res < 0 && errno == EINTR);
403 
404       // Delete all files in the replay directory
405       replay_delete_files();
406 
407       if (res < 0) {
408         perror("waitpid");
409         _exit(66);
410       }
411     }
412   }
413 
414   return 0;
415 }
416 
417 
418 /* KLEE functions */
419 
__fputc_unlocked(int c,FILE * f)420 int __fputc_unlocked(int c, FILE *f) {
421   return fputc_unlocked(c, f);
422 }
423 
__fgetc_unlocked(FILE * f)424 int __fgetc_unlocked(FILE *f) {
425   return fgetc_unlocked(f);
426 }
427 
klee_get_errno()428 int klee_get_errno() {
429   return errno;
430 }
431 
klee_warning(char * name)432 void klee_warning(char *name) {
433   fprintf(stderr, "KLEE-REPLAY: klee_warning: %s\n", name);
434 }
435 
klee_warning_once(char * name)436 void klee_warning_once(char *name) {
437   fprintf(stderr, "KLEE-REPLAY: klee_warning_once: %s\n", name);
438 }
439 
klee_assume(uintptr_t x)440 unsigned klee_assume(uintptr_t x) {
441   if (!x) {
442     fputs("KLEE-REPLAY: klee_assume(0)!\n", stderr);
443   }
444   return 0;
445 }
446 
klee_is_symbolic(uintptr_t x)447 unsigned klee_is_symbolic(uintptr_t x) {
448   return 0;
449 }
450 
klee_prefer_cex(void * buffer,uintptr_t condition)451 void klee_prefer_cex(void *buffer, uintptr_t condition) {
452   ;
453 }
454 
klee_posix_prefer_cex(void * buffer,uintptr_t condition)455 void klee_posix_prefer_cex(void *buffer, uintptr_t condition) {
456   ;
457 }
458 
klee_make_symbolic(void * addr,size_t nbytes,const char * name)459 void klee_make_symbolic(void *addr, size_t nbytes, const char *name) {
460   /* XXX remove model version code once new tests gen'd */
461   if (obj_index >= input->numObjects) {
462     if (strcmp("model_version", name) == 0) {
463       assert(nbytes == 4);
464       *((int*) addr) = 0;
465     } else {
466       __emit_error("ran out of appropriate inputs");
467     }
468   } else {
469     KTestObject *boo = &input->objects[obj_index];
470 
471     if (strcmp("model_version", name) == 0 &&
472         strcmp("model_version", boo->name) != 0) {
473       assert(nbytes == 4);
474       *((int*) addr) = 0;
475     } else {
476       if (boo->numBytes != nbytes) {
477         fprintf(stderr, "KLEE-REPLAY: ERROR: make_symbolic mismatch, different sizes: "
478            "%d in input file, %lu in code\n", boo->numBytes, (unsigned long)nbytes);
479         exit(1);
480       } else {
481         memcpy(addr, boo->bytes, nbytes);
482         obj_index++;
483       }
484     }
485   }
486 }
487 
488 /* Redefined here so that we can check the value read. */
klee_range(int start,int end,const char * name)489 int klee_range(int start, int end, const char* name) {
490   int r;
491 
492   if (start >= end) {
493     fputs("KLEE-REPLAY: ERROR: klee_range: invalid range\n", stderr);
494     exit(1);
495   }
496 
497   if (start+1 == end)
498     return start;
499   else {
500     klee_make_symbolic(&r, sizeof r, name);
501 
502     if (r < start || r >= end) {
503       fprintf(stderr, "KLEE-REPLAY: ERROR: klee_range(%d, %d, %s) returned invalid result: %d\n",
504         start, end, name, r);
505       exit(1);
506     }
507 
508     return r;
509   }
510 }
511 
klee_report_error(const char * file,int line,const char * message,const char * suffix)512 void klee_report_error(const char *file, int line,
513                        const char *message, const char *suffix) {
514   __emit_error(message);
515 }
516 
klee_mark_global(void * object)517 void klee_mark_global(void *object) {
518   ;
519 }
520 
521 /*** HELPER FUNCTIONS ***/
522 
__emit_error(const char * msg)523 static void __emit_error(const char *msg) {
524   fprintf(stderr, "KLEE-REPLAY: ERROR: %s\n", msg);
525   exit(1);
526 }
527