1 //===-- fd.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 #define _LARGEFILE64_SOURCE
11 #include "fd.h"
12
13 #include "klee/klee.h"
14
15 #include <assert.h>
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdarg.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <sys/syscall.h>
24 #include <sys/types.h>
25 #ifndef __FreeBSD__
26 #include <sys/vfs.h>
27 #endif
28 #include <dirent.h>
29 #include <sys/ioctl.h>
30 #include <sys/mtio.h>
31 #include <sys/select.h>
32 #include <sys/time.h>
33 #include <termios.h>
34 #include <unistd.h>
35
36 /* Returns pointer to the symbolic file structure fs the pathname is symbolic */
__get_sym_file(const char * pathname)37 static exe_disk_file_t *__get_sym_file(const char *pathname) {
38 if (!pathname)
39 return NULL;
40
41 // Handle the case where symbolic file is given as an absolute path, ie.
42 // /current/work/dir/A
43 if (pathname[0] == '/') {
44 char cwd[1024] = {0};
45 if (getcwd(cwd, 1024)) {
46 size_t cwd_len = strlen(cwd);
47 // strip trailing / if present
48 if (cwd_len > 0 && cwd[cwd_len - 1] == '/') {
49 cwd[--cwd_len] = '\0';
50 }
51 if (strncmp(pathname, cwd, cwd_len) == 0) {
52 if (pathname[cwd_len] != '\0')
53 pathname += cwd_len + 1;
54 }
55 }
56 }
57 char c = pathname[0];
58 unsigned i;
59
60 if (c == 0 || pathname[1] != 0)
61 return NULL;
62
63 for (i=0; i<__exe_fs.n_sym_files; ++i) {
64 if (c == 'A' + (char) i) {
65 exe_disk_file_t *df = &__exe_fs.sym_files[i];
66 if (df->stat->st_ino == 0)
67 return NULL;
68 return df;
69 }
70 }
71
72 return NULL;
73 }
74
75 static void *__concretize_ptr(const void *p);
76 static size_t __concretize_size(size_t s);
77 static const char *__concretize_string(const char *s);
78
79 /* Returns pointer to the file entry for a valid fd */
__get_file(int fd)80 static exe_file_t *__get_file(int fd) {
81 if (fd>=0 && fd<MAX_FDS) {
82 exe_file_t *f = &__exe_env.fds[fd];
83 if (f->flags & eOpen)
84 return f;
85 }
86
87 return 0;
88 }
89
access(const char * pathname,int mode)90 int access(const char *pathname, int mode) {
91 exe_disk_file_t *dfile = __get_sym_file(pathname);
92
93 if (dfile) {
94 /* XXX we should check against stat values but we also need to
95 enforce in open and friends then. */
96 return 0;
97 }
98 return syscall(__NR_access, __concretize_string(pathname), mode);
99 }
100
umask(mode_t mask)101 mode_t umask(mode_t mask) {
102 mode_t r = __exe_env.umask;
103 __exe_env.umask = mask & 0777;
104 return r;
105 }
106
107
108 /* Returns 1 if the process has the access rights specified by 'flags'
109 to the file with stat 's'. Returns 0 otherwise*/
has_permission(int flags,struct stat64 * s)110 static int has_permission(int flags, struct stat64 *s) {
111 int write_access, read_access;
112 mode_t mode = s->st_mode;
113
114 if (flags & O_RDONLY || flags & O_RDWR)
115 read_access = 1;
116 else read_access = 0;
117
118 if (flags & O_WRONLY || flags & O_RDWR)
119 write_access = 1;
120 else write_access = 0;
121
122 /* XXX: We don't worry about process uid and gid for now.
123 We allow access if any user has access to the file. */
124 #if 0
125 uid_t uid = s->st_uid;
126 uid_t euid = geteuid();
127 gid_t gid = s->st_gid;
128 gid_t egid = getegid();
129 #endif
130
131 if (read_access && ((mode & S_IRUSR) | (mode & S_IRGRP) | (mode & S_IROTH)))
132 return 0;
133
134 if (write_access && !((mode & S_IWUSR) | (mode & S_IWGRP) | (mode & S_IWOTH)))
135 return 0;
136
137 return 1;
138 }
139
140
__fd_open(const char * pathname,int flags,mode_t mode)141 int __fd_open(const char *pathname, int flags, mode_t mode) {
142 exe_disk_file_t *df;
143 exe_file_t *f;
144 int fd;
145
146 for (fd = 0; fd < MAX_FDS; ++fd)
147 if (!(__exe_env.fds[fd].flags & eOpen))
148 break;
149 if (fd == MAX_FDS) {
150 errno = EMFILE;
151 return -1;
152 }
153
154 f = &__exe_env.fds[fd];
155
156 /* Should be the case if file was available, but just in case. */
157 memset(f, 0, sizeof *f);
158
159 df = __get_sym_file(pathname);
160 if (df) {
161 /* XXX Should check access against mode / stat / possible
162 deletion. */
163 f->dfile = df;
164
165 if ((flags & O_CREAT) && (flags & O_EXCL)) {
166 errno = EEXIST;
167 return -1;
168 }
169
170 if ((flags & O_TRUNC) && (flags & O_RDONLY)) {
171 /* The result of using O_TRUNC with O_RDONLY is undefined, so we
172 return error */
173 klee_warning("Undefined call to open(): O_TRUNC | O_RDONLY\n");
174 errno = EACCES;
175 return -1;
176 }
177
178 if ((flags & O_EXCL) && !(flags & O_CREAT)) {
179 /* The result of using O_EXCL without O_CREAT is undefined, so
180 we return error */
181 klee_warning("Undefined call to open(): O_EXCL w/o O_RDONLY\n");
182 errno = EACCES;
183 return -1;
184 }
185
186 if (!has_permission(flags, df->stat)) {
187 errno = EACCES;
188 return -1;
189 }
190 else
191 f->dfile->stat->st_mode = ((f->dfile->stat->st_mode & ~0777) |
192 (mode & ~__exe_env.umask));
193 } else {
194 int os_fd = syscall(__NR_open, __concretize_string(pathname), flags, mode);
195 if (os_fd == -1)
196 return -1;
197 f->fd = os_fd;
198 }
199
200 f->flags = eOpen;
201 if ((flags & O_ACCMODE) == O_RDONLY) {
202 f->flags |= eReadable;
203 } else if ((flags & O_ACCMODE) == O_WRONLY) {
204 f->flags |= eWriteable;
205 } else { /* XXX What actually happens here if != O_RDWR. */
206 f->flags |= eReadable | eWriteable;
207 }
208
209 return fd;
210 }
211
__fd_openat(int basefd,const char * pathname,int flags,mode_t mode)212 int __fd_openat(int basefd, const char *pathname, int flags, mode_t mode) {
213 exe_file_t *f;
214 int fd;
215 if (basefd != AT_FDCWD) {
216 exe_file_t *bf = __get_file(basefd);
217
218 if (!bf) {
219 errno = EBADF;
220 return -1;
221 } else if (bf->dfile) {
222 klee_warning("symbolic file descriptor, ignoring (ENOENT)");
223 errno = ENOENT;
224 return -1;
225 }
226 basefd = bf->fd;
227 }
228
229 if (__get_sym_file(pathname)) {
230 /* for a symbolic file, it doesn't matter if/where it exists on disk */
231 return __fd_open(pathname, flags, mode);
232 }
233
234 for (fd = 0; fd < MAX_FDS; ++fd)
235 if (!(__exe_env.fds[fd].flags & eOpen))
236 break;
237 if (fd == MAX_FDS) {
238 errno = EMFILE;
239 return -1;
240 }
241
242 f = &__exe_env.fds[fd];
243
244 /* Should be the case if file was available, but just in case. */
245 memset(f, 0, sizeof *f);
246
247 int os_fd = syscall(__NR_openat, (long)basefd, __concretize_string(pathname), (long)flags, mode);
248 if (os_fd == -1)
249 return -1;
250
251 f->fd = os_fd;
252 f->flags = eOpen;
253 if ((flags & O_ACCMODE) == O_RDONLY) {
254 f->flags |= eReadable;
255 } else if ((flags & O_ACCMODE) == O_WRONLY) {
256 f->flags |= eWriteable;
257 } else { /* XXX What actually happens here if != O_RDWR. */
258 f->flags |= eReadable | eWriteable;
259 }
260
261 return fd;
262 }
263
264
utimes(const char * path,const struct timeval times[2])265 int utimes(const char *path, const struct timeval times[2]) {
266 exe_disk_file_t *dfile = __get_sym_file(path);
267
268 if (dfile) {
269
270 if (!times) {
271 struct timeval newTimes[2];
272 gettimeofday(&(newTimes[0]), NULL);
273 newTimes[1] = newTimes[0];
274 times = newTimes;
275 }
276
277 /* don't bother with usecs */
278 dfile->stat->st_atime = times[0].tv_sec;
279 dfile->stat->st_mtime = times[1].tv_sec;
280 #ifdef _BSD_SOURCE
281 dfile->stat->st_atim.tv_nsec = 1000000000ll * times[0].tv_sec;
282 dfile->stat->st_mtim.tv_nsec = 1000000000ll * times[1].tv_sec;
283 #endif
284 return 0;
285 }
286 return syscall(__NR_utimes, __concretize_string(path), times);
287 }
288
289
futimesat(int fd,const char * path,const struct timeval times[2])290 int futimesat(int fd, const char* path, const struct timeval times[2]) {
291 if (fd != AT_FDCWD) {
292 exe_file_t *f = __get_file(fd);
293
294 if (!f) {
295 errno = EBADF;
296 return -1;
297 } else if (f->dfile) {
298 klee_warning("symbolic file descriptor, ignoring (ENOENT)");
299 errno = ENOENT;
300 return -1;
301 }
302 fd = f->fd;
303 }
304 if (__get_sym_file(path)) {
305 return utimes(path, times);
306 }
307
308 return syscall(__NR_futimesat, (long)fd,
309 (path ? __concretize_string(path) : NULL), times);
310 }
311
close(int fd)312 int close(int fd) {
313 static int n_calls = 0;
314 exe_file_t *f;
315 int r = 0;
316
317 n_calls++;
318
319 f = __get_file(fd);
320 if (!f) {
321 errno = EBADF;
322 return -1;
323 }
324
325 if (__exe_fs.max_failures && *__exe_fs.close_fail == n_calls) {
326 __exe_fs.max_failures--;
327 errno = EIO;
328 return -1;
329 }
330
331 #if 0
332 if (!f->dfile) {
333 /* if a concrete fd */
334 r = syscall(__NR_close, f->fd);
335 }
336 else r = 0;
337 #endif
338
339 memset(f, 0, sizeof *f);
340
341 return r;
342 }
343
read(int fd,void * buf,size_t count)344 ssize_t read(int fd, void *buf, size_t count) {
345 static int n_calls = 0;
346 exe_file_t *f;
347
348 n_calls++;
349
350 if (count == 0)
351 return 0;
352
353 if (buf == NULL) {
354 errno = EFAULT;
355 return -1;
356 }
357
358 f = __get_file(fd);
359
360 if (!f) {
361 errno = EBADF;
362 return -1;
363 }
364
365 if (__exe_fs.max_failures && *__exe_fs.read_fail == n_calls) {
366 __exe_fs.max_failures--;
367 errno = EIO;
368 return -1;
369 }
370
371 if (!f->dfile) {
372 /* concrete file */
373 int r;
374 buf = __concretize_ptr(buf);
375 count = __concretize_size(count);
376 /* XXX In terms of looking for bugs we really should do this check
377 before concretization, at least once the routine has been fixed
378 to properly work with symbolics. */
379 klee_check_memory_access(buf, count);
380 if (f->fd == 0)
381 r = syscall(__NR_read, f->fd, buf, count);
382 else
383 r = syscall(__NR_pread64, f->fd, buf, count, (off64_t) f->off);
384
385 if (r == -1)
386 return -1;
387
388 if (f->fd != 0)
389 f->off += r;
390 return r;
391 }
392 else {
393 assert(f->off >= 0);
394 if (((off64_t)f->dfile->size) < f->off)
395 return 0;
396
397 /* symbolic file */
398 if (f->off + count > f->dfile->size) {
399 count = f->dfile->size - f->off;
400 }
401
402 memcpy(buf, f->dfile->contents + f->off, count);
403 f->off += count;
404
405 return count;
406 }
407 }
408
409
write(int fd,const void * buf,size_t count)410 ssize_t write(int fd, const void *buf, size_t count) {
411 static int n_calls = 0;
412 exe_file_t *f;
413
414 n_calls++;
415
416 f = __get_file(fd);
417
418 if (!f) {
419 errno = EBADF;
420 return -1;
421 }
422
423 if (__exe_fs.max_failures && *__exe_fs.write_fail == n_calls) {
424 __exe_fs.max_failures--;
425 errno = EIO;
426 return -1;
427 }
428
429 if (!f->dfile) {
430 int r;
431
432 buf = __concretize_ptr(buf);
433 count = __concretize_size(count);
434 /* XXX In terms of looking for bugs we really should do this check
435 before concretization, at least once the routine has been fixed
436 to properly work with symbolics. */
437 klee_check_memory_access(buf, count);
438 if (f->fd == 1 || f->fd == 2)
439 r = syscall(__NR_write, f->fd, buf, count);
440 else r = syscall(__NR_pwrite64, f->fd, buf, count, (off64_t) f->off);
441
442 if (r == -1)
443 return -1;
444
445 assert(r >= 0);
446 if (f->fd != 1 && f->fd != 2)
447 f->off += r;
448
449 return r;
450 }
451 else {
452 /* symbolic file */
453 size_t actual_count = 0;
454 if (f->off + count <= f->dfile->size)
455 actual_count = count;
456 else {
457 if (__exe_env.save_all_writes)
458 assert(0);
459 else {
460 if (f->off < (off64_t) f->dfile->size)
461 actual_count = f->dfile->size - f->off;
462 }
463 }
464
465 if (actual_count)
466 memcpy(f->dfile->contents + f->off, buf, actual_count);
467
468 if (count != actual_count)
469 klee_warning("write() ignores bytes.\n");
470
471 if (f->dfile == __exe_fs.sym_stdout)
472 __exe_fs.stdout_writes += actual_count;
473
474 f->off += count;
475 return count;
476 }
477 }
478
479
__fd_lseek(int fd,off64_t offset,int whence)480 off64_t __fd_lseek(int fd, off64_t offset, int whence) {
481 off64_t new_off;
482 exe_file_t *f = __get_file(fd);
483
484 if (!f) {
485 errno = EBADF;
486 return -1;
487 }
488
489 if (!f->dfile) {
490 /* We could always do SEEK_SET then whence, but this causes
491 troubles with directories since we play nasty tricks with the
492 offset, and the OS doesn't want us to randomly seek
493 directories. We could detect if it is a directory and correct
494 the offset, but really directories should only be SEEK_SET, so
495 this solves the problem. */
496 if (whence == SEEK_SET) {
497 new_off = syscall(__NR_lseek, f->fd, offset, SEEK_SET);
498 } else {
499 new_off = syscall(__NR_lseek, f->fd, f->off, SEEK_SET);
500
501 /* If we can't seek to start off, just return same error.
502 Probably ESPIPE. */
503 if (new_off != -1) {
504 assert(new_off == f->off);
505 new_off = syscall(__NR_lseek, f->fd, offset, whence);
506 }
507 }
508
509 if (new_off == -1)
510 return -1;
511
512 f->off = new_off;
513 return new_off;
514 }
515
516 switch (whence) {
517 case SEEK_SET: new_off = offset; break;
518 case SEEK_CUR: new_off = f->off + offset; break;
519 case SEEK_END: new_off = f->dfile->size + offset; break;
520 default: {
521 errno = EINVAL;
522 return (off64_t) -1;
523 }
524 }
525
526 if (new_off < 0) {
527 errno = EINVAL;
528 return (off64_t) -1;
529 }
530
531 f->off = new_off;
532 return f->off;
533 }
534
__fd_stat(const char * path,struct stat64 * buf)535 int __fd_stat(const char *path, struct stat64 *buf) {
536 exe_disk_file_t *dfile = __get_sym_file(path);
537 if (dfile) {
538 memcpy(buf, dfile->stat, sizeof(*dfile->stat));
539 return 0;
540 }
541
542 {
543 #if __WORDSIZE == 64
544 return syscall(__NR_stat, __concretize_string(path), buf);
545 #else
546 return syscall(__NR_stat64, __concretize_string(path), buf);
547 #endif
548 }
549 }
550
fstatat(int fd,const char * path,struct stat * buf,int flags)551 int fstatat(int fd, const char *path, struct stat *buf, int flags) {
552 if (fd != AT_FDCWD) {
553 exe_file_t *f = __get_file(fd);
554
555 if (!f) {
556 errno = EBADF;
557 return -1;
558 } else if (f->dfile) {
559 klee_warning("symbolic file descriptor, ignoring (ENOENT)");
560 errno = ENOENT;
561 return -1;
562 }
563 fd = f->fd;
564 }
565 exe_disk_file_t *dfile = __get_sym_file(path);
566 if (dfile) {
567 memcpy(buf, dfile->stat, sizeof(*dfile->stat));
568 return 0;
569 }
570
571 #if (defined __NR_newfstatat) && (__NR_newfstatat != 0)
572 return syscall(__NR_newfstatat, (long)fd,
573 (path ? __concretize_string(path) : NULL), buf, (long)flags);
574 #else
575 return syscall(__NR_fstatat64, (long)fd,
576 (path ? __concretize_string(path) : NULL), buf, (long)flags);
577 #endif
578 }
579
580
__fd_lstat(const char * path,struct stat64 * buf)581 int __fd_lstat(const char *path, struct stat64 *buf) {
582 exe_disk_file_t *dfile = __get_sym_file(path);
583 if (dfile) {
584 memcpy(buf, dfile->stat, sizeof(*dfile->stat));
585 return 0;
586 }
587
588 {
589 #if __WORDSIZE == 64
590 return syscall(__NR_lstat, __concretize_string(path), buf);
591 #else
592 return syscall(__NR_lstat64, __concretize_string(path), buf);
593 #endif
594 }
595 }
596
chdir(const char * path)597 int chdir(const char *path) {
598 exe_disk_file_t *dfile = __get_sym_file(path);
599
600 if (dfile) {
601 /* XXX incorrect */
602 klee_warning("symbolic file, ignoring (ENOENT)");
603 errno = ENOENT;
604 return -1;
605 }
606
607 return syscall(__NR_chdir, __concretize_string(path));
608 }
609
fchdir(int fd)610 int fchdir(int fd) {
611 exe_file_t *f = __get_file(fd);
612
613 if (!f) {
614 errno = EBADF;
615 return -1;
616 }
617
618 if (f->dfile) {
619 klee_warning("symbolic file, ignoring (ENOENT)");
620 errno = ENOENT;
621 return -1;
622 }
623
624 return syscall(__NR_fchdir, f->fd);
625 }
626
627 /* Sets mode and or errno and return appropriate result. */
__df_chmod(exe_disk_file_t * df,mode_t mode)628 static int __df_chmod(exe_disk_file_t *df, mode_t mode) {
629 if (geteuid() == df->stat->st_uid) {
630 if (getgid() != df->stat->st_gid)
631 mode &= ~ S_ISGID;
632 df->stat->st_mode = ((df->stat->st_mode & ~07777) |
633 (mode & 07777));
634 return 0;
635 } else {
636 errno = EPERM;
637 return -1;
638 }
639 }
640
chmod(const char * path,mode_t mode)641 int chmod(const char *path, mode_t mode) {
642 static int n_calls = 0;
643
644 exe_disk_file_t *dfile = __get_sym_file(path);
645
646 n_calls++;
647 if (__exe_fs.max_failures && *__exe_fs.chmod_fail == n_calls) {
648 __exe_fs.max_failures--;
649 errno = EIO;
650 return -1;
651 }
652
653 if (dfile) {
654 return __df_chmod(dfile, mode);
655 }
656
657 return syscall(__NR_chmod, __concretize_string(path), mode);
658 }
659
fchmod(int fd,mode_t mode)660 int fchmod(int fd, mode_t mode) {
661 static int n_calls = 0;
662
663 exe_file_t *f = __get_file(fd);
664
665 if (!f) {
666 errno = EBADF;
667 return -1;
668 }
669
670 n_calls++;
671 if (__exe_fs.max_failures && *__exe_fs.fchmod_fail == n_calls) {
672 __exe_fs.max_failures--;
673 errno = EIO;
674 return -1;
675 }
676
677 if (f->dfile) {
678 return __df_chmod(f->dfile, mode);
679 }
680
681 return syscall(__NR_fchmod, f->fd, mode);
682 }
683
__df_chown(exe_disk_file_t * df,uid_t owner,gid_t group)684 static int __df_chown(exe_disk_file_t *df, uid_t owner, gid_t group) {
685 klee_warning("symbolic file, ignoring (EPERM)");
686 errno = EPERM;
687 return -1;
688 }
689
chown(const char * path,uid_t owner,gid_t group)690 int chown(const char *path, uid_t owner, gid_t group) {
691 exe_disk_file_t *df = __get_sym_file(path);
692
693 if (df) {
694 return __df_chown(df, owner, group);
695 }
696
697 return syscall(__NR_chown, __concretize_string(path), owner, group);
698 }
699
fchown(int fd,uid_t owner,gid_t group)700 int fchown(int fd, uid_t owner, gid_t group) {
701 exe_file_t *f = __get_file(fd);
702
703 if (!f) {
704 errno = EBADF;
705 return -1;
706 }
707
708 if (f->dfile) {
709 return __df_chown(f->dfile, owner, group);
710 }
711
712 return syscall(__NR_fchown, fd, owner, group);
713 }
714
lchown(const char * path,uid_t owner,gid_t group)715 int lchown(const char *path, uid_t owner, gid_t group) {
716 /* XXX Ignores 'l' part */
717 exe_disk_file_t *df = __get_sym_file(path);
718
719 if (df) {
720 return __df_chown(df, owner, group);
721 }
722
723 return syscall(__NR_chown, __concretize_string(path), owner, group);
724 }
725
__fd_fstat(int fd,struct stat64 * buf)726 int __fd_fstat(int fd, struct stat64 *buf) {
727 exe_file_t *f = __get_file(fd);
728
729 if (!f) {
730 errno = EBADF;
731 return -1;
732 }
733
734 if (!f->dfile) {
735 #if __WORDSIZE == 64
736 return syscall(__NR_fstat, f->fd, buf);
737 #else
738 return syscall(__NR_fstat64, f->fd, buf);
739 #endif
740 }
741
742 memcpy(buf, f->dfile->stat, sizeof(*f->dfile->stat));
743 return 0;
744 }
745
__fd_ftruncate(int fd,off64_t length)746 int __fd_ftruncate(int fd, off64_t length) {
747 static int n_calls = 0;
748 exe_file_t *f = __get_file(fd);
749
750 n_calls++;
751
752 if (!f) {
753 errno = EBADF;
754 return -1;
755 }
756
757 if (__exe_fs.max_failures && *__exe_fs.ftruncate_fail == n_calls) {
758 __exe_fs.max_failures--;
759 errno = EIO;
760 return -1;
761 }
762
763 if (f->dfile) {
764 klee_warning("symbolic file, ignoring (EIO)");
765 errno = EIO;
766 return -1;
767 }
768 #if __WORDSIZE == 64
769 return syscall(__NR_ftruncate, f->fd, length);
770 #else
771 return syscall(__NR_ftruncate64, f->fd, length);
772 #endif
773 }
774
__fd_getdents(unsigned int fd,struct dirent64 * dirp,unsigned int count)775 int __fd_getdents(unsigned int fd, struct dirent64 *dirp, unsigned int count) {
776 exe_file_t *f = __get_file(fd);
777
778 if (!f) {
779 errno = EBADF;
780 return -1;
781 }
782
783 if (f->dfile) {
784 klee_warning("symbolic file, ignoring (EINVAL)");
785 errno = EINVAL;
786 return -1;
787 } else {
788 if ((unsigned long) f->off < 4096u) {
789 /* Return our dirents */
790 off64_t i, pad, bytes=0;
791
792 /* What happens for bad offsets? */
793 i = f->off / sizeof(*dirp);
794 if (((off64_t) (i * sizeof(*dirp)) != f->off) ||
795 i > __exe_fs.n_sym_files) {
796 errno = EINVAL;
797 return -1;
798 }
799 for (; i<__exe_fs.n_sym_files; ++i) {
800 exe_disk_file_t *df = &__exe_fs.sym_files[i];
801 dirp->d_ino = df->stat->st_ino;
802 dirp->d_reclen = sizeof(*dirp);
803 dirp->d_type = IFTODT(df->stat->st_mode);
804 dirp->d_name[0] = 'A' + i;
805 dirp->d_name[1] = '\0';
806 #ifdef _DIRENT_HAVE_D_OFF
807 dirp->d_off = (i+1) * sizeof(*dirp);
808 #endif
809 bytes += dirp->d_reclen;
810 ++dirp;
811 }
812
813 /* Fake jump to OS records by a "deleted" file. */
814 pad = count>=4096 ? 4096 : count;
815 dirp->d_ino = 0;
816 dirp->d_reclen = pad - bytes;
817 dirp->d_type = DT_UNKNOWN;
818 dirp->d_name[0] = '\0';
819 #ifdef _DIRENT_HAVE_D_OFF
820 dirp->d_off = 4096;
821 #endif
822 bytes += dirp->d_reclen;
823 f->off = pad;
824
825 return bytes;
826 } else {
827 off64_t os_pos = f->off - 4096;
828 int res;
829 off64_t s = 0;
830
831 /* For reasons which I really don't understand, if I don't
832 memset this then sometimes the kernel returns d_ino==0 for
833 some valid entries? Am I crazy? Can writeback possibly be
834 failing?
835
836 Even more bizarre, interchanging the memset and the seek also
837 case strange behavior. Really should be debugged properly. */
838 memset(dirp, 0, count);
839 s = syscall(__NR_lseek, f->fd, os_pos, SEEK_SET);
840 assert(s != (off64_t) -1);
841 res = syscall(__NR_getdents64, f->fd, dirp, count);
842 if (res > -1) {
843 int pos = 0;
844 f->off = syscall(__NR_lseek, f->fd, 0, SEEK_CUR);
845 assert(f->off != (off64_t)-1);
846 f->off += 4096;
847
848 /* Patch offsets */
849 while (pos < res) {
850 struct dirent64 *dp = (struct dirent64*) ((char*) dirp + pos);
851 #ifdef _DIRENT_HAVE_D_OFF
852 dp->d_off += 4096;
853 #endif
854 pos += dp->d_reclen;
855 }
856 }
857 return res;
858 }
859 }
860 }
861
862 #if __WORDSIZE == 64
ioctl(int fd,unsigned long int request,...)863 int ioctl(int fd, unsigned long int request, ...) {
864 #else
865 int ioctl(int fd, unsigned long request, ...) {
866 #endif
867 exe_file_t *f = __get_file(fd);
868 va_list ap;
869 void *buf;
870
871 #if 0
872 printf("In ioctl(%d, ...)\n", fd);
873 #endif
874
875 if (!f) {
876 errno = EBADF;
877 return -1;
878 }
879
880 va_start(ap, request);
881 buf = va_arg(ap, void*);
882 va_end(ap);
883 if (f->dfile) {
884 struct stat *stat = (struct stat*) f->dfile->stat;
885
886 switch (request) {
887 case TCGETS: {
888 struct termios *ts = buf;
889
890 klee_warning_once("(TCGETS) symbolic file, incomplete model");
891
892 /* XXX need more data, this is ok but still not good enough */
893 if (S_ISCHR(stat->st_mode)) {
894 /* Just copied from my system, munged to match what fields
895 uclibc thinks are there. */
896 ts->c_iflag = 27906;
897 ts->c_oflag = 5;
898 ts->c_cflag = 1215;
899 ts->c_lflag = 35287;
900 #ifdef __GLIBC__
901 ts->c_line = 0;
902 #endif
903 ts->c_cc[0] = '\x03';
904 ts->c_cc[1] = '\x1c';
905 ts->c_cc[2] = '\x7f';
906 ts->c_cc[3] = '\x15';
907 ts->c_cc[4] = '\x04';
908 ts->c_cc[5] = '\x00';
909 ts->c_cc[6] = '\x01';
910 ts->c_cc[7] = '\xff';
911 ts->c_cc[8] = '\x11';
912 ts->c_cc[9] = '\x13';
913 ts->c_cc[10] = '\x1a';
914 ts->c_cc[11] = '\xff';
915 ts->c_cc[12] = '\x12';
916 ts->c_cc[13] = '\x0f';
917 ts->c_cc[14] = '\x17';
918 ts->c_cc[15] = '\x16';
919 ts->c_cc[16] = '\xff';
920 ts->c_cc[17] = '\x0';
921 ts->c_cc[18] = '\x0';
922 return 0;
923 } else {
924 errno = ENOTTY;
925 return -1;
926 }
927 }
928 case TCSETS: {
929 /* const struct termios *ts = buf; */
930 klee_warning_once("(TCSETS) symbolic file, silently ignoring");
931 if (S_ISCHR(stat->st_mode)) {
932 return 0;
933 } else {
934 errno = ENOTTY;
935 return -1;
936 }
937 }
938 case TCSETSW: {
939 /* const struct termios *ts = buf; */
940 klee_warning_once("(TCSETSW) symbolic file, silently ignoring");
941 if (fd==0) {
942 return 0;
943 } else {
944 errno = ENOTTY;
945 return -1;
946 }
947 }
948 case TCSETSF: {
949 /* const struct termios *ts = buf; */
950 klee_warning_once("(TCSETSF) symbolic file, silently ignoring");
951 if (S_ISCHR(stat->st_mode)) {
952 return 0;
953 } else {
954 errno = ENOTTY;
955 return -1;
956 }
957 }
958 case TIOCGWINSZ: {
959 struct winsize *ws = buf;
960 ws->ws_row = 24;
961 ws->ws_col = 80;
962 klee_warning_once("(TIOCGWINSZ) symbolic file, incomplete model");
963 if (S_ISCHR(stat->st_mode)) {
964 return 0;
965 } else {
966 errno = ENOTTY;
967 return -1;
968 }
969 }
970 case TIOCSWINSZ: {
971 /* const struct winsize *ws = buf; */
972 klee_warning_once("(TIOCSWINSZ) symbolic file, ignoring (EINVAL)");
973 if (S_ISCHR(stat->st_mode)) {
974 errno = EINVAL;
975 return -1;
976 } else {
977 errno = ENOTTY;
978 return -1;
979 }
980 }
981 case FIONREAD: {
982 int *res = buf;
983 klee_warning_once("(FIONREAD) symbolic file, incomplete model");
984 if (S_ISCHR(stat->st_mode)) {
985 if (f->off < (off64_t) f->dfile->size) {
986 *res = f->dfile->size - f->off;
987 } else {
988 *res = 0;
989 }
990 return 0;
991 } else {
992 errno = ENOTTY;
993 return -1;
994 }
995 }
996 case MTIOCGET: {
997 klee_warning("(MTIOCGET) symbolic file, ignoring (EINVAL)");
998 errno = EINVAL;
999 return -1;
1000 }
1001 default:
1002 klee_warning("symbolic file, ignoring (EINVAL)");
1003 errno = EINVAL;
1004 return -1;
1005 }
1006 }
1007 return syscall(__NR_ioctl, f->fd, request, buf);
1008 }
1009
1010 int fcntl(int fd, int cmd, ...) {
1011 exe_file_t *f = __get_file(fd);
1012 va_list ap;
1013 unsigned arg; /* 32 bit assumption (int/ptr) */
1014 struct flock *lock;
1015
1016 if (!f) {
1017 errno = EBADF;
1018 return -1;
1019 }
1020 #ifdef F_GETSIG
1021 if (cmd==F_GETFD || cmd==F_GETFL || cmd==F_GETOWN || cmd==F_GETSIG ||
1022 cmd==F_GETLEASE || cmd==F_NOTIFY) {
1023 #else
1024 if (cmd==F_GETFD || cmd==F_GETFL || cmd==F_GETOWN) {
1025 #endif
1026 arg = 0;
1027 } else if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW) {
1028 va_start(ap, cmd);
1029 lock = va_arg(ap, struct flock *);
1030 va_end(ap);
1031 } else {
1032 va_start(ap, cmd);
1033 arg = va_arg(ap, int);
1034 va_end(ap);
1035 }
1036
1037 if (f->dfile) {
1038 switch(cmd) {
1039 case F_GETFD: {
1040 int flags = 0;
1041 if (f->flags & eCloseOnExec)
1042 flags |= FD_CLOEXEC;
1043 return flags;
1044 }
1045 case F_SETFD: {
1046 f->flags &= ~eCloseOnExec;
1047 if (arg & FD_CLOEXEC)
1048 f->flags |= eCloseOnExec;
1049 return 0;
1050 }
1051 case F_GETFL: {
1052 /* XXX (CrC): This should return the status flags: O_APPEND,
1053 O_ASYNC, O_DIRECT, O_NOATIME, O_NONBLOCK. As of now, we
1054 discard these flags during open(). We should save them and
1055 return them here. These same flags can be set by F_SETFL,
1056 which we could also handle properly.
1057 */
1058 return 0;
1059 }
1060 // Initially no other process keeps a lock, so we say the file is unlocked.
1061 // Of course this doesn't account for a program locking and then checking if
1062 // a lock is there. However this is quite paranoid programming and we assume
1063 // doesn't happen.
1064 case F_GETLK: {
1065 lock->l_type = F_UNLCK;
1066 return 0;
1067 }
1068 // We assume the application does locking correctly and will lock/unlock
1069 // files correctly.
1070 // Therefore this call always succeeds.
1071 case F_SETLK: {
1072 return 0;
1073 }
1074 default:
1075 klee_warning("symbolic file, ignoring (EINVAL)");
1076 errno = EINVAL;
1077 return -1;
1078 }
1079 }
1080 return syscall(__NR_fcntl, f->fd, cmd, arg);
1081 }
1082
1083 int __fd_statfs(const char *path, struct statfs *buf) {
1084 exe_disk_file_t *dfile = __get_sym_file(path);
1085 if (dfile) {
1086 /* XXX incorrect */
1087 klee_warning("symbolic file, ignoring (ENOENT)");
1088 errno = ENOENT;
1089 return -1;
1090 }
1091
1092 return syscall(__NR_statfs, __concretize_string(path), buf);
1093 }
1094
1095 int fstatfs(int fd, struct statfs *buf) {
1096 exe_file_t *f = __get_file(fd);
1097
1098 if (!f) {
1099 errno = EBADF;
1100 return -1;
1101 }
1102
1103 if (f->dfile) {
1104 klee_warning("symbolic file, ignoring (EBADF)");
1105 errno = EBADF;
1106 return -1;
1107 }
1108 return syscall(__NR_fstatfs, f->fd, buf);
1109 }
1110
1111 int fsync(int fd) {
1112 exe_file_t *f = __get_file(fd);
1113
1114 if (!f) {
1115 errno = EBADF;
1116 return -1;
1117 } else if (f->dfile) {
1118 return 0;
1119 }
1120 return syscall(__NR_fsync, f->fd);
1121 }
1122
1123 int dup2(int oldfd, int newfd) {
1124 exe_file_t *f = __get_file(oldfd);
1125
1126 if (!f || !(newfd>=0 && newfd<MAX_FDS)) {
1127 errno = EBADF;
1128 return -1;
1129 } else {
1130 exe_file_t *f2 = &__exe_env.fds[newfd];
1131 if (f2->flags & eOpen) close(newfd);
1132
1133 /* XXX Incorrect, really we need another data structure for open
1134 files */
1135 *f2 = *f;
1136
1137 f2->flags &= ~eCloseOnExec;
1138
1139 /* I'm not sure it is wise, but we can get away with not dup'ng
1140 the OS fd, since actually that will in many cases effect the
1141 sharing of the open file (and the process should never have
1142 access to it). */
1143
1144 return newfd;
1145 }
1146 }
1147
1148 int dup(int oldfd) {
1149 exe_file_t *f = __get_file(oldfd);
1150 if (!f) {
1151 errno = EBADF;
1152 return -1;
1153 } else {
1154 int fd;
1155 for (fd = 0; fd < MAX_FDS; ++fd)
1156 if (!(__exe_env.fds[fd].flags & eOpen))
1157 break;
1158 if (fd == MAX_FDS) {
1159 errno = EMFILE;
1160 return -1;
1161 } else {
1162 return dup2(oldfd, fd);
1163 }
1164 }
1165 }
1166
1167 int rmdir(const char *pathname) {
1168 exe_disk_file_t *dfile = __get_sym_file(pathname);
1169 if (dfile) {
1170 /* XXX check access */
1171 if (S_ISDIR(dfile->stat->st_mode)) {
1172 dfile->stat->st_ino = 0;
1173 return 0;
1174 } else {
1175 errno = ENOTDIR;
1176 return -1;
1177 }
1178 }
1179
1180 klee_warning("ignoring (EPERM)");
1181 errno = EPERM;
1182 return -1;
1183 }
1184
1185 int unlink(const char *pathname) {
1186 exe_disk_file_t *dfile = __get_sym_file(pathname);
1187 if (dfile) {
1188 /* XXX check access */
1189 if (S_ISREG(dfile->stat->st_mode)) {
1190 dfile->stat->st_ino = 0;
1191 return 0;
1192 } else if (S_ISDIR(dfile->stat->st_mode)) {
1193 errno = EISDIR;
1194 return -1;
1195 } else {
1196 errno = EPERM;
1197 return -1;
1198 }
1199 }
1200
1201 klee_warning("ignoring (EPERM)");
1202 errno = EPERM;
1203 return -1;
1204 }
1205
1206 int unlinkat(int dirfd, const char *pathname, int flags) {
1207 /* similar to unlink. keep them separated though to avoid
1208 problems if unlink changes to actually delete files */
1209 exe_disk_file_t *dfile = __get_sym_file(pathname);
1210 if (dfile) {
1211 /* XXX check access */
1212 if (S_ISREG(dfile->stat->st_mode)) {
1213 dfile->stat->st_ino = 0;
1214 return 0;
1215 } else if (S_ISDIR(dfile->stat->st_mode)) {
1216 errno = EISDIR;
1217 return -1;
1218 } else {
1219 errno = EPERM;
1220 return -1;
1221 }
1222 }
1223
1224 klee_warning("ignoring (EPERM)");
1225 errno = EPERM;
1226 return -1;
1227 }
1228
1229 ssize_t readlink(const char *path, char *buf, size_t bufsize) {
1230 exe_disk_file_t *dfile = __get_sym_file(path);
1231 if (dfile) {
1232 /* XXX We need to get the sym file name really, but since we don't
1233 handle paths anyway... */
1234 if (S_ISLNK(dfile->stat->st_mode)) {
1235 buf[0] = path[0];
1236 if (bufsize>1) buf[1] = '.';
1237 if (bufsize>2) buf[2] = 'l';
1238 if (bufsize>3) buf[3] = 'n';
1239 if (bufsize>4) buf[4] = 'k';
1240 return (bufsize>5) ? 5 : bufsize;
1241 } else {
1242 errno = EINVAL;
1243 return -1;
1244 }
1245 }
1246 return syscall(__NR_readlink, path, buf, bufsize);
1247 }
1248
1249 #undef FD_SET
1250 #undef FD_CLR
1251 #undef FD_ISSET
1252 #undef FD_ZERO
1253 #define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
1254 #define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
1255 #define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
1256 #define FD_ZERO(p) memset((char *)(p), '\0', sizeof(*(p)))
1257 int select(int nfds, fd_set *read, fd_set *write,
1258 fd_set *except, struct timeval *timeout) {
1259 fd_set in_read, in_write, in_except, os_read, os_write, os_except;
1260 int i, count = 0, os_nfds = 0;
1261
1262 if (read) {
1263 in_read = *read;
1264 FD_ZERO(read);
1265 } else {
1266 FD_ZERO(&in_read);
1267 }
1268
1269 if (write) {
1270 in_write = *write;
1271 FD_ZERO(write);
1272 } else {
1273 FD_ZERO(&in_write);
1274 }
1275
1276 if (except) {
1277 in_except = *except;
1278 FD_ZERO(except);
1279 } else {
1280 FD_ZERO(&in_except);
1281 }
1282
1283 FD_ZERO(&os_read);
1284 FD_ZERO(&os_write);
1285 FD_ZERO(&os_except);
1286
1287 /* Check for symbolic stuff */
1288 for (i=0; i<nfds; i++) {
1289 if (FD_ISSET(i, &in_read) || FD_ISSET(i, &in_write) || FD_ISSET(i, &in_except)) {
1290 exe_file_t *f = __get_file(i);
1291 if (!f) {
1292 errno = EBADF;
1293 return -1;
1294 } else if (f->dfile) {
1295 /* Operations on this fd will never block... */
1296 if (FD_ISSET(i, &in_read)) FD_SET(i, read);
1297 if (FD_ISSET(i, &in_write)) FD_SET(i, write);
1298 if (FD_ISSET(i, &in_except)) FD_SET(i, except);
1299 ++count;
1300 } else {
1301 if (FD_ISSET(i, &in_read)) FD_SET(f->fd, &os_read);
1302 if (FD_ISSET(i, &in_write)) FD_SET(f->fd, &os_write);
1303 if (FD_ISSET(i, &in_except)) FD_SET(f->fd, &os_except);
1304 if (f->fd >= os_nfds) os_nfds = f->fd + 1;
1305 }
1306 }
1307 }
1308
1309 if (os_nfds > 0) {
1310 /* Never allow blocking select. This is broken but what else can
1311 we do. */
1312 struct timeval tv = { 0, 0 };
1313 int r = syscall(__NR_select, os_nfds,
1314 &os_read, &os_write, &os_except, &tv);
1315
1316 if (r == -1) {
1317 /* If no symbolic results, return error. Otherwise we will
1318 silently ignore the OS error. */
1319 if (!count)
1320 return -1;
1321 } else {
1322 count += r;
1323
1324 /* Translate resulting sets back */
1325 for (i=0; i<nfds; i++) {
1326 exe_file_t *f = __get_file(i);
1327 if (f && !f->dfile) {
1328 if (read && FD_ISSET(f->fd, &os_read)) FD_SET(i, read);
1329 if (write && FD_ISSET(f->fd, &os_write)) FD_SET(i, write);
1330 if (except && FD_ISSET(f->fd, &os_except)) FD_SET(i, except);
1331 }
1332 }
1333 }
1334 }
1335
1336 return count;
1337 }
1338
1339 /*** Library functions ***/
1340
1341 char *getcwd(char *buf, size_t size) {
1342 static int n_calls = 0;
1343 int r;
1344
1345 n_calls++;
1346
1347 if (__exe_fs.max_failures && *__exe_fs.getcwd_fail == n_calls) {
1348 __exe_fs.max_failures--;
1349 errno = ERANGE;
1350 return NULL;
1351 }
1352
1353 if (!buf) {
1354 if (!size)
1355 size = 1024;
1356 buf = malloc(size);
1357 }
1358
1359 buf = __concretize_ptr(buf);
1360 size = __concretize_size(size);
1361 /* XXX In terms of looking for bugs we really should do this check
1362 before concretization, at least once the routine has been fixed
1363 to properly work with symbolics. */
1364 klee_check_memory_access(buf, size);
1365 r = syscall(__NR_getcwd, buf, size);
1366 if (r == -1)
1367 return NULL;
1368 return buf;
1369 }
1370
1371 /*** Helper functions ***/
1372
1373 static void *__concretize_ptr(const void *p) {
1374 /* XXX 32-bit assumption */
1375 char *pc = (char*) klee_get_valuel((long) p);
1376 klee_assume(pc == p);
1377 return pc;
1378 }
1379
1380 static size_t __concretize_size(size_t s) {
1381 size_t sc = klee_get_valuel((long)s);
1382 klee_assume(sc == s);
1383 return sc;
1384 }
1385
1386 static const char *__concretize_string(const char *s) {
1387 char *sc = __concretize_ptr(s);
1388 unsigned i;
1389
1390 for (i = 0;; ++i, ++sc) {
1391 char c = *sc;
1392 // Avoid writing read-only memory locations
1393 if (!klee_is_symbolic(c)) {
1394 if (!c)
1395 break;
1396 continue;
1397 }
1398 if (!(i&(i-1))) {
1399 if (!c) {
1400 *sc = 0;
1401 break;
1402 } else if (c=='/') {
1403 *sc = '/';
1404 }
1405 } else {
1406 char cc = (char) klee_get_valuel((long)c);
1407 klee_assume(cc == c);
1408 *sc = cc;
1409 if (!cc) break;
1410 }
1411 }
1412
1413 return s;
1414 }
1415
1416
1417
1418 /* Trivial model:
1419 if path is "/" (basically no change) accept, otherwise reject
1420 */
1421 int chroot(const char *path) {
1422 if (path[0] == '\0') {
1423 errno = ENOENT;
1424 return -1;
1425 }
1426
1427 if (path[0] == '/' && path[1] == '\0') {
1428 return 0;
1429 }
1430
1431 klee_warning("ignoring (ENOENT)");
1432 errno = ENOENT;
1433 return -1;
1434 }
1435