klee
klee-replay.c
Go to the documentation of this file.
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__)
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
44static void __emit_error(const char *msg);
45
46static KTest* input;
47static unsigned obj_index;
48
49static const char *progname = 0;
50static unsigned monitored_pid = 0;
51static unsigned monitored_timeout;
52
53static char *rootdir = NULL;
54static 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
62static 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
108static 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) {
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
122static void timeout_handler(int signal) {
123 fprintf(stderr, "KLEE-REPLAY: NOTE: EXIT STATUS: TIMED OUT (%d seconds)\n",
125 if (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
136void 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 */
163static inline char *strip_root_dir(char *executable, char *rootdir) {
164 return executable + strlen(rootdir);
165}
166
167static 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#ifndef __FreeBSD__
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. */
252void 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
269static 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",
280 exit(1);
281}
282
283
285
286int 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
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
420int __fputc_unlocked(int c, FILE *f) {
421 return fputc_unlocked(c, f);
422}
423
424int __fgetc_unlocked(FILE *f) {
425 return fgetc_unlocked(f);
426}
427
429 return errno;
430}
431
432void klee_warning(char *name) {
433 fprintf(stderr, "KLEE-REPLAY: klee_warning: %s\n", name);
434}
435
436void klee_warning_once(char *name) {
437 fprintf(stderr, "KLEE-REPLAY: klee_warning_once: %s\n", name);
438}
439
440unsigned klee_assume(uintptr_t x) {
441 if (!x) {
442 fputs("KLEE-REPLAY: klee_assume(0)!\n", stderr);
443 }
444 return 0;
445}
446
447unsigned klee_is_symbolic(uintptr_t x) {
448 return 0;
449}
450
451void klee_prefer_cex(void *buffer, uintptr_t condition) {
452 ;
453}
454
455void klee_posix_prefer_cex(void *buffer, uintptr_t condition) {
456 ;
457}
458
459void 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 {
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. */
489int 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
512void klee_report_error(const char *file, int line,
513 const char *message, const char *suffix) {
515}
516
517void klee_mark_global(void *object) {
518 ;
519}
520
521/*** HELPER FUNCTIONS ***/
522
523static void __emit_error(const char *msg) {
524 fprintf(stderr, "KLEE-REPLAY: ERROR: %s\n", msg);
525 exit(1);
526}
KTest * kTest_fromFile(const char *path)
Definition: KTest.cpp:94
char replay_dir[]
Definition: file-creator.c:396
void replay_delete_files()
Definition: file-creator.c:444
void replay_create_files(exe_file_system_t *exe_fs)
Definition: file-creator.c:398
void klee_mark_global(void *object)
Definition: klee-replay.c:517
unsigned klee_assume(uintptr_t x)
Definition: klee-replay.c:440
static void run_monitored(char *executable, int argc, char **argv)
Definition: klee-replay.c:167
int __fgetc_unlocked(FILE *f)
Definition: klee-replay.c:424
static void timeout_handler(int signal)
Definition: klee-replay.c:122
int main(int argc, char **argv)
Definition: klee-replay.c:286
static unsigned monitored_timeout
Definition: klee-replay.c:51
void klee_warning(char *name)
Definition: klee-replay.c:432
void klee_warning_once(char *name)
Definition: klee-replay.c:436
int klee_get_errno()
Definition: klee-replay.c:428
int __fputc_unlocked(int c, FILE *f)
Definition: klee-replay.c:420
static void stop_monitored(int process)
Definition: klee-replay.c:62
void klee_prefer_cex(void *buffer, uintptr_t condition)
Definition: klee-replay.c:451
static KTest * input
Definition: klee-replay.c:46
static struct option long_options[]
Definition: klee-replay.c:54
static unsigned monitored_pid
Definition: klee-replay.c:50
static char * rootdir
Definition: klee-replay.c:53
static char * strip_root_dir(char *executable, char *rootdir)
Definition: klee-replay.c:163
static void int_handler(int signal)
Definition: klee-replay.c:108
unsigned klee_is_symbolic(uintptr_t x)
Definition: klee-replay.c:447
static unsigned obj_index
Definition: klee-replay.c:47
static void __emit_error(const char *msg)
Definition: klee-replay.c:523
void klee_make_symbolic(void *addr, size_t nbytes, const char *name)
Definition: klee-replay.c:459
static const char * progname
Definition: klee-replay.c:49
static void usage(void)
Definition: klee-replay.c:269
void process_status(int status, time_t elapsed, const char *pfx)
Definition: klee-replay.c:136
int keep_temps
Definition: klee-replay.c:284
int klee_range(int start, int end, const char *name)
Definition: klee-replay.c:489
void klee_report_error(const char *file, int line, const char *message, const char *suffix)
Definition: klee-replay.c:512
void klee_posix_prefer_cex(void *buffer, uintptr_t condition)
Definition: klee-replay.c:455
int const char * message
Definition: klee.h:75
int line
Definition: klee.h:74
int const char const char * suffix
Definition: klee.h:76
char * name
Definition: KTest.h:19
unsigned char * bytes
Definition: KTest.h:21
unsigned numBytes
Definition: KTest.h:20
Definition: KTest.h:25
unsigned numObjects
Definition: KTest.h:35
KTestObject * objects
Definition: KTest.h:36
unsigned numArgs
Definition: KTest.h:29
char ** args
Definition: KTest.h:30