Description:
added box for 64bit
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r86:17b9b508c897 - - 1 file changed: 666 inserted, 0 deleted

This diff has been collapsed as it changes many lines, (666 lines changed) Show them Hide them
@@ -0,0 +1,666
1 + /*
2 + * A Simple Testing Sandbox
3 + *
4 + * (c) 2001--2004 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 <unistd.h>
17 + #include <getopt.h>
18 + #include <time.h>
19 + #include <sys/wait.h>
20 + #include <sys/user.h>
21 + #include <sys/time.h>
22 + #include <sys/ptrace.h>
23 + #include <sys/signal.h>
24 + #include <sys/sysinfo.h>
25 + #include <sys/syscall.h>
26 + #include <sys/resource.h>
27 +
28 + #define NONRET __attribute__((noreturn))
29 + #define UNUSED __attribute__((unused))
30 +
31 + static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
32 + static double timeout;
33 + static int pass_environ;
34 + static int use_wall_clock;
35 + static int file_access;
36 + static int verbose;
37 + static int memory_limit;
38 + static int allow_times;
39 + static char *redir_stdin, *redir_stdout;
40 + static char *set_cwd;
41 +
42 + static pid_t box_pid;
43 + static int is_ptraced;
44 + static volatile int timer_tick;
45 + static time_t start_time;
46 + static int ticks_per_sec;
47 + static int page_size;
48 +
49 + #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ > 0
50 + /* glibc 2.1 or newer -> has lseek64 */
51 + #define long_seek(f,o,w) lseek64(f,o,w)
52 + #else
53 + /* Touching clandestine places in glibc */
54 + extern loff_t llseek(int fd, loff_t pos, int whence);
55 + #define long_seek(f,o,w) llseek(f,o,w)
56 + #endif
57 +
58 + int max_mem_used = 0;
59 +
60 + void print_running_stat(double wall_time,
61 + double user_time,
62 + double system_time,
63 + int mem_usage)
64 + {
65 + fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
66 + wall_time, user_time, system_time, mem_usage);
67 + }
68 +
69 + static void NONRET
70 + box_exit(void)
71 + {
72 + if (box_pid > 0) {
73 + if (is_ptraced)
74 + ptrace(PTRACE_KILL, box_pid);
75 + kill(-box_pid, SIGKILL);
76 + kill(box_pid, SIGKILL);
77 + }
78 +
79 + struct timeval total;
80 + int wall;
81 + struct rusage rus;
82 + int stat;
83 + pid_t p;
84 +
85 + // wait so that we can get information
86 + p = wait4(box_pid, &stat, WUNTRACED, &rus);
87 + if (p < 0) {
88 + fprintf(stderr,"wait4: error\n");
89 + print_running_stat(0,0,0,max_mem_used);
90 + } else if (p != box_pid) {
91 + fprintf(stderr,"wait4: unknown pid %d exited!\n", p);
92 + print_running_stat(0,0,0,max_mem_used);
93 + } else {
94 + if (!WIFEXITED(stat))
95 + fprintf(stderr,"wait4: unknown status\n");
96 + struct timeval total;
97 + int wall;
98 + wall = time(NULL) - start_time;
99 + timeradd(&rus.ru_utime, &rus.ru_stime, &total);
100 +
101 + print_running_stat((double)wall,
102 + (double) rus.ru_utime.tv_sec +
103 + ((double) rus.ru_utime.tv_usec/1000000.0),
104 + (double) rus.ru_stime.tv_sec +
105 + ((double) rus.ru_stime.tv_usec/1000000.0),
106 + max_mem_used);
107 + }
108 + exit(1);
109 + }
110 +
111 + static void NONRET __attribute__((format(printf,1,2)))
112 + die(char *msg, ...)
113 + {
114 + va_list args;
115 + va_start(args, msg);
116 + vfprintf(stderr, msg, args);
117 + fputc('\n', stderr);
118 + box_exit();
119 + }
120 +
121 + static void __attribute__((format(printf,1,2)))
122 + log(char *msg, ...)
123 + {
124 + va_list args;
125 + va_start(args, msg);
126 + if (verbose)
127 + {
128 + vfprintf(stderr, msg, args);
129 + fflush(stderr);
130 + }
131 + va_end(args);
132 + }
133 +
134 + static void
135 + valid_filename(unsigned long addr)
136 + {
137 + char namebuf[4096], *p, *end;
138 + static int mem_fd;
139 +
140 + if (!file_access)
141 + die("File access forbidden.");
142 + if (file_access >= 9)
143 + return;
144 +
145 + if (!mem_fd)
146 + {
147 + sprintf(namebuf, "/proc/%d/mem", (int) box_pid);
148 + mem_fd = open(namebuf, O_RDONLY);
149 + if (mem_fd < 0)
150 + die("open(%s): %m", namebuf);
151 + }
152 + p = end = namebuf;
153 + do
154 + {
155 + if (p >= end)
156 + {
157 + int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1));
158 + int l = namebuf + sizeof(namebuf) - end;
159 + if (l > remains)
160 + l = remains;
161 + if (!l)
162 + die("Access to file with name too long.");
163 + if (long_seek(mem_fd, addr, SEEK_SET) < 0)
164 + die("long_seek(mem): %m");
165 + remains = read(mem_fd, end, l);
166 + if (remains < 0)
167 + die("read(mem): %m");
168 + if (!remains)
169 + die("Access to file with name out of memory.");
170 + end += l;
171 + addr += l;
172 + }
173 + }
174 + while (*p++);
175 +
176 + log("[%s] ", namebuf);
177 + if (file_access >= 3)
178 + return;
179 + if (!strchr(namebuf, '/') && strcmp(namebuf, ".."))
180 + return;
181 + if (file_access >= 2)
182 + {
183 + if ((!strncmp(namebuf, "/etc/", 5) ||
184 + !strncmp(namebuf, "/lib/", 5) ||
185 + !strncmp(namebuf, "/usr/lib/", 9))
186 + && !strstr(namebuf, ".."))
187 + return;
188 + if (!strcmp(namebuf, "/dev/null") ||
189 + !strcmp(namebuf, "/dev/zero") ||
190 + !strcmp(namebuf, "/proc/meminfo") ||
191 + !strcmp(namebuf, "/proc/self/stat") ||
192 + !strncmp(namebuf, "/usr/share/zoneinfo/", 20))
193 + return;
194 + }
195 + die("Forbidden access to file `%s'.", namebuf);
196 + }
197 +
198 + static int
199 + valid_syscall(struct user *u)
200 + {
201 + switch (u->regs.orig_rax)
202 + {
203 + case __NR_execve:
204 + {
205 + static int exec_counter;
206 + return !exec_counter++;
207 + }
208 + case __NR_open:
209 + case __NR_creat:
210 + case __NR_unlink:
211 + case __NR_access:
212 + case __NR_truncate:
213 + case __NR_stat:
214 + case __NR_lstat:
215 + valid_filename(u->regs.rbx);
216 + return 1;
217 + case __NR_exit:
218 + case __NR_read:
219 + case __NR_write:
220 + case __NR_close:
221 + case __NR_lseek:
222 + case __NR_getpid:
223 + case __NR_dup:
224 + case __NR_brk:
225 + case __NR_getgid:
226 + case __NR_getuid:
227 + case __NR_geteuid:
228 + case __NR_getegid:
229 + case __NR_dup2:
230 + case __NR_ftruncate:
231 + case __NR_fstat:
232 + case __NR_personality:
233 + case __NR_readv:
234 + case __NR_writev:
235 + case __NR_getresuid:
236 + #ifdef __NR_pread64
237 + case __NR_pread64:
238 + case __NR_pwrite64:
239 + #else
240 + case __NR_pread:
241 + case __NR_pwrite:
242 + #endif
243 + case __NR_fcntl:
244 + case __NR_mmap:
245 + case __NR_munmap:
246 + case __NR_ioctl:
247 + case __NR_uname:
248 + case 252:
249 + case 243:
250 + // added for free pascal
251 + case __NR_readlink:
252 + // added for 64 bits
253 + case __NR_arch_prctl:
254 + case __NR_exit_group:
255 + return 1;
256 + // case __NR_time:
257 + case __NR_alarm:
258 + // case __NR_pause:
259 + case __NR_fchmod:
260 + case __NR_getrlimit:
261 + case __NR_getrusage:
262 + case __NR_gettimeofday:
263 + case __NR_select:
264 + case __NR_setitimer:
265 + case __NR_getitimer:
266 + case __NR_mprotect:
267 + case __NR_getdents:
268 + case __NR_getdents64:
269 + case __NR_fdatasync:
270 + case __NR_mremap:
271 + case __NR_poll:
272 + case __NR_getcwd:
273 + case __NR_nanosleep:
274 + case __NR_rt_sigreturn:
275 + case __NR_rt_sigaction:
276 + case __NR_rt_sigprocmask:
277 + case __NR_rt_sigpending:
278 + case __NR_rt_sigtimedwait:
279 + case __NR_rt_sigqueueinfo:
280 + case __NR_rt_sigsuspend:
281 + case __NR__sysctl:
282 + return (filter_syscalls == 1);
283 + case __NR_times:
284 + case __NR_time:
285 + return allow_times;
286 + case __NR_kill:
287 + if (u->regs.rbx == box_pid)
288 + die("Commited suicide by signal %ld.", (int)u->regs.rcx);
289 + return 0;
290 + default:
291 + return 0;
292 + }
293 + }
294 +
295 + static void
296 + signal_alarm(int unused UNUSED)
297 + {
298 + /* Time limit checks are synchronous, so we only schedule them there. */
299 + timer_tick = 1;
300 +
301 + //NOTE: do not use alarm, changed to setitimer for precision
302 + // alarm(1);
303 + }
304 +
305 + static void
306 + signal_int(int unused UNUSED)
307 + {
308 + /* Interrupts are fatal, so no synchronization requirements. */
309 + die("Interrupted.");
310 + }
311 +
312 + static void
313 + check_timeout(void)
314 + {
315 + double sec;
316 +
317 + if (use_wall_clock)
318 + sec = (double)(time(NULL) - start_time);
319 + else
320 + {
321 + char buf[4096], *x;
322 + int c, utime, stime;
323 + static int proc_status_fd;
324 + if (!proc_status_fd)
325 + {
326 + sprintf(buf, "/proc/%d/stat", (int) box_pid);
327 + proc_status_fd = open(buf, O_RDONLY);
328 + if (proc_status_fd < 0)
329 + die("open(%s): %m", buf);
330 + }
331 + lseek(proc_status_fd, 0, SEEK_SET);
332 + if ((c = read(proc_status_fd, buf, sizeof(buf)-1)) < 0)
333 + die("read on /proc/$pid/stat: %m");
334 + if (c >= (int) sizeof(buf) - 1)
335 + die("/proc/$pid/stat too long");
336 + buf[c] = 0;
337 + x = buf;
338 + while (*x && *x != ' ')
339 + x++;
340 + while (*x == ' ')
341 + x++;
342 + if (*x++ != '(')
343 + die("proc syntax error 1");
344 + while (*x && (*x != ')' || x[1] != ' '))
345 + x++;
346 + while (*x == ')' || *x == ' ')
347 + x++;
348 + if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
349 + die("proc syntax error 2");
350 + //printf("%s - %d\n",x,ticks_per_sec);
351 + sec = ((double)(utime + stime))/(double)ticks_per_sec;
352 + }
353 + if (verbose > 1)
354 + fprintf(stderr, "[timecheck: %d seconds]\n", sec);
355 + if (sec > timeout) {
356 + die("Time limit exceeded.",sec,timeout);
357 + }
358 + }
359 +
360 + static void
361 + check_memory_usage()
362 + {
363 + char proc_fname[100];
364 + sprintf(proc_fname,"/proc/%d/statm",box_pid);
365 + //printf("proc fname: %s\n",proc_fname);
366 + FILE *fp = fopen(proc_fname,"r");
367 + if(fp!=NULL) {
368 + char line[1000];
369 + fgets(line,999,fp);
370 + //printf("%s\n",line);
371 + int m;
372 +
373 + if(sscanf(line,"%d",&m)==1) {
374 + m = (m*page_size+1023)/1024;
375 + if(m>max_mem_used)
376 + max_mem_used = m;
377 + }
378 +
379 + fclose(fp);
380 + }
381 + }
382 +
383 + static void
384 + boxkeeper(void)
385 + {
386 + int syscall_count = 0;
387 + struct sigaction sa;
388 +
389 + is_ptraced = 1;
390 + bzero(&sa, sizeof(sa));
391 + sa.sa_handler = signal_int;
392 + sigaction(SIGINT, &sa, NULL);
393 + start_time = time(NULL);
394 + ticks_per_sec = sysconf(_SC_CLK_TCK);
395 + page_size = getpagesize();
396 + if (ticks_per_sec <= 0)
397 + die("Invalid ticks_per_sec!");
398 +
399 + check_memory_usage();
400 +
401 + sa.sa_handler = signal_alarm;
402 + sigaction(SIGALRM, &sa, NULL);
403 + //alarm(1);
404 +
405 + struct itimerval val;
406 + val.it_interval.tv_sec = 0;
407 + val.it_interval.tv_usec = 50000;
408 + val.it_value.tv_sec = 0;
409 + val.it_value.tv_usec = 50000;
410 + setitimer(ITIMER_REAL,&val,NULL);
411 +
412 + /*
413 + --- add alarm handler no matter what..
414 + if (timeout)
415 + {
416 + sa.sa_handler = signal_alarm;
417 + sigaction(SIGALRM, &sa, NULL);
418 + alarm(1);
419 + }
420 + */
421 +
422 + for(;;)
423 + {
424 + struct rusage rus;
425 + int stat;
426 + pid_t p;
427 +
428 + if (timer_tick)
429 + {
430 + check_timeout();
431 + check_memory_usage();
432 + timer_tick = 0;
433 + }
434 + p = wait4(box_pid, &stat, WUNTRACED, &rus);
435 +
436 + if (p < 0)
437 + {
438 + if (errno == EINTR)
439 + continue;
440 + die("wait4: %m");
441 + }
442 + if (p != box_pid)
443 + die("wait4: unknown pid %d exited!", p);
444 + if (WIFEXITED(stat))
445 + {
446 + struct timeval total;
447 + int wall;
448 + wall = time(NULL) - start_time;
449 + timeradd(&rus.ru_utime, &rus.ru_stime, &total);
450 +
451 + box_pid = 0;
452 + if (WEXITSTATUS(stat))
453 + fprintf(stderr,"Exited with error status %d.\n", WEXITSTATUS(stat));
454 + else if ((use_wall_clock ?
455 + wall :
456 + (double) total.tv_sec +
457 + ((double) total.tv_usec/1000000.0)) > timeout)
458 + fprintf(stderr,"Time limit exceeded.\n");
459 + else
460 + // report OK and statistics
461 + fprintf(stderr,"OK\n");
462 +
463 + print_running_stat((double) wall,
464 + (double) rus.ru_utime.tv_sec +
465 + ((double) rus.ru_utime.tv_usec/1000000.0),
466 + (double) rus.ru_stime.tv_sec +
467 + ((double) rus.ru_stime.tv_usec/1000000.0),
468 + max_mem_used);
469 + /*
470 + (%.4lf sec real (%d), %d sec wall, %d syscalls, %d kb)\n",
471 + (double) total.tv_sec + ((double)total.tv_usec / 1000000.0),
472 + (int) total.tv_usec,
473 + wall,
474 + syscall_count,
475 + max_mem_used);
476 + */
477 + exit(0);
478 + }
479 + if (WIFSIGNALED(stat))
480 + {
481 + box_pid = 0;
482 + fprintf(stderr,"Caught fatal signal %d.\n", WTERMSIG(stat));
483 +
484 + struct timeval total;
485 + int wall;
486 + wall = time(NULL) - start_time;
487 + timeradd(&rus.ru_utime, &rus.ru_stime, &total);
488 + print_running_stat((double) wall,
489 + (double) rus.ru_utime.tv_sec +
490 + ((double) rus.ru_utime.tv_usec/1000000.0),
491 + (double) rus.ru_stime.tv_sec +
492 + ((double) rus.ru_stime.tv_usec/1000000.0),
493 + max_mem_used);
494 + exit(0);
495 + }
496 + if (WIFSTOPPED(stat))
497 + {
498 + int sig = WSTOPSIG(stat);
499 + if (sig == SIGTRAP)
500 + {
501 + struct user u;
502 + static int stop_count = -1;
503 + if (ptrace(PTRACE_GETREGS, box_pid, NULL, &u) < 0)
504 + die("ptrace(PTRACE_GETREGS): %m");
505 + stop_count++;
506 + if (!stop_count) /* Traceme request */
507 + log(">> Traceme request caught\n");
508 + else if (stop_count & 1) /* Syscall entry */
509 + {
510 + //log(">> Syscall %3ld (%08lx,%08lx,%08lx) ", u.regs.orig_eax, u.regs.ebx, u.regs.ecx, u.regs.edx);
511 + syscall_count++;
512 + if (!valid_syscall(&u))
513 + {
514 + /*
515 + * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
516 + * so we have to change it to something harmless (e.g., an undefined
517 + * syscall) and make the program continue.
518 + */
519 + unsigned long sys = u.regs.orig_rax;
520 + u.regs.orig_rax = 0xffffffff;
521 + if (ptrace(PTRACE_SETREGS, box_pid, NULL, &u) < 0)
522 + die("ptrace(PTRACE_SETREGS): %m");
523 + die("Forbidden syscall %d.", sys);
524 + }
525 + }
526 + else /* Syscall return */
527 + //log("= %ld\n", u.regs.eax);
528 + ;
529 + ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
530 + }
531 + else if (sig != SIGSTOP && sig != SIGXCPU && sig != SIGXFSZ)
532 + {
533 + log(">> Signal %d\n", sig);
534 + ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
535 + }
536 + else
537 + die("Received signal %d.", sig);
538 + }
539 + else
540 + die("wait4: unknown status %x, giving up!", stat);
541 + }
542 + }
543 +
544 + static void
545 + box_inside(int argc, char **argv)
546 + {
547 + struct rlimit rl;
548 + char *args[argc+1];
549 + char *env[1] = { NULL };
550 +
551 + memcpy(args, argv, argc * sizeof(char *));
552 + args[argc] = NULL;
553 + if (set_cwd && chdir(set_cwd))
554 + die("chdir: %m");
555 + if (redir_stdin)
556 + {
557 + close(0);
558 + if (open(redir_stdin, O_RDONLY) != 0)
559 + die("open(\"%s\"): %m", redir_stdin);
560 + }
561 + if (redir_stdout)
562 + {
563 + close(1);
564 + if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
565 + die("open(\"%s\"): %m", redir_stdout);
566 + }
567 + dup2(1, 2);
568 + setpgrp();
569 + if (memory_limit)
570 + {
571 + rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
572 + if (setrlimit(RLIMIT_AS, &rl) < 0)
573 + die("setrlimit: %m");
574 + }
575 + rl.rlim_cur = rl.rlim_max = 64;
576 + if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
577 + die("setrlimit: %m");
578 + if (filter_syscalls && ptrace(PTRACE_TRACEME) < 0)
579 + die("ptrace(PTRACE_TRACEME): %m");
580 + execve(args[0], args, (pass_environ ? environ : env));
581 + die("execve(\"%s\"): %m", args[0]);
582 + }
583 +
584 + static void
585 + usage(void)
586 + {
587 + fprintf(stderr, "Invalid arguments!\n");
588 + printf("\
589 + Usage: box [<options>] -- <command> <arguments>\n\
590 + \n\
591 + Options:\n\
592 + -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
593 + -c <dir>\tChange directory to <dir> first\n\
594 + -e\t\tPass full environment of parent process\n\
595 + -f\t\tFilter system calls (-ff=very restricted)\n\
596 + -i <file>\tRedirect stdin from <file>\n\
597 + -m <size>\tLimit address space to <size> KB\n\
598 + -o <file>\tRedirect stdout to <file>\n\
599 + -t <time>\tStop after <time> seconds\n\
600 + -T\t\tAllow syscalls for measuring run time\n\
601 + -v\t\tBe verbose\n\
602 + -w\t\tMeasure wall clock time instead of run time\n\
603 + ");
604 + exit(1);
605 + }
606 +
607 + int
608 + main(int argc, char **argv)
609 + {
610 + int c;
611 + uid_t uid;
612 +
613 + while ((c = getopt(argc, argv, "a:c:efi:m:o:t:Tvw")) >= 0)
614 + switch (c)
615 + {
616 + case 'a':
617 + file_access = atol(optarg);
618 + break;
619 + case 'c':
620 + set_cwd = optarg;
621 + break;
622 + case 'e':
623 + pass_environ = 1;
624 + break;
625 + case 'f':
626 + filter_syscalls++;
627 + break;
628 + case 'i':
629 + redir_stdin = optarg;
630 + break;
631 + case 'm':
632 + memory_limit = atol(optarg);
633 + break;
634 + case 'o':
635 + redir_stdout = optarg;
636 + break;
637 + case 't':
638 + timeout = atof(optarg);
639 + break;
640 + case 'T':
641 + allow_times++;
642 + break;
643 + case 'v':
644 + verbose++;
645 + break;
646 + case 'w':
647 + use_wall_clock = 1;
648 + break;
649 + default:
650 + usage();
651 + }
652 + if (optind >= argc)
653 + usage();
654 +
655 + uid = geteuid();
656 + if (setreuid(uid, uid) < 0)
657 + die("setreuid: %m");
658 + box_pid = fork();
659 + if (box_pid < 0)
660 + die("fork: %m");
661 + if (!box_pid)
662 + box_inside(argc-optind, argv+optind);
663 + else
664 + boxkeeper();
665 + die("Internal error: fell over edge of the world");
666 + }
You need to be logged in to leave comments. Login now