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