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