klee
file-creator.c
Go to the documentation of this file.
1//===-- file-creator.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 <assert.h>
13#include <errno.h>
14#include <fcntl.h>
15#include <ftw.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <time.h>
20#include <unistd.h>
21
22#include <sys/stat.h>
23#include <sys/wait.h>
24
25#ifdef HAVE_PTY_H
26#include <pty.h>
27#elif defined(HAVE_UTIL_H)
28#include <util.h>
29#elif defined(HAVE_LIBUTIL_H)
30#include <libutil.h>
31#endif
32
33#if defined(__APPLE__)
34#include <sys/termios.h>
35#else
36#include <termios.h>
37#endif
38
39static void create_file(int target_fd,
40 const char *target_name,
41 exe_disk_file_t *dfile,
42 const char *tmpdir);
43static void check_file(int index, exe_disk_file_t *dfile);
44
45
46#define __STDIN (-1)
47#define __STDOUT (-2)
48
49static int create_link(const char *fname,
50 exe_disk_file_t *dfile,
51 const char *tmpdir) {
52 char buf[PATH_MAX];
53 struct stat64 *s = dfile->stat;
54
55 // make sure that the .lnk suffix is not truncated
56 if (snprintf(buf, sizeof buf, "%s.lnk", fname) >= PATH_MAX) {
57 fputs("create_link: fname is too long for additional .lnk suffix", stderr);
58 return -1;
59 }
60
61 s->st_mode = (s->st_mode & ~S_IFMT) | S_IFREG;
62 create_file(-1, buf, dfile, tmpdir);
63
64 int res = symlink(buf, fname);
65 if (res < 0) {
66 perror("symlink");
67 }
68
69 return open(fname, O_RDWR);
70}
71
72
73static int create_dir(const char *fname, exe_disk_file_t *dfile,
74 const char *tmpdir) {
75 int res = mkdir(fname, dfile->stat->st_mode);
76 if (res < 0) {
77 perror("mkdir");
78 return -1;
79 }
80 return open(fname, O_RDWR);
81}
82
83double getTime() {
84 struct timeval t;
85 gettimeofday(&t, NULL);
86
87 return (double) t.tv_sec + ((double) t.tv_usec / 1000000.0);
88}
89
91int wait_for_timeout_or_exit(pid_t pid, const char *name, int *statusp) {
92 char *t = getenv("KLEE_REPLAY_TIMEOUT");
93 int timeout = t ? atoi(t) : 5;
94 double wait = timeout * .5;
95 double start = getTime();
96 fprintf(stderr, "KLEE-REPLAY: NOTE: %s: waiting %.2fs\n", name, wait);
97 while (getTime() - start < wait) {
98 struct timespec r = {0, 1000000};
99 nanosleep(&r, 0);
100 int res = waitpid(pid, statusp, WNOHANG);
101 if (res==pid)
102 return 1;
103 }
104
105 return 0;
106}
107
108static int create_char_dev(const char *fname, exe_disk_file_t *dfile,
109 const char *tmpdir) {
110 struct stat64 *s = dfile->stat;
111 unsigned flen = dfile->size;
112 char* contents = dfile->contents;
113
114 // Assume tty, kinda broken, need an actual device id or something
115 struct termios term, *ts=&term;
116 struct winsize win = { 24, 80, 0, 0 };
117 /* Just copied from my system, munged to match what fields
118 uclibc thinks are there. */
119 ts->c_iflag = 27906;
120 ts->c_oflag = 5;
121 ts->c_cflag = 1215;
122 ts->c_lflag = 35287;
123#ifdef __GLIBC__
124 ts->c_line = 0;
125#endif
126 ts->c_cc[0] = '\x03';
127 ts->c_cc[1] = '\x1c';
128 ts->c_cc[2] = '\x7f';
129 ts->c_cc[3] = '\x15';
130 ts->c_cc[4] = '\x04';
131 ts->c_cc[5] = '\x00';
132 ts->c_cc[6] = '\x01';
133 ts->c_cc[7] = '\xff';
134 ts->c_cc[8] = '\x11';
135 ts->c_cc[9] = '\x13';
136 ts->c_cc[10] = '\x1a';
137 ts->c_cc[11] = '\xff';
138 ts->c_cc[12] = '\x12';
139 ts->c_cc[13] = '\x0f';
140 ts->c_cc[14] = '\x17';
141 ts->c_cc[15] = '\x16';
142 ts->c_cc[16] = '\xff';
143 ts->c_cc[17] = '\x00';
144 ts->c_cc[18] = '\x00';
145
146 {
147 char name[1024];
148 int amaster, aslave;
149 int res = openpty(&amaster, &aslave, name, &term, &win);
150 if (res < 0) {
151 perror("openpty");
152 exit(1);
153 }
154
155 if (symlink(name, fname) == -1) {
156 fputs("KLEE-REPLAY: ERROR: unable to create sym link to tty\n", stderr);
157 perror("symlink");
158 }
159
160 // pty will not be world writeable
161 s->st_mode &= ~02;
162
163 pid_t pid = fork();
164 if (pid < 0) {
165 perror("fork failed\n");
166 exit(1);
167 } else if (pid == 0) {
168 close(amaster);
169
170 fputs("KLEE-REPLAY: NOTE: pty slave: setting raw mode\n", stderr);
171 {
172 struct termios mode;
173
174 int res = tcgetattr(aslave, &mode);
175 assert(!res);
176 mode.c_iflag = IGNBRK;
177#if defined(__APPLE__) || defined(__FreeBSD__)
178 mode.c_oflag &= ~(ONLCR | OCRNL | ONLRET);
179#else
180 mode.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONLRET);
181#endif
182 mode.c_lflag = 0;
183 mode.c_cc[VMIN] = 1;
184 mode.c_cc[VTIME] = 0;
185 res = tcsetattr(aslave, TCSANOW, &mode);
186 assert(res == 0);
187 }
188
189 return aslave;
190 } else {
191 unsigned pos = 0;
192 int status;
193 fputs("KLEE-REPLAY: NOTE: pty master: starting\n", stderr);
194 close(aslave);
195
196 while (pos < flen) {
197 ssize_t res = write(amaster, &contents[pos], flen - pos);
198 if (res<0) {
199 if (errno != EINTR) {
200 fputs("KLEE-REPLAY: NOTE: pty master: write error\n", stderr);
201 perror("errno");
202 break;
203 }
204 } else if (res) {
205 fprintf(stderr, "KLEE-REPLAY: NOTE: pty master: wrote: %zd (of %d)\n", res, flen);
206 pos += res;
207 }
208 }
209
210 if (wait_for_timeout_or_exit(pid, "pty master", &status))
211 goto pty_exit;
212
213 fputs("KLEE-REPLAY: NOTE: pty master: closing & waiting\n", stderr);
214 close(amaster);
215 while (1) {
216 pid_t res = waitpid(pid, &status, 0);
217 if (res < 0) {
218 if (errno != EINTR)
219 break;
220 } else {
221 break;
222 }
223 }
224
225 pty_exit:
226 close(amaster);
227 fputs("KLEE-REPLAY: NOTE: pty master: done\n", stderr);
228 process_status(status, 0, "PTY MASTER");
229 }
230 }
231}
232
233static int create_pipe(const char *fname, exe_disk_file_t *dfile,
234 const char *tmpdir) {
235 //struct stat64 *s = dfile->stat;
236 unsigned flen = dfile->size;
237 char* contents = dfile->contents;
238
239 // XXX what is direction ? need more data
240 pid_t pid;
241 int fds[2];
242 int res = pipe(fds);
243 if (res < 0) {
244 perror("pipe");
245 exit(1);
246 }
247
248 pid = fork();
249 if (pid < 0) {
250 perror("fork");
251 exit(1);
252 } else if (pid == 0) {
253 close(fds[1]);
254 return fds[0];
255 } else {
256 unsigned pos = 0;
257 int status;
258 fputs("KLEE-REPLAY: NOTE: pipe master: starting\n", stderr);
259 close(fds[0]);
260
261 while (pos < flen) {
262 int res = write(fds[1], &contents[pos], flen - pos);
263 if (res<0) {
264 if (errno != EINTR)
265 break;
266 } else if (res) {
267 pos += res;
268 }
269 }
270
271 if (wait_for_timeout_or_exit(pid, "pipe master", &status))
272 goto pipe_exit;
273
274 fputs("KLEE-REPLAY: NOTE: pipe master: closing & waiting\n", stderr);
275 close(fds[1]);
276 while (1) {
277 pid_t res = waitpid(pid, &status, 0);
278 if (res < 0) {
279 if (errno != EINTR)
280 break;
281 } else {
282 break;
283 }
284 }
285
286 pipe_exit:
287 close(fds[1]);
288 fputs("KLEE-REPLAY: NOTE: pipe master: done\n", stderr);
289 process_status(status, 0, "PTY MASTER");
290 }
291}
292
293
294static int create_reg_file(const char *fname, exe_disk_file_t *dfile,
295 const char *tmpdir) {
296 struct stat64 *s = dfile->stat;
297 char* contents = dfile->contents;
298 unsigned flen = dfile->size;
299 unsigned mode = s->st_mode & 0777;
300
301 fprintf(stderr, "KLEE-REPLAY: NOTE: Creating file %s of length %d\n", fname, flen);
302
303 // Open in RDWR just in case we have to end up using this fd.
304 if (__exe_env.version == 0 && mode == 0)
305 mode = 0644;
306
307 int fd = open(fname, O_CREAT | O_RDWR, mode);
308 // int fd = open(fname, O_CREAT | O_WRONLY, s->st_mode&0777);
309 if (fd < 0) {
310 fprintf(stderr, "KLEE-REPLAY: ERROR: Cannot create file %s\n", fname);
311 exit(1);
312 }
313
314 ssize_t r = write(fd, contents, flen);
315 if (r < 0 || (unsigned) r != flen) {
316 fprintf(stderr, "KLEE-REPLAY: ERROR: Cannot write file %s\n", fname);
317 exit(1);
318 }
319
320 struct timeval tv[2];
321 tv[0].tv_sec = s->st_atime;
322 tv[0].tv_usec = 0;
323 tv[1].tv_sec = s->st_mtime;
324 tv[1].tv_usec = 0;
325 futimes(fd, tv);
326
327 // XXX: Now what we should do is reopen a new fd with the correct modes
328 // as they were given to the process.
329 lseek(fd, 0, SEEK_SET);
330
331 return fd;
332}
333
334static void create_file(int target_fd,
335 const char *target_name,
336 exe_disk_file_t *dfile,
337 const char *tmpdir) {
338 struct stat64 *s = dfile->stat;
339 char tmpname[PATH_MAX];
340 const char *target;
341 int fd;
342
343 assert((target_fd == -1) ^ (target_name == NULL));
344
345 if (target_name)
346 snprintf(tmpname, sizeof(tmpname), "%s/%s", tmpdir, target_name);
347 else snprintf(tmpname, sizeof(tmpname), "%s/fd%d", tmpdir, target_fd);
348
349 target = tmpname;
350
351 // XXX get rid of me once a reasonable solution is found
352 s->st_uid = geteuid();
353 s->st_gid = getegid();
354
355 if (S_ISLNK(s->st_mode)) {
356 fd = create_link(target, dfile, tmpdir);
357 }
358 else if (S_ISDIR(s->st_mode)) {
359 fd = create_dir(target, dfile, tmpdir);
360 }
361 else if (S_ISCHR(s->st_mode)) {
362 fd = create_char_dev(target, dfile, tmpdir);
363 }
364 else if (S_ISFIFO(s->st_mode) ||
365 (target_fd==0 && (s->st_mode & S_IFMT) == 0)) { // XXX hack
366 fd = create_pipe(target, dfile, tmpdir);
367 }
368 else {
369 fd = create_reg_file(target, dfile, tmpdir);
370 }
371
372 if (fd >= 0) {
373 if (target_fd != -1) {
374 close(target_fd);
375 if (dup2(fd, target_fd) < 0) {
376 fprintf(stderr, "KLEE-REPLAY: ERROR: dup2 failed for target: %d\n", target_fd);
377 perror("dup2");
378 }
379 close(fd);
380 } else {
381 // Only worry about 1 vs !1
382 if (s->st_nlink > 1) {
383 char tmp2[PATH_MAX];
384 snprintf(tmp2, sizeof(tmp2), "%s/%s.link2", tmpdir, target_name);
385 if (link(target_name, tmp2) < 0) {
386 perror("link");
387 exit(1);
388 }
389 }
390
391 close(fd);
392 }
393 }
394}
395
396char replay_dir[] = "/tmp/klee-replay-XXXXXX";
397
398void replay_create_files(exe_file_system_t *exe_fs) {
399 unsigned k;
400
401 // Create a temporary directory to place files involved in replay
402 strcpy(replay_dir, "/tmp/klee-replay-XXXXXX"); // new template for each replayed file
403 char* tmpdir = mkdtemp(replay_dir);
404
405 if (tmpdir == NULL) {
406 perror("mkdtemp: could not create temporary directory");
407 exit(EXIT_FAILURE);
408 }
409
410 fprintf(stderr, "KLEE-REPLAY: NOTE: Storing KLEE replay files in %s\n", tmpdir);
411
412 umask(0);
413 for (k=0; k < exe_fs->n_sym_files; k++) {
414 char name[2];
415 snprintf(name, sizeof(name), "%c", 'A' + k);
416 create_file(-1, name, &exe_fs->sym_files[k], tmpdir);
417 }
418
419 if (exe_fs->sym_stdin)
420 create_file(0, NULL, exe_fs->sym_stdin, tmpdir);
421
422 if (exe_fs->sym_stdout)
423 create_file(1, NULL, exe_fs->sym_stdout, tmpdir);
424
425 if (exe_fs->sym_stdin)
426 check_file(__STDIN, exe_fs->sym_stdin);
427
428 if (exe_fs->sym_stdout)
429 check_file(__STDOUT, exe_fs->sym_stdout);
430
431 for (k=0; k<exe_fs->n_sym_files; ++k)
432 check_file(k, &exe_fs->sym_files[k]);
433}
434
435
436/* Used by nftw() in replay_delete_files() */
437int remove_callback(const char *fpath,
438 __attribute__((unused)) const struct stat *sb,
439 __attribute__((unused)) int typeflag,
440 __attribute__((unused)) struct FTW *ftwbuf) {
441 return remove(fpath);
442}
443
445 if (keep_temps)
446 return;
447
448 fprintf(stderr, "KLEE-REPLAY: NOTE: removing %s\n", replay_dir);
449
450 if (nftw(replay_dir, remove_callback, FOPEN_MAX,
451 FTW_DEPTH | FTW_PHYS) == -1) {
452 perror("nftw");
453 exit(EXIT_FAILURE);
454 }
455}
456
457static void check_file(int index, exe_disk_file_t *dfile) {
458 struct stat s;
459 int res;
460 char name[32];
461 char fullname[PATH_MAX];
462
463 switch (index) {
464 case __STDIN:
465 strcpy(name, "stdin");
466 res = fstat(0, &s);
467 break;
468 case __STDOUT:
469 strcpy(name, "stdout");
470 res = fstat(1, &s);
471 break;
472 default:
473 name[0] = 'A' + index;
474 name[1] = '\0';
475 snprintf(fullname, sizeof(fullname), "%s/%s", replay_dir, name);
476 res = stat(fullname, &s);
477
478 break;
479 }
480
481 if (res < 0) {
482 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %d: stat failure\n", index);
483 return;
484 }
485
486 if (s.st_dev != dfile->stat->st_dev) {
487 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: dev mismatch: %d vs %d\n",
488 name, (int) s.st_dev, (int) dfile->stat->st_dev);
489 }
490/* if (s.st_ino != dfile->stat->st_ino) { */
491/* fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: ino mismatch: %d vs %d\n", */
492/* name, (int) s.st_ino, (int) dfile->stat->st_ino); */
493/* } */
494 if (s.st_mode != dfile->stat->st_mode) {
495 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: mode mismatch: %#o vs %#o\n",
496 name, s.st_mode, dfile->stat->st_mode);
497 }
498 if (s.st_nlink != dfile->stat->st_nlink) {
499 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: nlink mismatch: %d vs %d\n",
500 name, (int) s.st_nlink, (int) dfile->stat->st_nlink);
501 }
502 if (s.st_uid != dfile->stat->st_uid) {
503 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: uid mismatch: %d vs %d\n",
504 name, s.st_uid, dfile->stat->st_uid);
505 }
506 if (s.st_gid != dfile->stat->st_gid) {
507 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: gid mismatch: %d vs %d\n",
508 name, s.st_gid, dfile->stat->st_gid);
509 }
510 if (s.st_rdev != dfile->stat->st_rdev) {
511 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: rdev mismatch: %d vs %d\n",
512 name, (int) s.st_rdev, (int) dfile->stat->st_rdev);
513 }
514 if (s.st_size != dfile->stat->st_size) {
515 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: size mismatch: %d vs %d\n",
516 name, (int) s.st_size, (int) dfile->stat->st_size);
517 }
518 if (s.st_blksize != dfile->stat->st_blksize) {
519 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: blksize mismatch: %d vs %d\n",
520 name, (int) s.st_blksize, (int) dfile->stat->st_blksize);
521 }
522 if (s.st_blocks != dfile->stat->st_blocks) {
523 fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: blocks mismatch: %d vs %d\n",
524 name, (int) s.st_blocks, (int) dfile->stat->st_blocks);
525 }
526/* if (s.st_atime != dfile->stat->st_atime) { */
527/* fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: atime mismatch: %d vs %d\n", */
528/* name, (int) s.st_atime, (int) dfile->stat->st_atime); */
529/* } */
530/* if (s.st_mtime != dfile->stat->st_mtime) { */
531/* fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: mtime mismatch: %d vs %d\n", */
532/* name, (int) s.st_mtime, (int) dfile->stat->st_mtime); */
533/* } */
534/* if (s.st_ctime != dfile->stat->st_ctime) { */
535/* fprintf(stderr, "KLEE-REPLAY: WARNING: check_file %s: ctime mismatch: %d vs %d\n", */
536/* name, (int) s.st_ctime, (int) dfile->stat->st_ctime); */
537/* } */
538}
void *__dso_handle __attribute__((__weak__))
static int create_reg_file(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:294
static void create_file(int target_fd, const char *target_name, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:334
#define __STDOUT
Definition: file-creator.c:47
double getTime()
Definition: file-creator.c:83
int wait_for_timeout_or_exit(pid_t pid, const char *name, int *statusp)
Return true if program exited, false if timed out.
Definition: file-creator.c:91
char replay_dir[]
Definition: file-creator.c:396
static int create_char_dev(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:108
int remove_callback(const char *fpath, __attribute__((unused)) const struct stat *sb, __attribute__((unused)) int typeflag, __attribute__((unused)) struct FTW *ftwbuf)
Definition: file-creator.c:437
#define __STDIN
Definition: file-creator.c:46
static void check_file(int index, exe_disk_file_t *dfile)
Definition: file-creator.c:457
static int create_link(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:49
static int create_pipe(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:233
static int create_dir(const char *fname, exe_disk_file_t *dfile, const char *tmpdir)
Definition: file-creator.c:73
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 process_status(int status, time_t elapsed, const char *pfx)
Definition: klee-replay.c:136
int keep_temps
Definition: klee-replay.c:284