]> mj.ucw.cz Git - moe.git/blob - box/box.c
7b670a256f4e5b1bb3ac4aac705f015dd3463577
[moe.git] / box / box.c
1 /*
2  *      A Simple Sandbox for Moe
3  *
4  *      (c) 2001--2010 Martin Mares <mj@ucw.cz>
5  */
6
7 #define _LARGEFILE64_SOURCE
8 #define _GNU_SOURCE
9
10 #include "autoconf.h"
11
12 #include <errno.h>
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdarg.h>
18 #include <stdint.h>
19 #include <unistd.h>
20 #include <getopt.h>
21 #include <time.h>
22 #include <sys/wait.h>
23 #include <sys/user.h>
24 #include <sys/time.h>
25 #include <sys/ptrace.h>
26 #include <sys/signal.h>
27 #include <sys/sysinfo.h>
28 #include <sys/resource.h>
29 #include <sys/utsname.h>
30 #include <linux/ptrace.h>
31
32 #if defined(CONFIG_BOX_KERNEL_AMD64) && !defined(CONFIG_BOX_USER_AMD64)
33 #include <asm/unistd_32.h>
34 #define NATIVE_NR_execve 59             /* 64-bit execve */
35 #else
36 #include <asm/unistd.h>
37 #define NATIVE_NR_execve __NR_execve
38 #endif
39
40 #define NONRET __attribute__((noreturn))
41 #define UNUSED __attribute__((unused))
42 #define ARRAY_SIZE(a) (int)(sizeof(a)/sizeof(a[0]))
43
44 static int filter_syscalls;             /* 0=off, 1=liberal, 2=totalitarian */
45 static int timeout;                     /* milliseconds */
46 static int wall_timeout;
47 static int extra_timeout;
48 static int pass_environ;
49 static int file_access;
50 static int verbose;
51 static int memory_limit;
52 static int stack_limit;
53 static char *redir_stdin, *redir_stdout, *redir_stderr;
54 static char *set_cwd;
55
56 static pid_t box_pid;
57 static int is_ptraced;
58 static volatile int timer_tick;
59 static struct timeval start_time;
60 static int ticks_per_sec;
61 static int exec_seen;
62 static int partial_line;
63
64 static int mem_peak_kb;
65 static int total_ms, wall_ms;
66
67 static void die(char *msg, ...) NONRET;
68 static void sample_mem_peak(void);
69
70 /*** Meta-files ***/
71
72 static FILE *metafile;
73
74 static void
75 meta_open(const char *name)
76 {
77   if (!strcmp(name, "-"))
78     {
79       metafile = stdout;
80       return;
81     }
82   metafile = fopen(name, "w");
83   if (!metafile)
84     die("Failed to open metafile '%s'",name);
85 }
86
87 static void
88 meta_close(void)
89 {
90   if (metafile && metafile != stdout)
91     fclose(metafile);
92 }
93
94 static void __attribute__((format(printf,1,2)))
95 meta_printf(const char *fmt, ...)
96 {
97   if (!metafile)
98     return;
99
100   va_list args;
101   va_start(args, fmt);
102   vfprintf(metafile, fmt, args);
103   va_end(args);
104 }
105
106 static void
107 final_stats(struct rusage *rus)
108 {
109   struct timeval total, now, wall;
110   timeradd(&rus->ru_utime, &rus->ru_stime, &total);
111   total_ms = total.tv_sec*1000 + total.tv_usec/1000;
112   gettimeofday(&now, NULL);
113   timersub(&now, &start_time, &wall);
114   wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
115
116   meta_printf("time:%d.%03d\n", total_ms/1000, total_ms%1000);
117   meta_printf("time-wall:%d.%03d\n", wall_ms/1000, wall_ms%1000);
118   meta_printf("mem:%llu\n", (unsigned long long) mem_peak_kb * 1024);
119 }
120
121 /*** Messages and exits ***/
122
123 static void NONRET
124 box_exit(int rc)
125 {
126   if (box_pid > 0)
127     {
128       sample_mem_peak();
129       if (is_ptraced)
130         ptrace(PTRACE_KILL, box_pid);
131       kill(-box_pid, SIGKILL);
132       kill(box_pid, SIGKILL);
133       meta_printf("killed:1\n");
134
135       struct rusage rus;
136       int p, stat;
137       do
138         p = wait4(box_pid, &stat, 0, &rus);
139       while (p < 0 && errno == EINTR);
140       if (p < 0)
141         fprintf(stderr, "UGH: Lost track of the process (%m)\n");
142       else
143         final_stats(&rus);
144     }
145   meta_close();
146   exit(rc);
147 }
148
149 static void
150 flush_line(void)
151 {
152   if (partial_line)
153     fputc('\n', stderr);
154   partial_line = 0;
155 }
156
157 /* Report an error of the sandbox itself */
158 static void NONRET __attribute__((format(printf,1,2)))
159 die(char *msg, ...)
160 {
161   va_list args;
162   va_start(args, msg);
163   flush_line();
164   char buf[1024];
165   vsnprintf(buf, sizeof(buf), msg, args);
166   meta_printf("status:XX\nmessage:%s\n", buf);
167   fputs(buf, stderr);
168   fputc('\n', stderr);
169   box_exit(2);
170 }
171
172 /* Report an error of the program inside the sandbox */
173 static void NONRET __attribute__((format(printf,1,2)))
174 err(char *msg, ...)
175 {
176   va_list args;
177   va_start(args, msg);
178   flush_line();
179   if (msg[0] && msg[1] && msg[2] == ':' && msg[3] == ' ')
180     {
181       meta_printf("status:%c%c\n", msg[0], msg[1]);
182       msg += 4;
183     }
184   char buf[1024];
185   vsnprintf(buf, sizeof(buf), msg, args);
186   meta_printf("message:%s\n", buf);
187   fputs(buf, stderr);
188   fputc('\n', stderr);
189   box_exit(1);
190 }
191
192 /* Write a message, but only if in verbose mode */
193 static void __attribute__((format(printf,1,2)))
194 msg(char *msg, ...)
195 {
196   va_list args;
197   va_start(args, msg);
198   if (verbose)
199     {
200       int len = strlen(msg);
201       if (len > 0)
202         partial_line = (msg[len-1] != '\n');
203       vfprintf(stderr, msg, args);
204       fflush(stderr);
205     }
206   va_end(args);
207 }
208
209 static void *
210 xmalloc(size_t size)
211 {
212   void *p = malloc(size);
213   if (!p)
214     die("Out of memory");
215   return p;
216 }
217
218 /*** Syscall rules ***/
219
220 static const char * const syscall_names[] = {
221 #include "box/syscall-table.h"
222 };
223 #define NUM_SYSCALLS ARRAY_SIZE(syscall_names)
224 #define NUM_ACTIONS (NUM_SYSCALLS+64)
225
226 enum action {
227   A_DEFAULT,            // Use the default action
228   A_NO,                 // Always forbid
229   A_YES,                // Always permit
230   A_FILENAME,           // Permit if arg1 is a known filename
231   A_ACTION_MASK = 15,
232   A_NO_RETVAL = 32,     // Does not return a value
233   A_SAMPLE_MEM = 64,    // Sample memory usage before the syscall
234   A_LIBERAL = 128,      // Valid only in liberal mode
235   // Must fit in a unsigned char
236 };
237
238 static unsigned char syscall_action[NUM_ACTIONS] = {
239 #define S(x) [__NR_##x]
240
241     // Syscalls permitted for specific file names
242     S(open) = A_FILENAME,
243     S(creat) = A_FILENAME,
244     S(unlink) = A_FILENAME,
245     S(access) = A_FILENAME,                     
246     S(truncate) = A_FILENAME,
247     S(stat) = A_FILENAME,
248     S(lstat) = A_FILENAME,
249     S(readlink) = A_FILENAME,
250 #ifndef CONFIG_BOX_USER_AMD64
251     S(oldstat) = A_FILENAME,
252     S(oldlstat) = A_FILENAME,
253     S(truncate64) = A_FILENAME,
254     S(stat64) = A_FILENAME,
255     S(lstat64) = A_FILENAME,
256 #endif
257
258     // Syscalls permitted always
259     S(exit) = A_YES | A_SAMPLE_MEM,
260     S(read) = A_YES,
261     S(write) = A_YES,
262     S(close) = A_YES,
263     S(lseek) = A_YES,
264     S(getpid) = A_YES,
265     S(getuid) = A_YES,
266     S(dup) = A_YES,
267     S(brk) = A_YES,
268     S(getgid) = A_YES,
269     S(geteuid) = A_YES,
270     S(getegid) = A_YES,
271     S(dup2) = A_YES,
272     S(ftruncate) = A_YES,
273     S(fstat) = A_YES,
274     S(personality) = A_YES,
275     S(readv) = A_YES,
276     S(writev) = A_YES,
277     S(getresuid) = A_YES,
278 #ifdef __NR_pread64
279     S(pread64) = A_YES,
280     S(pwrite64) = A_YES,
281 #else
282     S(pread) = A_YES,
283     S(pwrite) = A_YES,
284 #endif
285     S(fcntl) = A_YES,
286     S(mmap) = A_YES,
287     S(munmap) = A_YES,
288     S(ioctl) = A_YES,
289     S(uname) = A_YES,
290     S(gettid) = A_YES,
291     S(set_thread_area) = A_YES,
292     S(get_thread_area) = A_YES,
293     S(set_tid_address) = A_YES,
294     S(exit_group) = A_YES | A_SAMPLE_MEM,
295 #ifdef CONFIG_BOX_USER_AMD64
296     S(arch_prctl) = A_YES,
297 #else
298     S(oldfstat) = A_YES,
299     S(ftruncate64) = A_YES,
300     S(_llseek) = A_YES,
301     S(fstat64) = A_YES,
302     S(fcntl64) = A_YES,
303     S(mmap2) = A_YES,
304 #endif
305
306     // Syscalls permitted only in liberal mode
307     S(time) = A_YES | A_LIBERAL,
308     S(alarm) = A_YES | A_LIBERAL,
309     S(pause) = A_YES | A_LIBERAL,
310     S(fchmod) = A_YES | A_LIBERAL,
311     S(getrlimit) = A_YES | A_LIBERAL,
312     S(getrusage) = A_YES | A_LIBERAL,
313     S(gettimeofday) = A_YES | A_LIBERAL,
314     S(select) = A_YES | A_LIBERAL,
315     S(setitimer) = A_YES | A_LIBERAL,
316     S(getitimer) = A_YES | A_LIBERAL,
317     S(mprotect) = A_YES | A_LIBERAL,
318     S(getdents) = A_YES | A_LIBERAL,
319     S(getdents64) = A_YES | A_LIBERAL,
320     S(fdatasync) = A_YES | A_LIBERAL,
321     S(mremap) = A_YES | A_LIBERAL,
322     S(poll) = A_YES | A_LIBERAL,
323     S(getcwd) = A_YES | A_LIBERAL,
324     S(nanosleep) = A_YES | A_LIBERAL,
325     S(rt_sigreturn) = A_YES | A_LIBERAL | A_NO_RETVAL,
326     S(rt_sigaction) = A_YES | A_LIBERAL,
327     S(rt_sigprocmask) = A_YES | A_LIBERAL,
328     S(rt_sigpending) = A_YES | A_LIBERAL,
329     S(rt_sigtimedwait) = A_YES | A_LIBERAL,
330     S(rt_sigqueueinfo) = A_YES | A_LIBERAL,
331     S(rt_sigsuspend) = A_YES | A_LIBERAL,
332     S(_sysctl) = A_YES | A_LIBERAL,
333 #ifndef CONFIG_BOX_USER_AMD64
334     S(sigaction) = A_YES | A_LIBERAL,
335     S(sgetmask) = A_YES | A_LIBERAL,
336     S(ssetmask) = A_YES | A_LIBERAL,
337     S(sigsuspend) = A_YES | A_LIBERAL,
338     S(sigpending) = A_YES | A_LIBERAL,
339     S(sigreturn) = A_YES | A_LIBERAL | A_NO_RETVAL,
340     S(sigprocmask) = A_YES | A_LIBERAL,
341     S(ugetrlimit) = A_YES | A_LIBERAL,
342     S(readdir) = A_YES | A_LIBERAL,
343     S(signal) = A_YES | A_LIBERAL,
344     S(_newselect) = A_YES | A_LIBERAL,
345 #endif
346
347 #undef S
348 };
349
350 static const char *
351 syscall_name(unsigned int id, char *buf)
352 {
353   if (id < NUM_SYSCALLS && syscall_names[id])
354     return syscall_names[id];
355   else
356     {
357       sprintf(buf, "#%d", id);
358       return buf;
359     }
360 }
361
362 static int
363 syscall_by_name(char *name)
364 {
365   for (unsigned int i=0; i<NUM_SYSCALLS; i++)
366     if (syscall_names[i] && !strcmp(syscall_names[i], name))
367       return i;
368   if (name[0] == '#')
369     name++;
370   if (!*name)
371     return -1;
372   char *ep;
373   unsigned long l = strtoul(name, &ep, 0);
374   if (*ep)
375     return -1;
376   if (l >= NUM_ACTIONS)
377     return NUM_ACTIONS;
378   return l;
379 }
380
381 static int
382 set_syscall_action(char *a)
383 {
384   char *sep = strchr(a, '=');
385   enum action act = A_YES;
386   if (sep)
387     {
388       *sep++ = 0;
389       if (!strcmp(sep, "yes"))
390         act = A_YES;
391       else if (!strcmp(sep, "no"))
392         act = A_NO;
393       else if (!strcmp(sep, "file"))
394         act = A_FILENAME;
395       else
396         return 0;
397     }
398
399   int sys = syscall_by_name(a);
400   if (sys < 0)
401     die("Unknown syscall `%s'", a);
402   if (sys >= NUM_ACTIONS)
403     die("Syscall `%s' out of range", a);
404   syscall_action[sys] = act;
405   return 1;
406 }
407
408 /*** Path rules ***/
409
410 struct path_rule {
411   char *path;
412   enum action action;
413   struct path_rule *next;
414 };
415
416 static struct path_rule default_path_rules[] = {
417   { "/etc/", A_YES },
418   { "/lib/", A_YES },
419   { "/usr/lib/", A_YES },
420   { "/opt/lib/", A_YES },
421   { "/usr/share/zoneinfo/", A_YES },
422   { "/usr/share/locale/", A_YES },
423   { "/dev/null", A_YES },
424   { "/dev/zero", A_YES },
425   { "/proc/meminfo", A_YES },
426   { "/proc/self/stat", A_YES },
427   { "/proc/self/exe", A_YES },                  // Needed by FPC 2.0.x runtime
428 };
429
430 static struct path_rule *user_path_rules;
431 static struct path_rule **last_path_rule = &user_path_rules;
432
433 static int
434 set_path_action(char *a)
435 {
436   char *sep = strchr(a, '=');
437   enum action act = A_YES;
438   if (sep)
439     {
440       *sep++ = 0;
441       if (!strcmp(sep, "yes"))
442         act = A_YES;
443       else if (!strcmp(sep, "no"))
444         act = A_NO;
445       else
446         return 0;
447     }
448
449   struct path_rule *r = xmalloc(sizeof(*r) + strlen(a) + 1);
450   r->path = (char *)(r+1);
451   strcpy(r->path, a);
452   r->action = act;
453   r->next = NULL;
454   *last_path_rule = r;
455   last_path_rule = &r->next;
456   return 1;
457 }
458
459 static enum action
460 match_path_rule(struct path_rule *r, char *path)
461 {
462   char *rr = r->path;
463   while (*rr)
464     if (*rr++ != *path++)
465       {
466         if (rr[-1] == '/' && !path[-1])
467           break;
468         return A_DEFAULT;
469       }
470   if (rr > r->path && rr[-1] != '/' && *path)
471     return A_DEFAULT;
472   return r->action;
473 }
474
475 /*** Environment rules ***/
476
477 struct env_rule {
478   char *var;                    // Variable to match
479   char *val;                    // ""=clear, NULL=inherit
480   int var_len;
481   struct env_rule *next;
482 };
483
484 static struct env_rule *first_env_rule;
485 static struct env_rule **last_env_rule = &first_env_rule;
486
487 static struct env_rule default_env_rules[] = {
488   { "LIBC_FATAL_STDERR_", "1" }
489 };
490
491 static int
492 set_env_action(char *a0)
493 {
494   struct env_rule *r = xmalloc(sizeof(*r) + strlen(a0) + 1);
495   char *a = (char *)(r+1);
496   strcpy(a, a0);
497
498   char *sep = strchr(a, '=');
499   if (sep == a)
500     return 0;
501   r->var = a;
502   if (sep)
503     {
504       *sep++ = 0;
505       r->val = sep;
506     }
507   else
508     r->val = NULL;
509   *last_env_rule = r;
510   last_env_rule = &r->next;
511   r->next = NULL;
512   return 1;
513 }
514
515 static int
516 match_env_var(char *env_entry, struct env_rule *r)
517 {
518   if (strncmp(env_entry, r->var, r->var_len))
519     return 0;
520   return (env_entry[r->var_len] == '=');
521 }
522
523 static void
524 apply_env_rule(char **env, int *env_sizep, struct env_rule *r)
525 {
526   // First remove the variable if already set
527   int pos = 0;
528   while (pos < *env_sizep && !match_env_var(env[pos], r))
529     pos++;
530   if (pos < *env_sizep)
531     {
532       (*env_sizep)--;
533       env[pos] = env[*env_sizep];
534       env[*env_sizep] = NULL;
535     }
536
537   // What is the new value?
538   char *new;
539   if (r->val)
540     {
541       if (!r->val[0])
542         return;
543       new = xmalloc(r->var_len + 1 + strlen(r->val) + 1);
544       sprintf(new, "%s=%s", r->var, r->val);
545     }
546   else
547     {
548       pos = 0;
549       while (environ[pos] && !match_env_var(environ[pos], r))
550         pos++;
551       if (!(new = environ[pos]))
552         return;
553     }
554
555   // Add it at the end of the array
556   env[(*env_sizep)++] = new;
557   env[*env_sizep] = NULL;
558 }
559
560 static char **
561 setup_environment(void)
562 {
563   // Link built-in rules with user rules
564   for (int i=ARRAY_SIZE(default_env_rules)-1; i >= 0; i--)
565     {
566       default_env_rules[i].next = first_env_rule;
567       first_env_rule = &default_env_rules[i];
568     }
569
570   // Scan the original environment
571   char **orig_env = environ;
572   int orig_size = 0;
573   while (orig_env[orig_size])
574     orig_size++;
575
576   // For each rule, reserve one more slot and calculate length
577   int num_rules = 0;
578   for (struct env_rule *r = first_env_rule; r; r=r->next)
579     {
580       num_rules++;
581       r->var_len = strlen(r->var);
582     }
583
584   // Create a new environment
585   char **env = xmalloc((orig_size + num_rules + 1) * sizeof(char *));
586   int size;
587   if (pass_environ)
588     {
589       memcpy(env, environ, orig_size * sizeof(char *));
590       size = orig_size;
591     }
592   else
593     size = 0;
594   env[size] = NULL;
595
596   // Apply the rules one by one
597   for (struct env_rule *r = first_env_rule; r; r=r->next)
598     apply_env_rule(env, &size, r);
599
600   // Return the new env and pass some gossip
601   if (verbose > 1)
602     {
603       fprintf(stderr, "Passing environment:\n");
604       for (int i=0; env[i]; i++)
605         fprintf(stderr, "\t%s\n", env[i]);
606     }
607   return env;
608 }
609
610 /*** Low-level parsing of syscalls ***/
611
612 #ifdef CONFIG_BOX_KERNEL_AMD64
613 typedef uint64_t arg_t;
614 #else
615 typedef uint32_t arg_t;
616 #endif
617
618 struct syscall_args {
619   arg_t sys;
620   arg_t arg1, arg2, arg3;
621   arg_t result;
622   struct user user;
623 };
624
625 static int read_user_mem(arg_t addr, char *buf, int len)
626 {
627   static int mem_fd;
628
629   if (!mem_fd)
630     {
631       char memname[64];
632       sprintf(memname, "/proc/%d/mem", (int) box_pid);
633       mem_fd = open(memname, O_RDONLY);
634       if (mem_fd < 0)
635         die("open(%s): %m", memname);
636     }
637   if (lseek64(mem_fd, addr, SEEK_SET) < 0)
638     die("lseek64(mem): %m");
639   return read(mem_fd, buf, len);
640 }
641
642 #ifdef CONFIG_BOX_KERNEL_AMD64
643
644 static void
645 get_syscall_args(struct syscall_args *a, int is_exit)
646 {
647   if (ptrace(PTRACE_GETREGS, box_pid, NULL, &a->user) < 0)
648     die("ptrace(PTRACE_GETREGS): %m");
649   a->sys = a->user.regs.orig_rax;
650   a->result = a->user.regs.rax;
651
652   /*
653    *  CAVEAT: We have to check carefully that this is a real 64-bit syscall.
654    *  We test whether the process runs in 64-bit mode, but surprisingly this
655    *  is not enough: a 64-bit process can still issue the INT 0x80 instruction
656    *  which performs a 32-bit syscall. Currently, the only known way how to
657    *  detect this situation is to inspect the instruction code (the kernel
658    *  keeps a syscall type flag internally, but it is not accessible from
659    *  user space). Hopefully, there is no instruction whose suffix is the
660    *  code of the SYSCALL instruction. Sometimes, one would wish the
661    *  instruction codes to be unique even when read backwards :)
662    */
663
664   if (is_exit)
665     return;
666
667   int sys_type;
668   uint16_t instr;
669
670   switch (a->user.regs.cs)
671     {
672     case 0x23:
673       // 32-bit CPU mode => only 32-bit syscalls can be issued
674       sys_type = 32;
675       break;
676     case 0x33:
677       // 64-bit CPU mode
678       if (read_user_mem(a->user.regs.rip-2, (char *) &instr, 2) != 2)
679         err("FO: Cannot read syscall instruction");
680       switch (instr)
681         {
682         case 0x050f:
683           break;
684         case 0x80cd:
685           err("FO: Forbidden 32-bit syscall in 64-bit mode");
686         default:
687           err("XX: Unknown syscall instruction %04x", instr);
688         }
689       sys_type = 64;
690       break;
691     default:
692       err("XX: Unknown code segment %04jx", (intmax_t) a->user.regs.cs);
693     }
694
695 #ifdef CONFIG_BOX_USER_AMD64
696   if (sys_type != 64)
697     err("FO: Forbidden %d-bit mode syscall", sys_type);
698 #else
699   if (sys_type != (exec_seen ? 32 : 64))
700     err("FO: Forbidden %d-bit mode syscall", sys_type);
701 #endif
702
703   if (sys_type == 32)
704     {
705       a->arg1 = a->user.regs.rbx;
706       a->arg2 = a->user.regs.rcx;
707       a->arg3 = a->user.regs.rdx;
708     }
709   else
710     {
711       a->arg1 = a->user.regs.rdi;
712       a->arg2 = a->user.regs.rsi;
713       a->arg3 = a->user.regs.rdx;
714     }
715 }
716
717 static void
718 set_syscall_nr(struct syscall_args *a, arg_t sys)
719 {
720   a->sys = sys;
721   a->user.regs.orig_rax = sys;
722   if (ptrace(PTRACE_SETREGS, box_pid, NULL, &a->user) < 0)
723     die("ptrace(PTRACE_SETREGS): %m");
724 }
725
726 static void
727 sanity_check(void)
728 {
729 }
730
731 #else
732
733 static void
734 get_syscall_args(struct syscall_args *a, int is_exit UNUSED)
735 {
736   if (ptrace(PTRACE_GETREGS, box_pid, NULL, &a->user) < 0)
737     die("ptrace(PTRACE_GETREGS): %m");
738   a->sys = a->user.regs.orig_eax;
739   a->arg1 = a->user.regs.ebx;
740   a->arg2 = a->user.regs.ecx;
741   a->arg3 = a->user.regs.edx;
742   a->result = a->user.regs.eax;
743 }
744
745 static void
746 set_syscall_nr(struct syscall_args *a, arg_t sys)
747 {
748   a->sys = sys;
749   a->user.regs.orig_eax = sys;
750   if (ptrace(PTRACE_SETREGS, box_pid, NULL, &a->user) < 0)
751     die("ptrace(PTRACE_SETREGS): %m");
752 }
753
754 static void
755 sanity_check(void)
756 {
757 #if !defined(CONFIG_BOX_ALLOW_INSECURE)
758   struct utsname uts;
759   if (uname(&uts) < 0)
760     die("uname() failed: %m");
761
762   if (!strcmp(uts.machine, "x86_64"))
763     die("Running 32-bit sandbox on 64-bit kernels is inherently unsafe. Please get a 64-bit version.");
764 #endif
765 }
766
767 #endif
768
769 /*** Syscall checks ***/
770
771 static void
772 valid_filename(arg_t addr)
773 {
774   char namebuf[4096], *p, *end;
775
776   if (!file_access)
777     err("FA: File access forbidden");
778   if (file_access >= 9)
779     return;
780
781   p = end = namebuf;
782   do
783     {
784       if (p >= end)
785         {
786           int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1));
787           int l = namebuf + sizeof(namebuf) - end;
788           if (l > remains)
789             l = remains;
790           if (!l)
791             err("FA: Access to file with name too long");
792           remains = read_user_mem(addr, end, l);
793           if (remains < 0)
794             die("read(mem): %m");
795           if (!remains)
796             err("FA: Access to file with name out of memory");
797           end += remains;
798           addr += remains;
799         }
800     }
801   while (*p++);
802
803   msg("[%s] ", namebuf);
804   if (file_access >= 3)
805     return;
806
807   // Everything in current directory is permitted
808   if (!strchr(namebuf, '/') && strcmp(namebuf, ".."))
809     return;
810
811   // ".." anywhere in the path is forbidden
812   enum action act = A_DEFAULT;
813   if (strstr(namebuf, ".."))
814     act = A_NO;
815
816   // Scan user rules
817   for (struct path_rule *r = user_path_rules; r && !act; r=r->next)
818     act = match_path_rule(r, namebuf);
819
820   // Scan built-in rules
821   if (file_access >= 2)
822     for (int i=0; i<ARRAY_SIZE(default_path_rules) && !act; i++)
823       act = match_path_rule(&default_path_rules[i], namebuf);
824
825   if (act != A_YES)
826     err("FA: Forbidden access to file `%s'", namebuf);
827 }
828
829 // Check syscall. If invalid, return -1, otherwise return the action mask.
830 static int
831 valid_syscall(struct syscall_args *a)
832 {
833   unsigned int sys = a->sys;
834   unsigned int act = (sys < NUM_ACTIONS) ? syscall_action[sys] : A_DEFAULT;
835
836   if (act & A_LIBERAL)
837     {
838       if (filter_syscalls != 1)
839         act = A_DEFAULT;
840     }
841
842   switch (act & A_ACTION_MASK)
843     {
844     case A_YES:
845       return act;
846     case A_NO:
847       return -1;
848     case A_FILENAME:
849       valid_filename(a->arg1);
850       return act;
851     default: ;
852     }
853
854   switch (sys)
855     {
856     case __NR_kill:
857       if (a->arg1 == (arg_t) box_pid)
858         {
859           meta_printf("exitsig:%d\n", (int) a->arg2);
860           err("SG: Committed suicide by signal %d", (int) a->arg2);
861         }
862       return -1;
863     case __NR_tgkill:
864       if (a->arg1 == (arg_t) box_pid && a->arg2 == (arg_t) box_pid)
865         {
866           meta_printf("exitsig:%d\n", (int) a->arg3);
867           err("SG: Committed suicide by signal %d", (int) a->arg3);
868         }
869       return -1;
870     default:
871       return -1;
872     }
873 }
874
875 static void
876 signal_alarm(int unused UNUSED)
877 {
878   /* Time limit checks are synchronous, so we only schedule them there. */
879   timer_tick = 1;
880   alarm(1);
881 }
882
883 static void
884 signal_int(int unused UNUSED)
885 {
886   /* Interrupts are fatal, so no synchronization requirements. */
887   meta_printf("exitsig:%d\n", SIGINT);
888   err("SG: Interrupted");
889 }
890
891 #define PROC_BUF_SIZE 4096
892 static void
893 read_proc_file(char *buf, char *name, int *fdp)
894 {
895   int c;
896
897   if (!*fdp)
898     {
899       sprintf(buf, "/proc/%d/%s", (int) box_pid, name);
900       *fdp = open(buf, O_RDONLY);
901       if (*fdp < 0)
902         die("open(%s): %m", buf);
903     }
904   lseek(*fdp, 0, SEEK_SET);
905   if ((c = read(*fdp, buf, PROC_BUF_SIZE-1)) < 0)
906     die("read on /proc/$pid/%s: %m", name);
907   if (c >= PROC_BUF_SIZE-1)
908     die("/proc/$pid/%s too long", name);
909   buf[c] = 0;
910 }
911
912 static void
913 check_timeout(void)
914 {
915   if (wall_timeout)
916     {
917       struct timeval now, wall;
918       int wall_ms;
919       gettimeofday(&now, NULL);
920       timersub(&now, &start_time, &wall);
921       wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
922       if (wall_ms > wall_timeout)
923         err("TO: Time limit exceeded (wall clock)");
924       if (verbose > 1)
925         fprintf(stderr, "[wall time check: %d msec]\n", wall_ms);
926     }
927   if (timeout)
928     {
929       char buf[PROC_BUF_SIZE], *x;
930       int utime, stime, ms;
931       static int proc_stat_fd;
932       read_proc_file(buf, "stat", &proc_stat_fd);
933       x = buf;
934       while (*x && *x != ' ')
935         x++;
936       while (*x == ' ')
937         x++;
938       if (*x++ != '(')
939         die("proc stat syntax error 1");
940       while (*x && (*x != ')' || x[1] != ' '))
941         x++;
942       while (*x == ')' || *x == ' ')
943         x++;
944       if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
945         die("proc stat syntax error 2");
946       ms = (utime + stime) * 1000 / ticks_per_sec;
947       if (verbose > 1)
948         fprintf(stderr, "[time check: %d msec]\n", ms);
949       if (ms > timeout && ms > extra_timeout)
950         err("TO: Time limit exceeded");
951     }
952 }
953
954 static void
955 sample_mem_peak(void)
956 {
957   /*
958    *  We want to find out the peak memory usage of the process, which is
959    *  maintained by the kernel, but unforunately it gets lost when the
960    *  process exits (it is not reported in struct rusage). Therefore we
961    *  have to sample it whenever we suspect that the process is about
962    *  to exit.
963    */
964   char buf[PROC_BUF_SIZE], *x;
965   static int proc_status_fd;
966   read_proc_file(buf, "status", &proc_status_fd);
967
968   x = buf;
969   while (*x)
970     {
971       char *key = x;
972       while (*x && *x != ':' && *x != '\n')
973         x++;
974       if (!*x || *x == '\n')
975         break;
976       *x++ = 0;
977       while (*x == ' ' || *x == '\t')
978         x++;
979
980       char *val = x;
981       while (*x && *x != '\n')
982         x++;
983       if (!*x)
984         break;
985       *x++ = 0;
986
987       if (!strcmp(key, "VmPeak"))
988         {
989           int peak = atoi(val);
990           if (peak > mem_peak_kb)
991             mem_peak_kb = peak;
992         }
993     }
994
995   if (verbose > 1)
996     msg("[mem-peak: %u KB]\n", mem_peak_kb);
997 }
998
999 static void
1000 boxkeeper(void)
1001 {
1002   int syscall_count = (filter_syscalls ? 0 : 1);
1003   struct sigaction sa;
1004
1005   is_ptraced = 1;
1006
1007   bzero(&sa, sizeof(sa));
1008   sa.sa_handler = signal_int;
1009   sigaction(SIGINT, &sa, NULL);
1010
1011   gettimeofday(&start_time, NULL);
1012   ticks_per_sec = sysconf(_SC_CLK_TCK);
1013   if (ticks_per_sec <= 0)
1014     die("Invalid ticks_per_sec!");
1015
1016   if (timeout || wall_timeout)
1017     {
1018       sa.sa_handler = signal_alarm;
1019       sigaction(SIGALRM, &sa, NULL);
1020       alarm(1);
1021     }
1022
1023   for(;;)
1024     {
1025       struct rusage rus;
1026       int stat;
1027       pid_t p;
1028       if (timer_tick)
1029         {
1030           check_timeout();
1031           timer_tick = 0;
1032         }
1033       p = wait4(box_pid, &stat, WUNTRACED, &rus);
1034       if (p < 0)
1035         {
1036           if (errno == EINTR)
1037             continue;
1038           die("wait4: %m");
1039         }
1040       if (p != box_pid)
1041         die("wait4: unknown pid %d exited!", p);
1042       if (WIFEXITED(stat))
1043         {
1044           box_pid = 0;
1045           final_stats(&rus);
1046           if (WEXITSTATUS(stat))
1047             {
1048               if (syscall_count)
1049                 {
1050                   meta_printf("exitcode:%d\n", WEXITSTATUS(stat));
1051                   err("RE: Exited with error status %d", WEXITSTATUS(stat));
1052                 }
1053               else
1054                 {
1055                   // Internal error happened inside the child process and it has been already reported.
1056                   box_exit(2);
1057                 }
1058             }
1059           if (timeout && total_ms > timeout)
1060             err("TO: Time limit exceeded");
1061           if (wall_timeout && wall_ms > wall_timeout)
1062             err("TO: Time limit exceeded (wall clock)");
1063           flush_line();
1064           fprintf(stderr, "OK (%d.%03d sec real, %d.%03d sec wall, %d MB, %d syscalls)\n",
1065               total_ms/1000, total_ms%1000,
1066               wall_ms/1000, wall_ms%1000,
1067               (mem_peak_kb + 1023) / 1024,
1068               syscall_count);
1069           box_exit(0);
1070         }
1071       if (WIFSIGNALED(stat))
1072         {
1073           box_pid = 0;
1074           meta_printf("exitsig:%d\n", WTERMSIG(stat));
1075           final_stats(&rus);
1076           err("SG: Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
1077         }
1078       if (WIFSTOPPED(stat))
1079         {
1080           int sig = WSTOPSIG(stat);
1081           if (sig == SIGTRAP)
1082             {
1083               if (verbose > 2)
1084                 msg("[ptrace status %08x] ", stat);
1085               static int stop_count;
1086               if (!stop_count++)                /* Traceme request */
1087                 msg(">> Traceme request caught\n");
1088               else
1089                 err("SG: Breakpoint");
1090               ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1091             }
1092           else if (sig == (SIGTRAP | 0x80))
1093             {
1094               if (verbose > 2)
1095                 msg("[ptrace status %08x] ", stat);
1096               struct syscall_args a;
1097               static unsigned int sys_tick, last_act;
1098               static arg_t last_sys;
1099               if (++sys_tick & 1)               /* Syscall entry */
1100                 {
1101                   char namebuf[32];
1102                   int act;
1103
1104                   get_syscall_args(&a, 0);
1105                   arg_t sys = a.sys;
1106                   msg(">> Syscall %-12s (%08jx,%08jx,%08jx) ", syscall_name(sys, namebuf), (intmax_t) a.arg1, (intmax_t) a.arg2, (intmax_t) a.arg3);
1107                   if (!exec_seen)
1108                     {
1109                       msg("[master] ");
1110                       if (sys == NATIVE_NR_execve)
1111                         exec_seen = 1;
1112                     }
1113                   else if ((act = valid_syscall(&a)) >= 0)
1114                     {
1115                       last_act = act;
1116                       syscall_count++;
1117                       if (act & A_SAMPLE_MEM)
1118                         sample_mem_peak();
1119                     }
1120                   else
1121                     {
1122                       /*
1123                        * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
1124                        * so we have to change it to something harmless (e.g., an undefined
1125                        * syscall) and make the program continue.
1126                        */
1127                       set_syscall_nr(&a, ~(arg_t)0);
1128                       err("FO: Forbidden syscall %s", syscall_name(sys, namebuf));
1129                     }
1130                   last_sys = sys;
1131                 }
1132               else                                      /* Syscall return */
1133                 {
1134                   get_syscall_args(&a, 1);
1135                   if (a.sys == ~(arg_t)0)
1136                     {
1137                       /* Some syscalls (sigreturn et al.) do not return a value */
1138                       if (!(last_act & A_NO_RETVAL))
1139                         err("XX: Syscall does not return, but it should");
1140                     }
1141                   else
1142                     {
1143                       if (a.sys != last_sys)
1144                         err("XX: Mismatched syscall entry/exit");
1145                     }
1146                   if (last_act & A_NO_RETVAL)
1147                     msg("= ?\n");
1148                   else
1149                     msg("= %jd\n", (intmax_t) a.result);
1150                 }
1151               ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1152             }
1153           else if (sig == SIGSTOP)
1154             {
1155               msg(">> SIGSTOP\n");
1156               if (ptrace(PTRACE_SETOPTIONS, box_pid, NULL, (void *) PTRACE_O_TRACESYSGOOD) < 0)
1157                 die("ptrace(PTRACE_SETOPTIONS): %m");
1158               ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1159             }
1160           else if (sig != SIGXCPU && sig != SIGXFSZ)
1161             {
1162               msg(">> Signal %d\n", sig);
1163               sample_mem_peak();                        /* Signal might be fatal, so update mem-peak */
1164               ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
1165             }
1166           else
1167             {
1168               meta_printf("exitsig:%d", sig);
1169               err("SG: Received signal %d", sig);
1170             }
1171         }
1172       else
1173         die("wait4: unknown status %x, giving up!", stat);
1174     }
1175 }
1176
1177 static void
1178 box_inside(int argc, char **argv)
1179 {
1180   struct rlimit rl;
1181   char *args[argc+1];
1182
1183   memcpy(args, argv, argc * sizeof(char *));
1184   args[argc] = NULL;
1185   if (set_cwd && chdir(set_cwd))
1186     die("chdir: %m");
1187   if (redir_stdin)
1188     {
1189       close(0);
1190       if (open(redir_stdin, O_RDONLY) != 0)
1191         die("open(\"%s\"): %m", redir_stdin);
1192     }
1193   if (redir_stdout)
1194     {
1195       close(1);
1196       if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
1197         die("open(\"%s\"): %m", redir_stdout);
1198     }
1199   if (redir_stderr)
1200     {
1201       close(2);
1202       if (open(redir_stderr, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 2)
1203         die("open(\"%s\"): %m", redir_stderr);
1204     }
1205   else
1206     dup2(1, 2);
1207   setpgrp();
1208
1209   if (memory_limit)
1210     {
1211       rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
1212       if (setrlimit(RLIMIT_AS, &rl) < 0)
1213         die("setrlimit(RLIMIT_AS): %m");
1214     }
1215
1216   rl.rlim_cur = rl.rlim_max = (stack_limit ? (rlim_t)stack_limit * 1024 : RLIM_INFINITY);
1217   if (setrlimit(RLIMIT_STACK, &rl) < 0)
1218     die("setrlimit(RLIMIT_STACK): %m");
1219
1220   rl.rlim_cur = rl.rlim_max = 64;
1221   if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
1222     die("setrlimit(RLIMIT_NOFILE): %m");
1223
1224   char **env = setup_environment();
1225   if (filter_syscalls)
1226     {
1227       if (ptrace(PTRACE_TRACEME) < 0)
1228         die("ptrace(PTRACE_TRACEME): %m");
1229       /* Trick: Make sure that we are stopped until the boxkeeper wakes up. */
1230       raise(SIGSTOP);
1231     }
1232   execve(args[0], args, env);
1233   die("execve(\"%s\"): %m", args[0]);
1234 }
1235
1236 static void
1237 usage(void)
1238 {
1239   fprintf(stderr, "Invalid arguments!\n");
1240   printf("\
1241 Usage: box [<options>] -- <command> <arguments>\n\
1242 \n\
1243 Options:\n\
1244 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
1245 -c <dir>\tChange directory to <dir> first\n\
1246 -e\t\tInherit full environment of the parent process\n\
1247 -E <var>\tInherit the environment variable <var> from the parent process\n\
1248 -E <var>=<val>\tSet the environment variable <var> to <val>; unset it if <var> is empty\n\
1249 -f\t\tFilter system calls (-ff=very restricted)\n\
1250 -i <file>\tRedirect stdin from <file>\n\
1251 -k <size>\tLimit stack size to <size> KB (default: 0=unlimited)\n\
1252 -m <size>\tLimit address space to <size> KB\n\
1253 -M <file>\tOutput process information to <file> (name:value)\n\
1254 -o <file>\tRedirect stdout to <file>\n\
1255 -p <path>\tPermit access to the specified path (or subtree if it ends with a `/')\n\
1256 -p <path>=<act>\tDefine action for the specified path (<act>=yes/no)\n\
1257 -r <file>\tRedirect stderr to <file>\n\
1258 -s <sys>\tPermit the specified syscall (be careful)\n\
1259 -s <sys>=<act>\tDefine action for the specified syscall (<act>=yes/no/file)\n\
1260 -t <time>\tSet run time limit (seconds, fractions allowed)\n\
1261 -T\t\tAllow syscalls for measuring run time\n\
1262 -v\t\tBe verbose (use multiple times for even more verbosity)\n\
1263 -w <time>\tSet wall clock time limit (seconds, fractions allowed)\n\
1264 -x <time>\tSet extra timeout, before which a timing-out program is not yet killed,\n\
1265 \t\tso that its real execution time is reported (seconds, fractions allowed)\n\
1266 ");
1267   exit(2);
1268 }
1269
1270 int
1271 main(int argc, char **argv)
1272 {
1273   int c;
1274   uid_t uid;
1275
1276   while ((c = getopt(argc, argv, "a:c:eE:fi:k:m:M:o:p:r:s:t:Tvw:x:")) >= 0)
1277     switch (c)
1278       {
1279       case 'a':
1280         file_access = atol(optarg);
1281         break;
1282       case 'c':
1283         set_cwd = optarg;
1284         break;
1285       case 'e':
1286         pass_environ = 1;
1287         break;
1288       case 'E':
1289         if (!set_env_action(optarg))
1290           usage();
1291         break;
1292       case 'f':
1293         filter_syscalls++;
1294         break;
1295       case 'k':
1296         stack_limit = atol(optarg);
1297         break;
1298       case 'i':
1299         redir_stdin = optarg;
1300         break;
1301       case 'm':
1302         memory_limit = atol(optarg);
1303         break;
1304       case 'M':
1305         meta_open(optarg);
1306         break;
1307       case 'o':
1308         redir_stdout = optarg;
1309         break;
1310       case 'p':
1311         if (!set_path_action(optarg))
1312           usage();
1313         break;
1314       case 'r':
1315         redir_stderr = optarg;
1316         break;
1317       case 's':
1318         if (!set_syscall_action(optarg))
1319           usage();
1320         break;
1321       case 't':
1322         timeout = 1000*atof(optarg);
1323         break;
1324       case 'T':
1325         syscall_action[__NR_times] = A_YES;
1326         break;
1327       case 'v':
1328         verbose++;
1329         break;
1330       case 'w':
1331         wall_timeout = 1000*atof(optarg);
1332         break;
1333       case 'x':
1334         extra_timeout = 1000*atof(optarg);
1335         break;
1336       default:
1337         usage();
1338       }
1339   if (optind >= argc)
1340     usage();
1341
1342   sanity_check();
1343   uid = geteuid();
1344   if (setreuid(uid, uid) < 0)
1345     die("setreuid: %m");
1346   box_pid = fork();
1347   if (box_pid < 0)
1348     die("fork: %m");
1349   if (!box_pid)
1350     box_inside(argc-optind, argv+optind);
1351   else
1352     boxkeeper();
1353   die("Internal error: fell over edge of the world");
1354 }