blob: 505fb8d78d3276ba19637969710d6ac6dae1879c [file] [log] [blame]
//===-- file-creator.c ----------------------------------------------------===//
//
// The KLEE Symbolic Virtual Machine
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "klee-replay.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <pty.h>
#include <time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <assert.h>
static void create_file(int target_fd,
const char *target_name,
exe_disk_file_t *dfile,
const char *tmpdir);
static void check_file(int index, exe_disk_file_t *file);
static void delete_file(const char *path, int recurse);
#define __STDIN -1
#define __STDOUT -2
static int create_link(const char *fname,
exe_disk_file_t *dfile,
const char *tmpdir) {
char buf[64];
struct stat64 *s = dfile->stat;
// XXX Broken, we want this path to be somewhere else most likely.
sprintf(buf, "%s.lnk", fname);
s->st_mode = (s->st_mode & ~S_IFMT) | S_IFREG;
create_file(-1, buf, dfile, tmpdir);
int res = symlink(buf, fname);
if (res < 0) {
perror("symlink");
}
return open(fname, O_RDWR);
}
static int create_dir(const char *fname, exe_disk_file_t *dfile,
const char *tmpdir) {
int res = mkdir(fname, dfile->stat->st_mode);
if (res < 0) {
perror("mkdir");
return -1;
}
return open(fname, O_RDWR);
}
double getTime() {
struct timeval t;
gettimeofday(&t, NULL);
return (double) t.tv_sec + ((double) t.tv_usec / 1000000.0);
}
/// Return true if program exited, false if timed out.
int wait_for_timeout_or_exit(pid_t pid, const char *name, int *statusp) {
char *t = getenv("KLEE_REPLAY_TIMEOUT");
int timeout = t ? atoi(t) : 5;
double wait = timeout * .5;
double start = getTime();
fprintf(stderr, "note: %s: waiting %.2fs\n", name, wait);
while (getTime() - start < wait) {
struct timespec r = {0, 1000000};
nanosleep(&r, 0);
int res = waitpid(pid, statusp, WNOHANG);
if (res==pid)
return 1;
}
return 0;
}
static int create_char_dev(const char *fname, exe_disk_file_t *dfile,
const char *tmpdir) {
struct stat64 *s = dfile->stat;
unsigned flen = dfile->size;
char* contents = dfile->contents;
// Assume tty, kinda broken, need an actual device id or something
struct termios term, *ts=&term;
struct winsize win = { 24, 80, 0, 0 };
/* Just copied from my system, munged to match what fields
uclibc thinks are there. */
ts->c_iflag = 27906;
ts->c_oflag = 5;
ts->c_cflag = 1215;
ts->c_lflag = 35287;
ts->c_line = 0;
ts->c_cc[0] = '\x03';
ts->c_cc[1] = '\x1c';
ts->c_cc[2] = '\x7f';
ts->c_cc[3] = '\x15';
ts->c_cc[4] = '\x04';
ts->c_cc[5] = '\x00';
ts->c_cc[6] = '\x01';
ts->c_cc[7] = '\xff';
ts->c_cc[8] = '\x11';
ts->c_cc[9] = '\x13';
ts->c_cc[10] = '\x1a';
ts->c_cc[11] = '\xff';
ts->c_cc[12] = '\x12';
ts->c_cc[13] = '\x0f';
ts->c_cc[14] = '\x17';
ts->c_cc[15] = '\x16';
ts->c_cc[16] = '\xff';
ts->c_cc[17] = '\x0';
ts->c_cc[18] = '\x0';
{
char name[1024];
int amaster, aslave;
int res = openpty(&amaster, &aslave, name, &term, &win);
if (res < 0) {
perror("openpty");
exit(1);
}
if (symlink(name, fname) == -1) {
fprintf(stderr, "unable to create sym link to tty\n");
perror("symlink");
}
// pty will not be world writeable
s->st_mode &= ~02;
pid_t pid = fork();
if (pid < 0) {
perror("fork failed\n");
exit(1);
} else if (pid == 0) {
close(amaster);
fprintf(stderr, "note: pty slave: setting raw mode\n");
{
struct termio mode;
int res = ioctl(aslave, TCGETA, &mode);
assert(!res);
mode.c_iflag = IGNBRK;
mode.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONLRET);
mode.c_lflag = 0;
mode.c_cc[VMIN] = 1;
mode.c_cc[VTIME] = 0;
res = ioctl(aslave, TCSETA, &mode);
assert(res == 0);
}
return aslave;
} else {
unsigned pos = 0;
int status;
fprintf(stderr, "note: pty master: starting\n");
close(aslave);
while (pos < flen) {
int res = write(amaster, &contents[pos], flen - pos);
if (res<0) {
if (errno != EINTR) {
fprintf(stderr, "note: pty master: write error\n");
perror("errno");
break;
}
} else if (res) {
fprintf(stderr, "note: pty master: wrote: %d (of %d)\n", res, flen);
pos += res;
}
}
if (wait_for_timeout_or_exit(pid, "pty master", &status))
goto pty_exit;
fprintf(stderr, "note: pty master: closing & waiting\n");
close(amaster);
while (1) {
int res = waitpid(pid, &status, 0);
if (res < 0) {
if (errno != EINTR)
break;
} else {
break;
}
}
pty_exit:
close(amaster);
fprintf(stderr, "note: pty master: done\n");
process_status(status, 0, "PTY MASTER");
}
}
}
static int create_pipe(const char *fname, exe_disk_file_t *dfile,
const char *tmpdir) {
//struct stat64 *s = dfile->stat;
unsigned flen = dfile->size;
char* contents = dfile->contents;
// XXX what is direction ? need more data
pid_t pid;
int fds[2];
int res = pipe(fds);
if (res < 0) {
perror("pipe");
exit(1);
}
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
} else if (pid == 0) {
close(fds[1]);
return fds[0];
} else {
unsigned pos = 0;
int status;
fprintf(stderr, "note: pipe master: starting\n");
close(fds[0]);
while (pos < flen) {
int res = write(fds[1], &contents[pos], flen - pos);
if (res<0) {
if (errno != EINTR)
break;
} else if (res) {
pos += res;
}
}
if (wait_for_timeout_or_exit(pid, "pipe master", &status))
goto pipe_exit;
fprintf(stderr, "note: pipe master: closing & waiting\n");
close(fds[1]);
while (1) {
int res = waitpid(pid, &status, 0);
if (res < 0) {
if (errno != EINTR)
break;
} else {
break;
}
}
pipe_exit:
close(fds[1]);
fprintf(stderr, "note: pipe master: done\n");
process_status(status, 0, "PTY MASTER");
}
}
static int create_reg_file(const char *fname, exe_disk_file_t *dfile,
const char *tmpdir) {
struct stat64 *s = dfile->stat;
char* contents = dfile->contents;
unsigned flen = dfile->size;
unsigned mode = s->st_mode & 0777;
//fprintf(stderr, "Creating regular file\n");
// Open in RDWR just in case we have to end up using this fd.
if (__exe_env.version == 0 && mode == 0)
mode = 0644;
int fd = open(fname, O_CREAT | O_RDWR, mode);
// int fd = open(fname, O_CREAT | O_WRONLY, s->st_mode&0777);
if (fd < 0) {
fprintf(stderr, "Cannot create file %s\n", fname);
exit(1);
}
int r = write(fd, contents, flen);
if (r < 0 || (unsigned) r != flen) {
fprintf(stderr, "Cannot write file %s\n", fname);
exit(1);
}
struct timeval tv[2];
tv[0].tv_sec = s->st_atime;
tv[0].tv_usec = 0;
tv[1].tv_sec = s->st_mtime;
tv[1].tv_usec = 0;
futimes(fd, tv);
// XXX: Now what we should do is reopen a new fd with the correct modes
// as they were given to the process.
lseek(fd, 0, SEEK_SET);
return fd;
}
static int delete_dir(const char *path, int recurse) {
if (recurse) {
DIR *d = opendir(path);
struct dirent *de;
if (d) {
while ((de = readdir(d))) {
if (strcmp(de->d_name, ".")!=0 && strcmp(de->d_name, "..")!=0) {
char tmp[PATH_MAX];
sprintf(tmp, "%s/%s", path, de->d_name);
delete_file(tmp, 0);
}
}
closedir(d);
}
}
if (rmdir(path) == -1) {
fprintf(stderr, "Cannot create file %s (exists, is dir, can't remove)\n", path);
perror("rmdir");
return -1;
}
return 0;
}
static void delete_file(const char *path, int recurse) {
if (unlink(path) < 0 && errno != ENOENT) {
if (errno == EISDIR) {
delete_dir(path, 1);
} else {
fprintf(stderr, "Cannot create file %s (already exists)\n", path);
perror("unlink");
}
}
}
static void create_file(int target_fd,
const char *target_name,
exe_disk_file_t *dfile,
const char *tmpdir) {
struct stat64 *s = dfile->stat;
char tmpname[PATH_MAX];
const char *target;
int fd;
assert((target_fd == -1) ^ (target_name == NULL));
if (target_name) {
target = target_name;
} else {
sprintf(tmpname, "%s/fd%d", tmpdir, target_fd);
target = tmpname;
}
delete_file(target, 1);
// XXX get rid of me once a reasonable solution is found
s->st_uid = geteuid();
s->st_gid = getegid();
if (S_ISLNK(s->st_mode)) {
fd = create_link(target, dfile, tmpdir);
}
else if (S_ISDIR(s->st_mode)) {
fd = create_dir(target, dfile, tmpdir);
}
else if (S_ISCHR(s->st_mode)) {
fd = create_char_dev(target, dfile, tmpdir);
}
else if (S_ISFIFO(s->st_mode) ||
(target_fd==0 && (s->st_mode & S_IFMT) == 0)) { // XXX hack
fd = create_pipe(target, dfile, tmpdir);
}
else {
fd = create_reg_file(target, dfile, tmpdir);
}
if (fd >= 0) {
if (target_fd != -1) {
close(target_fd);
if (dup2(fd, target_fd) < 0) {
fprintf(stderr, "note: dup2 failed for target: %d\n", target_fd);
perror("dup2");
}
close(fd);
} else {
// Only worry about 1 vs !1
if (s->st_nlink > 1) {
char tmp2[PATH_MAX];
sprintf(tmp2, "%s/%s.link2", tmpdir, target_name);
if (link(target_name, tmp2) < 0) {
perror("link");
exit(1);
}
}
close(fd);
}
}
}
void replay_create_files(exe_file_system_t *exe_fs) {
char tmpdir[PATH_MAX];
unsigned k;
if (!getcwd(tmpdir, PATH_MAX)) {
perror("getcwd");
exit(1);
}
strcat(tmpdir, ".temps");
delete_file(tmpdir, 1);
mkdir(tmpdir, 0755);
umask(0);
for (k=0; k < exe_fs->n_sym_files; k++) {
char name[2];
sprintf(name, "%c", 'A' + k);
create_file(-1, name, &exe_fs->sym_files[k], tmpdir);
}
if (exe_fs->sym_stdin)
create_file(0, NULL, exe_fs->sym_stdin, tmpdir);
if (exe_fs->sym_stdout)
create_file(1, NULL, exe_fs->sym_stdout, tmpdir);
if (exe_fs->sym_stdin)
check_file(__STDIN, exe_fs->sym_stdin);
if (exe_fs->sym_stdout)
check_file(__STDOUT, exe_fs->sym_stdout);
for (k=0; k<exe_fs->n_sym_files; ++k)
check_file(k, &exe_fs->sym_files[k]);
}
static void check_file(int index, exe_disk_file_t *dfile) {
struct stat s;
int res;
char name[32];
switch (index) {
case __STDIN:
strcpy(name, "stdin");
res = fstat(0, &s);
break;
case __STDOUT:
strcpy(name, "stdout");
res = fstat(1, &s);
break;
default:
name[0] = 'A' + index;
name[1] = '\0';
res = stat(name, &s);
break;
}
if (res < 0) {
fprintf(stderr, "warning: check_file %d: stat failure\n", index);
return;
}
if (s.st_dev != dfile->stat->st_dev) {
fprintf(stderr, "warning: check_file %s: dev mismatch: %d vs %d\n",
name, (int) s.st_dev, (int) dfile->stat->st_dev);
}
/* if (s.st_ino != dfile->stat->st_ino) { */
/* fprintf(stderr, "warning: check_file %s: ino mismatch: %d vs %d\n", */
/* name, (int) s.st_ino, (int) dfile->stat->st_ino); */
/* } */
if (s.st_mode != dfile->stat->st_mode) {
fprintf(stderr, "warning: check_file %s: mode mismatch: %#o vs %#o\n",
name, s.st_mode, dfile->stat->st_mode);
}
if (s.st_nlink != dfile->stat->st_nlink) {
fprintf(stderr, "warning: check_file %s: nlink mismatch: %d vs %d\n",
name, (int) s.st_nlink, (int) dfile->stat->st_nlink);
}
if (s.st_uid != dfile->stat->st_uid) {
fprintf(stderr, "warning: check_file %s: uid mismatch: %d vs %d\n",
name, s.st_uid, dfile->stat->st_uid);
}
if (s.st_gid != dfile->stat->st_gid) {
fprintf(stderr, "warning: check_file %s: gid mismatch: %d vs %d\n",
name, s.st_gid, dfile->stat->st_gid);
}
if (s.st_rdev != dfile->stat->st_rdev) {
fprintf(stderr, "warning: check_file %s: rdev mismatch: %d vs %d\n",
name, (int) s.st_rdev, (int) dfile->stat->st_rdev);
}
if (s.st_size != dfile->stat->st_size) {
fprintf(stderr, "warning: check_file %s: size mismatch: %d vs %d\n",
name, (int) s.st_size, (int) dfile->stat->st_size);
}
if (s.st_blksize != dfile->stat->st_blksize) {
fprintf(stderr, "warning: check_file %s: blksize mismatch: %d vs %d\n",
name, (int) s.st_blksize, (int) dfile->stat->st_blksize);
}
if (s.st_blocks != dfile->stat->st_blocks) {
fprintf(stderr, "warning: check_file %s: blocks mismatch: %d vs %d\n",
name, (int) s.st_blocks, (int) dfile->stat->st_blocks);
}
/* if (s.st_atime != dfile->stat->st_atime) { */
/* fprintf(stderr, "warning: check_file %s: atime mismatch: %d vs %d\n", */
/* name, (int) s.st_atime, (int) dfile->stat->st_atime); */
/* } */
/* if (s.st_mtime != dfile->stat->st_mtime) { */
/* fprintf(stderr, "warning: check_file %s: mtime mismatch: %d vs %d\n", */
/* name, (int) s.st_mtime, (int) dfile->stat->st_mtime); */
/* } */
/* if (s.st_ctime != dfile->stat->st_ctime) { */
/* fprintf(stderr, "warning: check_file %s: ctime mismatch: %d vs %d\n", */
/* name, (int) s.st_ctime, (int) dfile->stat->st_ctime); */
/* } */
}