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