Description:
merge java fix from branch algo
Commit status:
[Not Reviewed]
References:
merge java
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r159:6a6e1b1369c0 - - 5 files changed: 1807 inserted, 24 deleted

This diff has been collapsed as it changes many lines, (1752 lines changed) Show them Hide them
@@ -0,0 +1,1752
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 + /* Generated automatically by ./configure, please don't touch manually. */
11 + #define CONFIG_BOX_KERNEL_AMD64 1
12 + #define CONFIG_BOX_USER_AMD64 1
13 + #define CONFIG_DIR "cf"
14 + #define CONFIG_DIRECT_IO 1
15 + #define CONFIG_ISOLATE_BOX_DIR "/tmp/box"
16 + #define CONFIG_ISOLATE_CGROUP_ROOT "/sys/fs/cgroup"
17 + #define CONFIG_ISOLATE_FIRST_GID 60000
18 + #define CONFIG_ISOLATE_FIRST_UID 60000
19 + #define CONFIG_ISOLATE_NUM_BOXES 100
20 + #define CONFIG_LARGE_FILES 1
21 + #define CONFIG_LFS 1
22 + #define CONFIG_LINUX 1
23 + #define CONFIG_LOCAL 1
24 + #define CONFIG_UCW_PARTMAP_IS_MMAP 1
25 + #define CONFIG_UCW_PERL 1
26 + #define CONFIG_UCW_POOL_IS_MMAP 1
27 + #define CONFIG_UCW_RADIX_SORTER_BITS 10
28 + #define CONFIG_UCW_SHELL_UTILS 1
29 + #define CPU_64BIT_POINTERS 1
30 + #define CPU_ALLOW_UNALIGNED 1
31 + #define CPU_AMD64 1
32 + #define CPU_ARCH "default"
33 + #define CPU_LITTLE_ENDIAN 1
34 + #define CPU_PAGE_SIZE 4096
35 + #define CPU_STRUCT_ALIGN 8
36 + #define CWARNS_OFF " -Wno-pointer-sign"
37 + #define HAVE_ASCII_DOC "none"
38 + #define INSTALL_BIN_DIR "bin"
39 + #define INSTALL_CONFIG_DIR "cf"
40 + #define INSTALL_DOC_DIR "share/doc"
41 + #define INSTALL_INCLUDE_DIR "include"
42 + #define INSTALL_LIB_DIR "lib"
43 + #define INSTALL_LOG_DIR "log"
44 + #define INSTALL_MAN_DIR "share/man"
45 + #define INSTALL_PERL_DIR "lib/perl5"
46 + #define INSTALL_PKGCONFIG_DIR "lib/pkgconfig"
47 + #define INSTALL_PREFIX
48 + #define INSTALL_RUN_DIR "run"
49 + #define INSTALL_SBIN_DIR "sbin"
50 + #define INSTALL_SHARE_DIR "share"
51 + #define INSTALL_STATE_DIR "lib"
52 + #define INSTALL_USR_PREFIX
53 + #define INSTALL_VAR_PREFIX
54 + #define SHERLOCK_VERSION "3.99.2"
55 + #define SHERLOCK_VERSION_CODE 3099002
56 + #define SONAME_PREFIX "lib/"
57 + #define UCW_VERSION "3.99.2"
58 + #define UCW_VERSION_CODE 3099002
59 +
60 + #include <errno.h>
61 + #include <stdio.h>
62 + #include <fcntl.h>
63 + #include <stdlib.h>
64 + #include <string.h>
65 + #include <stdarg.h>
66 + #include <stdint.h>
67 + #include <unistd.h>
68 + #include <getopt.h>
69 + #include <time.h>
70 + #include <sys/wait.h>
71 + #include <sys/user.h>
72 + #include <sys/time.h>
73 + #include <sys/ptrace.h>
74 + #include <sys/signal.h>
75 + #include <sys/sysinfo.h>
76 + #include <sys/resource.h>
77 + #include <sys/utsname.h>
78 + //#include <linux/ptrace.h>
79 +
80 + #if defined(CONFIG_BOX_KERNEL_AMD64) && !defined(CONFIG_BOX_USER_AMD64)
81 + #include <asm/unistd_32.h>
82 + #define NATIVE_NR_execve 59 /* 64-bit execve */
83 + #else
84 + #include <asm/unistd.h>
85 + #define NATIVE_NR_execve __NR_execve
86 + #endif
87 +
88 + #define NONRET __attribute__((noreturn))
89 + #define UNUSED __attribute__((unused))
90 + #define ARRAY_SIZE(a) (int)(sizeof(a)/sizeof(a[0]))
91 +
92 + static int filter_syscalls; /* 0=off, 1=liberal, 2=totalitarian */
93 + static int timeout; /* milliseconds */
94 + static int wall_timeout;
95 + static int extra_timeout;
96 + static int pass_environ;
97 + static int file_access;
98 + static int verbose;
99 + static int memory_limit;
100 + static int stack_limit;
101 + static char *redir_stdin, *redir_stdout, *redir_stderr;
102 + static char *set_cwd;
103 +
104 + static pid_t box_pid;
105 + static int is_ptraced;
106 + static volatile int timer_tick;
107 + static struct timeval start_time;
108 + static int ticks_per_sec;
109 + static int exec_seen;
110 + static int partial_line;
111 +
112 + static int mem_peak_kb;
113 + static int total_ms, wall_ms, sys_ms;
114 +
115 + static void die(char *msg, ...) NONRET;
116 + static void sample_mem_peak(void);
117 +
118 + /*** Meta-files ***/
119 +
120 + static FILE *metafile;
121 +
122 + static void
123 + meta_open(const char *name)
124 + {
125 + if (!strcmp(name, "-"))
126 + {
127 + metafile = stdout;
128 + return;
129 + }
130 + metafile = fopen(name, "w");
131 + if (!metafile)
132 + die("Failed to open metafile '%s'",name);
133 + }
134 +
135 + static void
136 + meta_close(void)
137 + {
138 + if (metafile && metafile != stdout)
139 + fclose(metafile);
140 + }
141 +
142 + static void __attribute__((format(printf,1,2)))
143 + meta_printf(const char *fmt, ...)
144 + {
145 + if (!metafile)
146 + return;
147 +
148 + va_list args;
149 + va_start(args, fmt);
150 + vfprintf(metafile, fmt, args);
151 + va_end(args);
152 + }
153 +
154 +
155 + static void print_running_stat(double wall_time,
156 + double user_time,
157 + double system_time,
158 + int mem_usage)
159 + {
160 + //total is user
161 + //wall is wall
162 + //
163 + fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
164 + wall_time, user_time, system_time, mem_usage);
165 + }
166 +
167 + static void
168 + final_stats(struct rusage *rus)
169 + {
170 + struct timeval total, now, wall;
171 + timeradd(&rus->ru_utime, &rus->ru_stime, &total);
172 + total_ms = total.tv_sec*1000 + total.tv_usec/1000;
173 + gettimeofday(&now, NULL);
174 + timersub(&now, &start_time, &wall);
175 + wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
176 + sys_ms = rus->ru_stime.tv_sec * 1000 + rus->ru_stime.tv_usec / 1000;
177 +
178 + meta_printf("time:%d.%03d\n", total_ms/1000, total_ms%1000);
179 + meta_printf("time-wall:%d.%03d\n", wall_ms/1000, wall_ms%1000);
180 + meta_printf("mem:%llu\n", (unsigned long long) mem_peak_kb * 1024);
181 + }
182 +
183 + /*** Messages and exits ***/
184 +
185 + static void NONRET
186 + box_exit(int rc)
187 + {
188 + if (box_pid > 0)
189 + {
190 + sample_mem_peak();
191 + if (is_ptraced)
192 + ptrace(PTRACE_KILL, box_pid);
193 + kill(-box_pid, SIGKILL);
194 + kill(box_pid, SIGKILL);
195 + meta_printf("killed:1\n");
196 +
197 + struct rusage rus;
198 + int p, stat;
199 + do
200 + p = wait4(box_pid, &stat, 0, &rus);
201 + while (p < 0 && errno == EINTR);
202 + if (p < 0)
203 + fprintf(stderr, "UGH: Lost track of the process (%m)\n");
204 + else {
205 + final_stats(&rus);
206 + }
207 + }
208 + print_running_stat(
209 + (double)wall_ms/1000,
210 + (double)total_ms/1000,
211 + (double)sys_ms/1000,
212 + (mem_peak_kb + 1023) / 1024);
213 + meta_close();
214 + exit(rc);
215 + }
216 +
217 + static void
218 + flush_line(void)
219 + {
220 + if (partial_line)
221 + fputc('\n', stderr);
222 + partial_line = 0;
223 + }
224 +
225 + /* Report an error of the sandbox itself */
226 + static void NONRET __attribute__((format(printf,1,2)))
227 + die(char *msg, ...)
228 + {
229 + va_list args;
230 + va_start(args, msg);
231 + flush_line();
232 + char buf[1024];
233 + vsnprintf(buf, sizeof(buf), msg, args);
234 + meta_printf("status:XX\nmessage:%s\n", buf);
235 + fputs(buf, stderr);
236 + fputc('\n', stderr);
237 + box_exit(2);
238 + }
239 +
240 + /* Report an error of the program inside the sandbox */
241 + static void NONRET __attribute__((format(printf,1,2)))
242 + err(char *msg, ...)
243 + {
244 + va_list args;
245 + va_start(args, msg);
246 + flush_line();
247 + if (msg[0] && msg[1] && msg[2] == ':' && msg[3] == ' ')
248 + {
249 + meta_printf("status:%c%c\n", msg[0], msg[1]);
250 + msg += 4;
251 + }
252 + char buf[1024];
253 + vsnprintf(buf, sizeof(buf), msg, args);
254 + meta_printf("message:%s\n", buf);
255 + fputs(buf, stderr);
256 + fputc('\n', stderr);
257 + box_exit(1);
258 + }
259 +
260 + /* Write a message, but only if in verbose mode */
261 + static void __attribute__((format(printf,1,2)))
262 + msg(char *msg, ...)
263 + {
264 + va_list args;
265 + va_start(args, msg);
266 + if (verbose)
267 + {
268 + int len = strlen(msg);
269 + if (len > 0)
270 + partial_line = (msg[len-1] != '\n');
271 + vfprintf(stderr, msg, args);
272 + fflush(stderr);
273 + }
274 + va_end(args);
275 + }
276 +
277 + static void *
278 + xmalloc(size_t size)
279 + {
280 + void *p = malloc(size);
281 + if (!p)
282 + die("Out of memory");
283 + return p;
284 + }
285 +
286 + /*** Syscall rules ***/
287 +
288 + static const char * const syscall_names[] = {
289 +
290 + /* Syscall table automatically generated by mk-syscall-table */
291 +
292 + /* 0 */ [ __NR_read ] = "read",
293 + /* 1 */ [ __NR_write ] = "write",
294 + /* 2 */ [ __NR_open ] = "open",
295 + /* 3 */ [ __NR_close ] = "close",
296 + /* 4 */ [ __NR_stat ] = "stat",
297 + /* 5 */ [ __NR_fstat ] = "fstat",
298 + /* 6 */ [ __NR_lstat ] = "lstat",
299 + /* 7 */ [ __NR_poll ] = "poll",
300 + /* 8 */ [ __NR_lseek ] = "lseek",
301 + /* 9 */ [ __NR_mmap ] = "mmap",
302 + /* 10 */ [ __NR_mprotect ] = "mprotect",
303 + /* 11 */ [ __NR_munmap ] = "munmap",
304 + /* 12 */ [ __NR_brk ] = "brk",
305 + /* 13 */ [ __NR_rt_sigaction ] = "rt_sigaction",
306 + /* 14 */ [ __NR_rt_sigprocmask ] = "rt_sigprocmask",
307 + /* 15 */ [ __NR_rt_sigreturn ] = "rt_sigreturn",
308 + /* 16 */ [ __NR_ioctl ] = "ioctl",
309 + /* 17 */ [ __NR_pread64 ] = "pread64",
310 + /* 18 */ [ __NR_pwrite64 ] = "pwrite64",
311 + /* 19 */ [ __NR_readv ] = "readv",
312 + /* 20 */ [ __NR_writev ] = "writev",
313 + /* 21 */ [ __NR_access ] = "access",
314 + /* 22 */ [ __NR_pipe ] = "pipe",
315 + /* 23 */ [ __NR_select ] = "select",
316 + /* 24 */ [ __NR_sched_yield ] = "sched_yield",
317 + /* 25 */ [ __NR_mremap ] = "mremap",
318 + /* 26 */ [ __NR_msync ] = "msync",
319 + /* 27 */ [ __NR_mincore ] = "mincore",
320 + /* 28 */ [ __NR_madvise ] = "madvise",
321 + /* 29 */ [ __NR_shmget ] = "shmget",
322 + /* 30 */ [ __NR_shmat ] = "shmat",
323 + /* 31 */ [ __NR_shmctl ] = "shmctl",
324 + /* 32 */ [ __NR_dup ] = "dup",
325 + /* 33 */ [ __NR_dup2 ] = "dup2",
326 + /* 34 */ [ __NR_pause ] = "pause",
327 + /* 35 */ [ __NR_nanosleep ] = "nanosleep",
328 + /* 36 */ [ __NR_getitimer ] = "getitimer",
329 + /* 37 */ [ __NR_alarm ] = "alarm",
330 + /* 38 */ [ __NR_setitimer ] = "setitimer",
331 + /* 39 */ [ __NR_getpid ] = "getpid",
332 + /* 40 */ [ __NR_sendfile ] = "sendfile",
333 + /* 41 */ [ __NR_socket ] = "socket",
334 + /* 42 */ [ __NR_connect ] = "connect",
335 + /* 43 */ [ __NR_accept ] = "accept",
336 + /* 44 */ [ __NR_sendto ] = "sendto",
337 + /* 45 */ [ __NR_recvfrom ] = "recvfrom",
338 + /* 46 */ [ __NR_sendmsg ] = "sendmsg",
339 + /* 47 */ [ __NR_recvmsg ] = "recvmsg",
340 + /* 48 */ [ __NR_shutdown ] = "shutdown",
341 + /* 49 */ [ __NR_bind ] = "bind",
342 + /* 50 */ [ __NR_listen ] = "listen",
343 + /* 51 */ [ __NR_getsockname ] = "getsockname",
344 + /* 52 */ [ __NR_getpeername ] = "getpeername",
345 + /* 53 */ [ __NR_socketpair ] = "socketpair",
346 + /* 54 */ [ __NR_setsockopt ] = "setsockopt",
347 + /* 55 */ [ __NR_getsockopt ] = "getsockopt",
348 + /* 56 */ [ __NR_clone ] = "clone",
349 + /* 57 */ [ __NR_fork ] = "fork",
350 + /* 58 */ [ __NR_vfork ] = "vfork",
351 + /* 59 */ [ __NR_execve ] = "execve",
352 + /* 60 */ [ __NR_exit ] = "exit",
353 + /* 61 */ [ __NR_wait4 ] = "wait4",
354 + /* 62 */ [ __NR_kill ] = "kill",
355 + /* 63 */ [ __NR_uname ] = "uname",
356 + /* 64 */ [ __NR_semget ] = "semget",
357 + /* 65 */ [ __NR_semop ] = "semop",
358 + /* 66 */ [ __NR_semctl ] = "semctl",
359 + /* 67 */ [ __NR_shmdt ] = "shmdt",
360 + /* 68 */ [ __NR_msgget ] = "msgget",
361 + /* 69 */ [ __NR_msgsnd ] = "msgsnd",
362 + /* 70 */ [ __NR_msgrcv ] = "msgrcv",
363 + /* 71 */ [ __NR_msgctl ] = "msgctl",
364 + /* 72 */ [ __NR_fcntl ] = "fcntl",
365 + /* 73 */ [ __NR_flock ] = "flock",
366 + /* 74 */ [ __NR_fsync ] = "fsync",
367 + /* 75 */ [ __NR_fdatasync ] = "fdatasync",
368 + /* 76 */ [ __NR_truncate ] = "truncate",
369 + /* 77 */ [ __NR_ftruncate ] = "ftruncate",
370 + /* 78 */ [ __NR_getdents ] = "getdents",
371 + /* 79 */ [ __NR_getcwd ] = "getcwd",
372 + /* 80 */ [ __NR_chdir ] = "chdir",
373 + /* 81 */ [ __NR_fchdir ] = "fchdir",
374 + /* 82 */ [ __NR_rename ] = "rename",
375 + /* 83 */ [ __NR_mkdir ] = "mkdir",
376 + /* 84 */ [ __NR_rmdir ] = "rmdir",
377 + /* 85 */ [ __NR_creat ] = "creat",
378 + /* 86 */ [ __NR_link ] = "link",
379 + /* 87 */ [ __NR_unlink ] = "unlink",
380 + /* 88 */ [ __NR_symlink ] = "symlink",
381 + /* 89 */ [ __NR_readlink ] = "readlink",
382 + /* 90 */ [ __NR_chmod ] = "chmod",
383 + /* 91 */ [ __NR_fchmod ] = "fchmod",
384 + /* 92 */ [ __NR_chown ] = "chown",
385 + /* 93 */ [ __NR_fchown ] = "fchown",
386 + /* 94 */ [ __NR_lchown ] = "lchown",
387 + /* 95 */ [ __NR_umask ] = "umask",
388 + /* 96 */ [ __NR_gettimeofday ] = "gettimeofday",
389 + /* 97 */ [ __NR_getrlimit ] = "getrlimit",
390 + /* 98 */ [ __NR_getrusage ] = "getrusage",
391 + /* 99 */ [ __NR_sysinfo ] = "sysinfo",
392 + /* 100 */ [ __NR_times ] = "times",
393 + /* 101 */ [ __NR_ptrace ] = "ptrace",
394 + /* 102 */ [ __NR_getuid ] = "getuid",
395 + /* 103 */ [ __NR_syslog ] = "syslog",
396 + /* 104 */ [ __NR_getgid ] = "getgid",
397 + /* 105 */ [ __NR_setuid ] = "setuid",
398 + /* 106 */ [ __NR_setgid ] = "setgid",
399 + /* 107 */ [ __NR_geteuid ] = "geteuid",
400 + /* 108 */ [ __NR_getegid ] = "getegid",
401 + /* 109 */ [ __NR_setpgid ] = "setpgid",
402 + /* 110 */ [ __NR_getppid ] = "getppid",
403 + /* 111 */ [ __NR_getpgrp ] = "getpgrp",
404 + /* 112 */ [ __NR_setsid ] = "setsid",
405 + /* 113 */ [ __NR_setreuid ] = "setreuid",
406 + /* 114 */ [ __NR_setregid ] = "setregid",
407 + /* 115 */ [ __NR_getgroups ] = "getgroups",
408 + /* 116 */ [ __NR_setgroups ] = "setgroups",
409 + /* 117 */ [ __NR_setresuid ] = "setresuid",
410 + /* 118 */ [ __NR_getresuid ] = "getresuid",
411 + /* 119 */ [ __NR_setresgid ] = "setresgid",
412 + /* 120 */ [ __NR_getresgid ] = "getresgid",
413 + /* 121 */ [ __NR_getpgid ] = "getpgid",
414 + /* 122 */ [ __NR_setfsuid ] = "setfsuid",
415 + /* 123 */ [ __NR_setfsgid ] = "setfsgid",
416 + /* 124 */ [ __NR_getsid ] = "getsid",
417 + /* 125 */ [ __NR_capget ] = "capget",
418 + /* 126 */ [ __NR_capset ] = "capset",
419 + /* 127 */ [ __NR_rt_sigpending ] = "rt_sigpending",
420 + /* 128 */ [ __NR_rt_sigtimedwait ] = "rt_sigtimedwait",
421 + /* 129 */ [ __NR_rt_sigqueueinfo ] = "rt_sigqueueinfo",
422 + /* 130 */ [ __NR_rt_sigsuspend ] = "rt_sigsuspend",
423 + /* 131 */ [ __NR_sigaltstack ] = "sigaltstack",
424 + /* 132 */ [ __NR_utime ] = "utime",
425 + /* 133 */ [ __NR_mknod ] = "mknod",
426 + /* 134 */ [ __NR_uselib ] = "uselib",
427 + /* 135 */ [ __NR_personality ] = "personality",
428 + /* 136 */ [ __NR_ustat ] = "ustat",
429 + /* 137 */ [ __NR_statfs ] = "statfs",
430 + /* 138 */ [ __NR_fstatfs ] = "fstatfs",
431 + /* 139 */ [ __NR_sysfs ] = "sysfs",
432 + /* 140 */ [ __NR_getpriority ] = "getpriority",
433 + /* 141 */ [ __NR_setpriority ] = "setpriority",
434 + /* 142 */ [ __NR_sched_setparam ] = "sched_setparam",
435 + /* 143 */ [ __NR_sched_getparam ] = "sched_getparam",
436 + /* 144 */ [ __NR_sched_setscheduler ] = "sched_setscheduler",
437 + /* 145 */ [ __NR_sched_getscheduler ] = "sched_getscheduler",
438 + /* 146 */ [ __NR_sched_get_priority_max ] = "sched_get_priority_max",
439 + /* 147 */ [ __NR_sched_get_priority_min ] = "sched_get_priority_min",
440 + /* 148 */ [ __NR_sched_rr_get_interval ] = "sched_rr_get_interval",
441 + /* 149 */ [ __NR_mlock ] = "mlock",
442 + /* 150 */ [ __NR_munlock ] = "munlock",
443 + /* 151 */ [ __NR_mlockall ] = "mlockall",
444 + /* 152 */ [ __NR_munlockall ] = "munlockall",
445 + /* 153 */ [ __NR_vhangup ] = "vhangup",
446 + /* 154 */ [ __NR_modify_ldt ] = "modify_ldt",
447 + /* 155 */ [ __NR_pivot_root ] = "pivot_root",
448 + /* 156 */ [ __NR__sysctl ] = "_sysctl",
449 + /* 157 */ [ __NR_prctl ] = "prctl",
450 + /* 158 */ [ __NR_arch_prctl ] = "arch_prctl",
451 + /* 159 */ [ __NR_adjtimex ] = "adjtimex",
452 + /* 160 */ [ __NR_setrlimit ] = "setrlimit",
453 + /* 161 */ [ __NR_chroot ] = "chroot",
454 + /* 162 */ [ __NR_sync ] = "sync",
455 + /* 163 */ [ __NR_acct ] = "acct",
456 + /* 164 */ [ __NR_settimeofday ] = "settimeofday",
457 + /* 165 */ [ __NR_mount ] = "mount",
458 + /* 166 */ [ __NR_umount2 ] = "umount2",
459 + /* 167 */ [ __NR_swapon ] = "swapon",
460 + /* 168 */ [ __NR_swapoff ] = "swapoff",
461 + /* 169 */ [ __NR_reboot ] = "reboot",
462 + /* 170 */ [ __NR_sethostname ] = "sethostname",
463 + /* 171 */ [ __NR_setdomainname ] = "setdomainname",
464 + /* 172 */ [ __NR_iopl ] = "iopl",
465 + /* 173 */ [ __NR_ioperm ] = "ioperm",
466 + /* 174 */ [ __NR_create_module ] = "create_module",
467 + /* 175 */ [ __NR_init_module ] = "init_module",
468 + /* 176 */ [ __NR_delete_module ] = "delete_module",
469 + /* 177 */ [ __NR_get_kernel_syms ] = "get_kernel_syms",
470 + /* 178 */ [ __NR_query_module ] = "query_module",
471 + /* 179 */ [ __NR_quotactl ] = "quotactl",
472 + /* 180 */ [ __NR_nfsservctl ] = "nfsservctl",
473 + /* 181 */ [ __NR_getpmsg ] = "getpmsg",
474 + /* 182 */ [ __NR_putpmsg ] = "putpmsg",
475 + /* 183 */ [ __NR_afs_syscall ] = "afs_syscall",
476 + /* 184 */ [ __NR_tuxcall ] = "tuxcall",
477 + /* 185 */ [ __NR_security ] = "security",
478 + /* 186 */ [ __NR_gettid ] = "gettid",
479 + /* 187 */ [ __NR_readahead ] = "readahead",
480 + /* 188 */ [ __NR_setxattr ] = "setxattr",
481 + /* 189 */ [ __NR_lsetxattr ] = "lsetxattr",
482 + /* 190 */ [ __NR_fsetxattr ] = "fsetxattr",
483 + /* 191 */ [ __NR_getxattr ] = "getxattr",
484 + /* 192 */ [ __NR_lgetxattr ] = "lgetxattr",
485 + /* 193 */ [ __NR_fgetxattr ] = "fgetxattr",
486 + /* 194 */ [ __NR_listxattr ] = "listxattr",
487 + /* 195 */ [ __NR_llistxattr ] = "llistxattr",
488 + /* 196 */ [ __NR_flistxattr ] = "flistxattr",
489 + /* 197 */ [ __NR_removexattr ] = "removexattr",
490 + /* 198 */ [ __NR_lremovexattr ] = "lremovexattr",
491 + /* 199 */ [ __NR_fremovexattr ] = "fremovexattr",
492 + /* 200 */ [ __NR_tkill ] = "tkill",
493 + /* 201 */ [ __NR_time ] = "time",
494 + /* 202 */ [ __NR_futex ] = "futex",
495 + /* 203 */ [ __NR_sched_setaffinity ] = "sched_setaffinity",
496 + /* 204 */ [ __NR_sched_getaffinity ] = "sched_getaffinity",
497 + /* 205 */ [ __NR_set_thread_area ] = "set_thread_area",
498 + /* 206 */ [ __NR_io_setup ] = "io_setup",
499 + /* 207 */ [ __NR_io_destroy ] = "io_destroy",
500 + /* 208 */ [ __NR_io_getevents ] = "io_getevents",
501 + /* 209 */ [ __NR_io_submit ] = "io_submit",
502 + /* 210 */ [ __NR_io_cancel ] = "io_cancel",
503 + /* 211 */ [ __NR_get_thread_area ] = "get_thread_area",
504 + /* 212 */ [ __NR_lookup_dcookie ] = "lookup_dcookie",
505 + /* 213 */ [ __NR_epoll_create ] = "epoll_create",
506 + /* 214 */ [ __NR_epoll_ctl_old ] = "epoll_ctl_old",
507 + /* 215 */ [ __NR_epoll_wait_old ] = "epoll_wait_old",
508 + /* 216 */ [ __NR_remap_file_pages ] = "remap_file_pages",
509 + /* 217 */ [ __NR_getdents64 ] = "getdents64",
510 + /* 218 */ [ __NR_set_tid_address ] = "set_tid_address",
511 + /* 219 */ [ __NR_restart_syscall ] = "restart_syscall",
512 + /* 220 */ [ __NR_semtimedop ] = "semtimedop",
513 + /* 221 */ [ __NR_fadvise64 ] = "fadvise64",
514 + /* 222 */ [ __NR_timer_create ] = "timer_create",
515 + /* 223 */ [ __NR_timer_settime ] = "timer_settime",
516 + /* 224 */ [ __NR_timer_gettime ] = "timer_gettime",
517 + /* 225 */ [ __NR_timer_getoverrun ] = "timer_getoverrun",
518 + /* 226 */ [ __NR_timer_delete ] = "timer_delete",
519 + /* 227 */ [ __NR_clock_settime ] = "clock_settime",
520 + /* 228 */ [ __NR_clock_gettime ] = "clock_gettime",
521 + /* 229 */ [ __NR_clock_getres ] = "clock_getres",
522 + /* 230 */ [ __NR_clock_nanosleep ] = "clock_nanosleep",
523 + /* 231 */ [ __NR_exit_group ] = "exit_group",
524 + /* 232 */ [ __NR_epoll_wait ] = "epoll_wait",
525 + /* 233 */ [ __NR_epoll_ctl ] = "epoll_ctl",
526 + /* 234 */ [ __NR_tgkill ] = "tgkill",
527 + /* 235 */ [ __NR_utimes ] = "utimes",
528 + /* 236 */ [ __NR_vserver ] = "vserver",
529 + /* 237 */ [ __NR_mbind ] = "mbind",
530 + /* 238 */ [ __NR_set_mempolicy ] = "set_mempolicy",
531 + /* 239 */ [ __NR_get_mempolicy ] = "get_mempolicy",
532 + /* 240 */ [ __NR_mq_open ] = "mq_open",
533 + /* 241 */ [ __NR_mq_unlink ] = "mq_unlink",
534 + /* 242 */ [ __NR_mq_timedsend ] = "mq_timedsend",
535 + /* 243 */ [ __NR_mq_timedreceive ] = "mq_timedreceive",
536 + /* 244 */ [ __NR_mq_notify ] = "mq_notify",
537 + /* 245 */ [ __NR_mq_getsetattr ] = "mq_getsetattr",
538 + /* 246 */ [ __NR_kexec_load ] = "kexec_load",
539 + /* 247 */ [ __NR_waitid ] = "waitid",
540 + /* 248 */ [ __NR_add_key ] = "add_key",
541 + /* 249 */ [ __NR_request_key ] = "request_key",
542 + /* 250 */ [ __NR_keyctl ] = "keyctl",
543 + /* 251 */ [ __NR_ioprio_set ] = "ioprio_set",
544 + /* 252 */ [ __NR_ioprio_get ] = "ioprio_get",
545 + /* 253 */ [ __NR_inotify_init ] = "inotify_init",
546 + /* 254 */ [ __NR_inotify_add_watch ] = "inotify_add_watch",
547 + /* 255 */ [ __NR_inotify_rm_watch ] = "inotify_rm_watch",
548 + /* 256 */ [ __NR_migrate_pages ] = "migrate_pages",
549 + /* 257 */ [ __NR_openat ] = "openat",
550 + /* 258 */ [ __NR_mkdirat ] = "mkdirat",
551 + /* 259 */ [ __NR_mknodat ] = "mknodat",
552 + /* 260 */ [ __NR_fchownat ] = "fchownat",
553 + /* 261 */ [ __NR_futimesat ] = "futimesat",
554 + /* 262 */ [ __NR_newfstatat ] = "newfstatat",
555 + /* 263 */ [ __NR_unlinkat ] = "unlinkat",
556 + /* 264 */ [ __NR_renameat ] = "renameat",
557 + /* 265 */ [ __NR_linkat ] = "linkat",
558 + /* 266 */ [ __NR_symlinkat ] = "symlinkat",
559 + /* 267 */ [ __NR_readlinkat ] = "readlinkat",
560 + /* 268 */ [ __NR_fchmodat ] = "fchmodat",
561 + /* 269 */ [ __NR_faccessat ] = "faccessat",
562 + /* 270 */ [ __NR_pselect6 ] = "pselect6",
563 + /* 271 */ [ __NR_ppoll ] = "ppoll",
564 + /* 272 */ [ __NR_unshare ] = "unshare",
565 + /* 273 */ [ __NR_set_robust_list ] = "set_robust_list",
566 + /* 274 */ [ __NR_get_robust_list ] = "get_robust_list",
567 + /* 275 */ [ __NR_splice ] = "splice",
568 + /* 276 */ [ __NR_tee ] = "tee",
569 + /* 277 */ [ __NR_sync_file_range ] = "sync_file_range",
570 + /* 278 */ [ __NR_vmsplice ] = "vmsplice",
571 + /* 279 */ [ __NR_move_pages ] = "move_pages",
572 + /* 280 */ [ __NR_utimensat ] = "utimensat",
573 + /* 281 */ [ __NR_epoll_pwait ] = "epoll_pwait",
574 + /* 282 */ [ __NR_signalfd ] = "signalfd",
575 + /* 283 */ [ __NR_timerfd_create ] = "timerfd_create",
576 + /* 284 */ [ __NR_eventfd ] = "eventfd",
577 + /* 285 */ [ __NR_fallocate ] = "fallocate",
578 + /* 286 */ [ __NR_timerfd_settime ] = "timerfd_settime",
579 + /* 287 */ [ __NR_timerfd_gettime ] = "timerfd_gettime",
580 + /* 288 */ [ __NR_accept4 ] = "accept4",
581 + /* 289 */ [ __NR_signalfd4 ] = "signalfd4",
582 + /* 290 */ [ __NR_eventfd2 ] = "eventfd2",
583 + /* 291 */ [ __NR_epoll_create1 ] = "epoll_create1",
584 + /* 292 */ [ __NR_dup3 ] = "dup3",
585 + /* 293 */ [ __NR_pipe2 ] = "pipe2",
586 + /* 294 */ [ __NR_inotify_init1 ] = "inotify_init1",
587 + /* 295 */ [ __NR_preadv ] = "preadv",
588 + /* 296 */ [ __NR_pwritev ] = "pwritev",
589 + /* 297 */ [ __NR_rt_tgsigqueueinfo ] = "rt_tgsigqueueinfo",
590 + /* 298 */ [ __NR_perf_event_open ] = "perf_event_open",
591 + /* 299 */ [ __NR_recvmmsg ] = "recvmmsg",
592 + /* 300 */ [ __NR_fanotify_init ] = "fanotify_init",
593 + /* 301 */ [ __NR_fanotify_mark ] = "fanotify_mark",
594 + /* 302 */ [ __NR_prlimit64 ] = "prlimit64",
595 + /* 303 */ [ __NR_name_to_handle_at ] = "name_to_handle_at",
596 + /* 304 */ [ __NR_open_by_handle_at ] = "open_by_handle_at",
597 + /* 305 */ [ __NR_clock_adjtime ] = "clock_adjtime",
598 + /* 306 */ [ __NR_syncfs ] = "syncfs",
599 + /* 307 */ [ __NR_sendmmsg ] = "sendmmsg",
600 + /* 308 */ [ __NR_setns ] = "setns",
601 + /* 309 */ [ __NR_getcpu ] = "getcpu",
602 + /* 310 */ [ __NR_process_vm_readv ] = "process_vm_readv",
603 + /* 311 */ [ __NR_process_vm_writev ] = "process_vm_writev",
604 + /* 312 */ [ __NR_kcmp ] = "kcmp",
605 + /* 313 */ [ __NR_finit_module ] = "finit_module",
606 + };
607 + #define NUM_SYSCALLS ARRAY_SIZE(syscall_names)
608 + #define NUM_ACTIONS (NUM_SYSCALLS+64)
609 +
610 + enum action {
611 + A_DEFAULT, // Use the default action
612 + A_NO, // Always forbid
613 + A_YES, // Always permit
614 + A_FILENAME, // Permit if arg1 is a known filename
615 + A_ACTION_MASK = 15,
616 + A_NO_RETVAL = 32, // Does not return a value
617 + A_SAMPLE_MEM = 64, // Sample memory usage before the syscall
618 + A_LIBERAL = 128, // Valid only in liberal mode
619 + // Must fit in a unsigned char
620 + };
621 +
622 + static unsigned char syscall_action[NUM_ACTIONS] = {
623 + #define S(x) [__NR_##x]
624 +
625 + // Syscalls permitted for specific file names
626 + S(open) = A_FILENAME,
627 + S(creat) = A_FILENAME,
628 + S(unlink) = A_FILENAME,
629 + S(access) = A_FILENAME,
630 + S(truncate) = A_FILENAME,
631 + S(stat) = A_FILENAME,
632 + S(lstat) = A_FILENAME,
633 + S(readlink) = A_FILENAME,
634 + #ifndef CONFIG_BOX_USER_AMD64
635 + S(oldstat) = A_FILENAME,
636 + S(oldlstat) = A_FILENAME,
637 + S(truncate64) = A_FILENAME,
638 + S(stat64) = A_FILENAME,
639 + S(lstat64) = A_FILENAME,
640 + #endif
641 +
642 + // Syscalls permitted always
643 + S(exit) = A_YES | A_SAMPLE_MEM,
644 + S(read) = A_YES,
645 + S(write) = A_YES,
646 + S(close) = A_YES,
647 + S(lseek) = A_YES,
648 + S(getpid) = A_YES,
649 + S(getuid) = A_YES,
650 + S(dup) = A_YES,
651 + S(brk) = A_YES,
652 + S(getgid) = A_YES,
653 + S(geteuid) = A_YES,
654 + S(getegid) = A_YES,
655 + S(dup2) = A_YES,
656 + S(ftruncate) = A_YES,
657 + S(fstat) = A_YES,
658 + S(personality) = A_YES,
659 + S(readv) = A_YES,
660 + S(writev) = A_YES,
661 + S(getresuid) = A_YES,
662 + #ifdef __NR_pread64
663 + S(pread64) = A_YES,
664 + S(pwrite64) = A_YES,
665 + #else
666 + S(pread) = A_YES,
667 + S(pwrite) = A_YES,
668 + #endif
669 + S(fcntl) = A_YES,
670 + S(mmap) = A_YES,
671 + S(munmap) = A_YES,
672 + S(ioctl) = A_YES,
673 + S(uname) = A_YES,
674 + S(gettid) = A_YES,
675 + S(set_thread_area) = A_YES,
676 + S(get_thread_area) = A_YES,
677 + S(set_tid_address) = A_YES,
678 + S(exit_group) = A_YES | A_SAMPLE_MEM,
679 + #ifdef CONFIG_BOX_USER_AMD64
680 + S(arch_prctl) = A_YES,
681 + #else
682 + S(oldfstat) = A_YES,
683 + S(ftruncate64) = A_YES,
684 + S(_llseek) = A_YES,
685 + S(fstat64) = A_YES,
686 + S(fcntl64) = A_YES,
687 + S(mmap2) = A_YES,
688 + #endif
689 +
690 + // Syscalls permitted only in liberal mode
691 + S(time) = A_YES | A_LIBERAL,
692 + S(alarm) = A_YES | A_LIBERAL,
693 + S(pause) = A_YES | A_LIBERAL,
694 + S(fchmod) = A_YES | A_LIBERAL,
695 + S(getrlimit) = A_YES | A_LIBERAL,
696 + S(getrusage) = A_YES | A_LIBERAL,
697 + S(gettimeofday) = A_YES | A_LIBERAL,
698 + S(select) = A_YES | A_LIBERAL,
699 + S(setitimer) = A_YES | A_LIBERAL,
700 + S(getitimer) = A_YES | A_LIBERAL,
701 + S(mprotect) = A_YES | A_LIBERAL,
702 + S(getdents) = A_YES | A_LIBERAL,
703 + S(getdents64) = A_YES | A_LIBERAL,
704 + S(fdatasync) = A_YES | A_LIBERAL,
705 + S(mremap) = A_YES | A_LIBERAL,
706 + S(poll) = A_YES | A_LIBERAL,
707 + S(getcwd) = A_YES | A_LIBERAL,
708 + S(nanosleep) = A_YES | A_LIBERAL,
709 + S(rt_sigreturn) = A_YES | A_LIBERAL | A_NO_RETVAL,
710 + S(rt_sigaction) = A_YES | A_LIBERAL,
711 + S(rt_sigprocmask) = A_YES | A_LIBERAL,
712 + S(rt_sigpending) = A_YES | A_LIBERAL,
713 + S(rt_sigtimedwait) = A_YES | A_LIBERAL,
714 + S(rt_sigqueueinfo) = A_YES | A_LIBERAL,
715 + S(rt_sigsuspend) = A_YES | A_LIBERAL,
716 + S(_sysctl) = A_YES | A_LIBERAL,
717 + #ifndef CONFIG_BOX_USER_AMD64
718 + S(sigaction) = A_YES | A_LIBERAL,
719 + S(sgetmask) = A_YES | A_LIBERAL,
720 + S(ssetmask) = A_YES | A_LIBERAL,
721 + S(sigsuspend) = A_YES | A_LIBERAL,
722 + S(sigpending) = A_YES | A_LIBERAL,
723 + S(sigreturn) = A_YES | A_LIBERAL | A_NO_RETVAL,
724 + S(sigprocmask) = A_YES | A_LIBERAL,
725 + S(ugetrlimit) = A_YES | A_LIBERAL,
726 + S(readdir) = A_YES | A_LIBERAL,
727 + S(signal) = A_YES | A_LIBERAL,
728 + S(_newselect) = A_YES | A_LIBERAL,
729 + #endif
730 +
731 + #undef S
732 + };
733 +
734 + static const char *
735 + syscall_name(unsigned int id, char *buf)
736 + {
737 + if (id < NUM_SYSCALLS && syscall_names[id])
738 + return syscall_names[id];
739 + else
740 + {
741 + sprintf(buf, "#%d", id);
742 + return buf;
743 + }
744 + }
745 +
746 + static int
747 + syscall_by_name(char *name)
748 + {
749 + for (unsigned int i=0; i<NUM_SYSCALLS; i++)
750 + if (syscall_names[i] && !strcmp(syscall_names[i], name))
751 + return i;
752 + if (name[0] == '#')
753 + name++;
754 + if (!*name)
755 + return -1;
756 + char *ep;
757 + unsigned long l = strtoul(name, &ep, 0);
758 + if (*ep)
759 + return -1;
760 + if (l >= NUM_ACTIONS)
761 + return NUM_ACTIONS;
762 + return l;
763 + }
764 +
765 + static int
766 + set_syscall_action(char *a)
767 + {
768 + char *sep = strchr(a, '=');
769 + enum action act = A_YES;
770 + if (sep)
771 + {
772 + *sep++ = 0;
773 + if (!strcmp(sep, "yes"))
774 + act = A_YES;
775 + else if (!strcmp(sep, "no"))
776 + act = A_NO;
777 + else if (!strcmp(sep, "file"))
778 + act = A_FILENAME;
779 + else
780 + return 0;
781 + }
782 +
783 + int sys = syscall_by_name(a);
784 + if (sys < 0)
785 + die("Unknown syscall `%s'", a);
786 + if (sys >= NUM_ACTIONS)
787 + die("Syscall `%s' out of range", a);
788 + syscall_action[sys] = act;
789 + return 1;
790 + }
791 +
792 + /*** Path rules ***/
793 +
794 + struct path_rule {
795 + char *path;
796 + enum action action;
797 + struct path_rule *next;
798 + };
799 +
800 + static struct path_rule default_path_rules[] = {
801 + { "/etc/", A_YES },
802 + { "/lib/", A_YES },
803 + { "/usr/lib/", A_YES },
804 + { "/opt/lib/", A_YES },
805 + { "/usr/share/zoneinfo/", A_YES },
806 + { "/usr/share/locale/", A_YES },
807 + { "/dev/null", A_YES },
808 + { "/dev/zero", A_YES },
809 + { "/proc/meminfo", A_YES },
810 + { "/proc/self/stat", A_YES },
811 + { "/proc/self/exe", A_YES }, // Needed by FPC 2.0.x runtime
812 + { "/proc/self/maps", A_YES }, // Needed by glibc when it reports arena corruption
813 + };
814 +
815 + static struct path_rule *user_path_rules;
816 + static struct path_rule **last_path_rule = &user_path_rules;
817 +
818 + static int
819 + set_path_action(char *a)
820 + {
821 + char *sep = strchr(a, '=');
822 + enum action act = A_YES;
823 + if (sep)
824 + {
825 + *sep++ = 0;
826 + if (!strcmp(sep, "yes"))
827 + act = A_YES;
828 + else if (!strcmp(sep, "no"))
829 + act = A_NO;
830 + else
831 + return 0;
832 + }
833 +
834 + struct path_rule *r = xmalloc(sizeof(*r) + strlen(a) + 1);
835 + r->path = (char *)(r+1);
836 + strcpy(r->path, a);
837 + r->action = act;
838 + r->next = NULL;
839 + *last_path_rule = r;
840 + last_path_rule = &r->next;
841 + return 1;
842 + }
843 +
844 + static enum action
845 + match_path_rule(struct path_rule *r, char *path)
846 + {
847 + char *rr = r->path;
848 + while (*rr)
849 + if (*rr++ != *path++)
850 + {
851 + if (rr[-1] == '/' && !path[-1])
852 + break;
853 + return A_DEFAULT;
854 + }
855 + if (rr > r->path && rr[-1] != '/' && *path)
856 + return A_DEFAULT;
857 + return r->action;
858 + }
859 +
860 + /*** Environment rules ***/
861 +
862 + struct env_rule {
863 + char *var; // Variable to match
864 + char *val; // ""=clear, NULL=inherit
865 + int var_len;
866 + struct env_rule *next;
867 + };
868 +
869 + static struct env_rule *first_env_rule;
870 + static struct env_rule **last_env_rule = &first_env_rule;
871 +
872 + static struct env_rule default_env_rules[] = {
873 + { "LIBC_FATAL_STDERR_", "1" }
874 + };
875 +
876 + static int
877 + set_env_action(char *a0)
878 + {
879 + struct env_rule *r = xmalloc(sizeof(*r) + strlen(a0) + 1);
880 + char *a = (char *)(r+1);
881 + strcpy(a, a0);
882 +
883 + char *sep = strchr(a, '=');
884 + if (sep == a)
885 + return 0;
886 + r->var = a;
887 + if (sep)
888 + {
889 + *sep++ = 0;
890 + r->val = sep;
891 + }
892 + else
893 + r->val = NULL;
894 + *last_env_rule = r;
895 + last_env_rule = &r->next;
896 + r->next = NULL;
897 + return 1;
898 + }
899 +
900 + static int
901 + match_env_var(char *env_entry, struct env_rule *r)
902 + {
903 + if (strncmp(env_entry, r->var, r->var_len))
904 + return 0;
905 + return (env_entry[r->var_len] == '=');
906 + }
907 +
908 + static void
909 + apply_env_rule(char **env, int *env_sizep, struct env_rule *r)
910 + {
911 + // First remove the variable if already set
912 + int pos = 0;
913 + while (pos < *env_sizep && !match_env_var(env[pos], r))
914 + pos++;
915 + if (pos < *env_sizep)
916 + {
917 + (*env_sizep)--;
918 + env[pos] = env[*env_sizep];
919 + env[*env_sizep] = NULL;
920 + }
921 +
922 + // What is the new value?
923 + char *new;
924 + if (r->val)
925 + {
926 + if (!r->val[0])
927 + return;
928 + new = xmalloc(r->var_len + 1 + strlen(r->val) + 1);
929 + sprintf(new, "%s=%s", r->var, r->val);
930 + }
931 + else
932 + {
933 + pos = 0;
934 + while (environ[pos] && !match_env_var(environ[pos], r))
935 + pos++;
936 + if (!(new = environ[pos]))
937 + return;
938 + }
939 +
940 + // Add it at the end of the array
941 + env[(*env_sizep)++] = new;
942 + env[*env_sizep] = NULL;
943 + }
944 +
945 + static char **
946 + setup_environment(void)
947 + {
948 + // Link built-in rules with user rules
949 + for (int i=ARRAY_SIZE(default_env_rules)-1; i >= 0; i--)
950 + {
951 + default_env_rules[i].next = first_env_rule;
952 + first_env_rule = &default_env_rules[i];
953 + }
954 +
955 + // Scan the original environment
956 + char **orig_env = environ;
957 + int orig_size = 0;
958 + while (orig_env[orig_size])
959 + orig_size++;
960 +
961 + // For each rule, reserve one more slot and calculate length
962 + int num_rules = 0;
963 + for (struct env_rule *r = first_env_rule; r; r=r->next)
964 + {
965 + num_rules++;
966 + r->var_len = strlen(r->var);
967 + }
968 +
969 + // Create a new environment
970 + char **env = xmalloc((orig_size + num_rules + 1) * sizeof(char *));
971 + int size;
972 + if (pass_environ)
973 + {
974 + memcpy(env, environ, orig_size * sizeof(char *));
975 + size = orig_size;
976 + }
977 + else
978 + size = 0;
979 + env[size] = NULL;
980 +
981 + // Apply the rules one by one
982 + for (struct env_rule *r = first_env_rule; r; r=r->next)
983 + apply_env_rule(env, &size, r);
984 +
985 + // Return the new env and pass some gossip
986 + if (verbose > 1)
987 + {
988 + fprintf(stderr, "Passing environment:\n");
989 + for (int i=0; env[i]; i++)
990 + fprintf(stderr, "\t%s\n", env[i]);
991 + }
992 + return env;
993 + }
994 +
995 + /*** Low-level parsing of syscalls ***/
996 +
997 + #ifdef CONFIG_BOX_KERNEL_AMD64
998 + typedef uint64_t arg_t;
999 + #else
1000 + typedef uint32_t arg_t;
1001 + #endif
1002 +
1003 + struct syscall_args {
1004 + arg_t sys;
1005 + arg_t arg1, arg2, arg3;
1006 + arg_t result;
1007 + struct user user;
1008 + };
1009 +
1010 + static int user_mem_fd;
1011 +
1012 + static int read_user_mem(arg_t addr, char *buf, int len)
1013 + {
1014 + if (!user_mem_fd)
1015 + {
1016 + char memname[64];
1017 + sprintf(memname, "/proc/%d/mem", (int) box_pid);
1018 + user_mem_fd = open(memname, O_RDONLY);
1019 + if (user_mem_fd < 0)
1020 + die("open(%s): %m", memname);
1021 + }
1022 + if (lseek64(user_mem_fd, addr, SEEK_SET) < 0)
1023 + die("lseek64(mem): %m");
1024 + return read(user_mem_fd, buf, len);
1025 + }
1026 +
1027 + static void close_user_mem(void)
1028 + {
1029 + if (user_mem_fd)
1030 + {
1031 + close(user_mem_fd);
1032 + user_mem_fd = 0;
1033 + }
1034 + }
1035 +
1036 + #ifdef CONFIG_BOX_KERNEL_AMD64
1037 +
1038 + static void
1039 + get_syscall_args(struct syscall_args *a, int is_exit)
1040 + {
1041 + if (ptrace(PTRACE_GETREGS, box_pid, NULL, &a->user) < 0)
1042 + die("ptrace(PTRACE_GETREGS): %m");
1043 + a->sys = a->user.regs.orig_rax;
1044 + a->result = a->user.regs.rax;
1045 +
1046 + /*
1047 + * CAVEAT: We have to check carefully that this is a real 64-bit syscall.
1048 + * We test whether the process runs in 64-bit mode, but surprisingly this
1049 + * is not enough: a 64-bit process can still issue the INT 0x80 instruction
1050 + * which performs a 32-bit syscall. Currently, the only known way how to
1051 + * detect this situation is to inspect the instruction code (the kernel
1052 + * keeps a syscall type flag internally, but it is not accessible from
1053 + * user space). Hopefully, there is no instruction whose suffix is the
1054 + * code of the SYSCALL instruction. Sometimes, one would wish the
1055 + * instruction codes to be unique even when read backwards :)
1056 + */
1057 +
1058 + if (is_exit)
1059 + return;
1060 +
1061 + int sys_type;
1062 + uint16_t instr;
1063 +
1064 + switch (a->user.regs.cs)
1065 + {
1066 + case 0x23:
1067 + // 32-bit CPU mode => only 32-bit syscalls can be issued
1068 + sys_type = 32;
1069 + break;
1070 + case 0x33:
1071 + // 64-bit CPU mode
1072 + if (read_user_mem(a->user.regs.rip-2, (char *) &instr, 2) != 2)
1073 + err("FO: Cannot read syscall instruction");
1074 + switch (instr)
1075 + {
1076 + case 0x050f:
1077 + break;
1078 + case 0x80cd:
1079 + err("FO: Forbidden 32-bit syscall in 64-bit mode");
1080 + default:
1081 + err("XX: Unknown syscall instruction %04x", instr);
1082 + }
1083 + sys_type = 64;
1084 + break;
1085 + default:
1086 + err("XX: Unknown code segment %04jx", (intmax_t) a->user.regs.cs);
1087 + }
1088 +
1089 + #ifdef CONFIG_BOX_USER_AMD64
1090 + if (sys_type != 64)
1091 + err("FO: Forbidden %d-bit mode syscall", sys_type);
1092 + #else
1093 + if (sys_type != (exec_seen ? 32 : 64))
1094 + err("FO: Forbidden %d-bit mode syscall", sys_type);
1095 + #endif
1096 +
1097 + if (sys_type == 32)
1098 + {
1099 + a->arg1 = a->user.regs.rbx;
1100 + a->arg2 = a->user.regs.rcx;
1101 + a->arg3 = a->user.regs.rdx;
1102 + }
1103 + else
1104 + {
1105 + a->arg1 = a->user.regs.rdi;
1106 + a->arg2 = a->user.regs.rsi;
1107 + a->arg3 = a->user.regs.rdx;
1108 + }
1109 + }
1110 +
1111 + static void
1112 + set_syscall_nr(struct syscall_args *a, arg_t sys)
1113 + {
1114 + a->sys = sys;
1115 + a->user.regs.orig_rax = sys;
1116 + if (ptrace(PTRACE_SETREGS, box_pid, NULL, &a->user) < 0)
1117 + die("ptrace(PTRACE_SETREGS): %m");
1118 + }
1119 +
1120 + static void
1121 + sanity_check(void)
1122 + {
1123 + }
1124 +
1125 + #else
1126 +
1127 + static void
1128 + get_syscall_args(struct syscall_args *a, int is_exit UNUSED)
1129 + {
1130 + if (ptrace(PTRACE_GETREGS, box_pid, NULL, &a->user) < 0)
1131 + die("ptrace(PTRACE_GETREGS): %m");
1132 + a->sys = a->user.regs.orig_eax;
1133 + a->arg1 = a->user.regs.ebx;
1134 + a->arg2 = a->user.regs.ecx;
1135 + a->arg3 = a->user.regs.edx;
1136 + a->result = a->user.regs.eax;
1137 + }
1138 +
1139 + static void
1140 + set_syscall_nr(struct syscall_args *a, arg_t sys)
1141 + {
1142 + a->sys = sys;
1143 + a->user.regs.orig_eax = sys;
1144 + if (ptrace(PTRACE_SETREGS, box_pid, NULL, &a->user) < 0)
1145 + die("ptrace(PTRACE_SETREGS): %m");
1146 + }
1147 +
1148 + static void
1149 + sanity_check(void)
1150 + {
1151 + #if !defined(CONFIG_BOX_ALLOW_INSECURE)
1152 + struct utsname uts;
1153 + if (uname(&uts) < 0)
1154 + die("uname() failed: %m");
1155 +
1156 + if (!strcmp(uts.machine, "x86_64"))
1157 + die("Running 32-bit sandbox on 64-bit kernels is inherently unsafe. Please get a 64-bit version.");
1158 + #endif
1159 + }
1160 +
1161 + #endif
1162 +
1163 + /*** Syscall checks ***/
1164 +
1165 + static void
1166 + valid_filename(arg_t addr)
1167 + {
1168 + char namebuf[4096], *p, *end;
1169 +
1170 + if (!file_access)
1171 + err("FA: File access forbidden");
1172 + if (file_access >= 9)
1173 + return;
1174 +
1175 + p = end = namebuf;
1176 + do
1177 + {
1178 + if (p >= end)
1179 + {
1180 + int remains = PAGE_SIZE - (addr & (PAGE_SIZE-1));
1181 + int l = namebuf + sizeof(namebuf) - end;
1182 + if (l > remains)
1183 + l = remains;
1184 + if (!l)
1185 + err("FA: Access to file with name too long");
1186 + remains = read_user_mem(addr, end, l);
1187 + if (remains < 0)
1188 + die("read(mem): %m");
1189 + if (!remains)
1190 + err("FA: Access to file with name out of memory");
1191 + end += remains;
1192 + addr += remains;
1193 + }
1194 + }
1195 + while (*p++);
1196 +
1197 + msg("[%s] ", namebuf);
1198 + if (file_access >= 3)
1199 + return;
1200 +
1201 + // Everything in current directory is permitted
1202 + if (!strchr(namebuf, '/') && strcmp(namebuf, ".."))
1203 + return;
1204 +
1205 + // ".." anywhere in the path is forbidden
1206 + enum action act = A_DEFAULT;
1207 + if (strstr(namebuf, ".."))
1208 + act = A_NO;
1209 +
1210 + // Scan user rules
1211 + for (struct path_rule *r = user_path_rules; r && !act; r=r->next)
1212 + act = match_path_rule(r, namebuf);
1213 +
1214 + // Scan built-in rules
1215 + if (file_access >= 2)
1216 + for (int i=0; i<ARRAY_SIZE(default_path_rules) && !act; i++)
1217 + act = match_path_rule(&default_path_rules[i], namebuf);
1218 +
1219 + if (act != A_YES)
1220 + err("FA: Forbidden access to file `%s'", namebuf);
1221 + }
1222 +
1223 + // Check syscall. If invalid, return -1, otherwise return the action mask.
1224 + static int
1225 + valid_syscall(struct syscall_args *a)
1226 + {
1227 + unsigned int sys = a->sys;
1228 + unsigned int act = (sys < NUM_ACTIONS) ? syscall_action[sys] : A_DEFAULT;
1229 +
1230 + if (act & A_LIBERAL)
1231 + {
1232 + if (filter_syscalls != 1)
1233 + act = A_DEFAULT;
1234 + }
1235 +
1236 + switch (act & A_ACTION_MASK)
1237 + {
1238 + case A_YES:
1239 + return act;
1240 + case A_NO:
1241 + return -1;
1242 + case A_FILENAME:
1243 + valid_filename(a->arg1);
1244 + return act;
1245 + default: ;
1246 + }
1247 +
1248 + switch (sys)
1249 + {
1250 + case __NR_kill:
1251 + if (a->arg1 == (arg_t) box_pid)
1252 + {
1253 + meta_printf("exitsig:%d\n", (int) a->arg2);
1254 + err("SG: Committed suicide by signal %d", (int) a->arg2);
1255 + }
1256 + return -1;
1257 + case __NR_tgkill:
1258 + if (a->arg1 == (arg_t) box_pid && a->arg2 == (arg_t) box_pid)
1259 + {
1260 + meta_printf("exitsig:%d\n", (int) a->arg3);
1261 + err("SG: Committed suicide by signal %d", (int) a->arg3);
1262 + }
1263 + return -1;
1264 + default:
1265 + return -1;
1266 + }
1267 + }
1268 +
1269 + static void
1270 + signal_alarm(int unused UNUSED)
1271 + {
1272 + /* Time limit checks are synchronous, so we only schedule them there. */
1273 + timer_tick = 1;
1274 + alarm(1);
1275 + }
1276 +
1277 + static void
1278 + signal_int(int unused UNUSED)
1279 + {
1280 + /* Interrupts are fatal, so no synchronization requirements. */
1281 + meta_printf("exitsig:%d\n", SIGINT);
1282 + err("SG: Interrupted");
1283 + }
1284 +
1285 + #define PROC_BUF_SIZE 4096
1286 + static void
1287 + read_proc_file(char *buf, char *name, int *fdp)
1288 + {
1289 + int c;
1290 +
1291 + if (!*fdp)
1292 + {
1293 + sprintf(buf, "/proc/%d/%s", (int) box_pid, name);
1294 + *fdp = open(buf, O_RDONLY);
1295 + if (*fdp < 0)
1296 + die("open(%s): %m", buf);
1297 + }
1298 + lseek(*fdp, 0, SEEK_SET);
1299 + if ((c = read(*fdp, buf, PROC_BUF_SIZE-1)) < 0)
1300 + die("read on /proc/$pid/%s: %m", name);
1301 + if (c >= PROC_BUF_SIZE-1)
1302 + die("/proc/$pid/%s too long", name);
1303 + buf[c] = 0;
1304 + }
1305 +
1306 + static void
1307 + check_timeout(void)
1308 + {
1309 + if (wall_timeout)
1310 + {
1311 + struct timeval now, wall;
1312 + int wall_ms;
1313 + gettimeofday(&now, NULL);
1314 + timersub(&now, &start_time, &wall);
1315 + wall_ms = wall.tv_sec*1000 + wall.tv_usec/1000;
1316 + if (wall_ms > wall_timeout)
1317 + err("TO: Time limit exceeded (wall clock)");
1318 + if (verbose > 1)
1319 + fprintf(stderr, "[wall time check: %d msec]\n", wall_ms);
1320 + }
1321 + if (timeout)
1322 + {
1323 + char buf[PROC_BUF_SIZE], *x;
1324 + int utime, stime, ms;
1325 + static int proc_stat_fd;
1326 + read_proc_file(buf, "stat", &proc_stat_fd);
1327 + x = buf;
1328 + while (*x && *x != ' ')
1329 + x++;
1330 + while (*x == ' ')
1331 + x++;
1332 + if (*x++ != '(')
1333 + die("proc stat syntax error 1");
1334 + while (*x && (*x != ')' || x[1] != ' '))
1335 + x++;
1336 + while (*x == ')' || *x == ' ')
1337 + x++;
1338 + if (sscanf(x, "%*c %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d %d %d", &utime, &stime) != 2)
1339 + die("proc stat syntax error 2");
1340 + ms = (utime + stime) * 1000 / ticks_per_sec;
1341 + if (verbose > 1)
1342 + fprintf(stderr, "[time check: %d msec]\n", ms);
1343 + if (ms > timeout && ms > extra_timeout)
1344 + err("TO: Time limit exceeded");
1345 + }
1346 + }
1347 +
1348 + static void
1349 + sample_mem_peak(void)
1350 + {
1351 + /*
1352 + * We want to find out the peak memory usage of the process, which is
1353 + * maintained by the kernel, but unforunately it gets lost when the
1354 + * process exits (it is not reported in struct rusage). Therefore we
1355 + * have to sample it whenever we suspect that the process is about
1356 + * to exit.
1357 + */
1358 + char buf[PROC_BUF_SIZE], *x;
1359 + static int proc_status_fd;
1360 + read_proc_file(buf, "status", &proc_status_fd);
1361 +
1362 + x = buf;
1363 + while (*x)
1364 + {
1365 + char *key = x;
1366 + while (*x && *x != ':' && *x != '\n')
1367 + x++;
1368 + if (!*x || *x == '\n')
1369 + break;
1370 + *x++ = 0;
1371 + while (*x == ' ' || *x == '\t')
1372 + x++;
1373 +
1374 + char *val = x;
1375 + while (*x && *x != '\n')
1376 + x++;
1377 + if (!*x)
1378 + break;
1379 + *x++ = 0;
1380 +
1381 + if (!strcmp(key, "VmPeak"))
1382 + {
1383 + int peak = atoi(val);
1384 + if (peak > mem_peak_kb)
1385 + mem_peak_kb = peak;
1386 + }
1387 + }
1388 +
1389 + if (verbose > 1)
1390 + msg("[mem-peak: %u KB]\n", mem_peak_kb);
1391 + }
1392 +
1393 + static void
1394 + boxkeeper(void)
1395 + {
1396 + int syscall_count = (filter_syscalls ? 0 : 1);
1397 + struct sigaction sa;
1398 +
1399 + is_ptraced = 1;
1400 +
1401 + bzero(&sa, sizeof(sa));
1402 + sa.sa_handler = signal_int;
1403 + sigaction(SIGINT, &sa, NULL);
1404 +
1405 + gettimeofday(&start_time, NULL);
1406 + ticks_per_sec = sysconf(_SC_CLK_TCK);
1407 + if (ticks_per_sec <= 0)
1408 + die("Invalid ticks_per_sec!");
1409 +
1410 + if (timeout || wall_timeout)
1411 + {
1412 + sa.sa_handler = signal_alarm;
1413 + sigaction(SIGALRM, &sa, NULL);
1414 + alarm(1);
1415 + }
1416 +
1417 + for(;;)
1418 + {
1419 + struct rusage rus;
1420 + int stat;
1421 + pid_t p;
1422 + if (timer_tick)
1423 + {
1424 + check_timeout();
1425 + timer_tick = 0;
1426 + }
1427 + p = wait4(box_pid, &stat, WUNTRACED, &rus);
1428 + if (p < 0)
1429 + {
1430 + if (errno == EINTR)
1431 + continue;
1432 + die("wait4: %m");
1433 + }
1434 + if (p != box_pid)
1435 + die("wait4: unknown pid %d exited!", p);
1436 + if (WIFEXITED(stat))
1437 + {
1438 + box_pid = 0;
1439 + final_stats(&rus);
1440 + if (WEXITSTATUS(stat))
1441 + {
1442 + if (syscall_count)
1443 + {
1444 + meta_printf("exitcode:%d\n", WEXITSTATUS(stat));
1445 + err("RE: Exited with error status %d", WEXITSTATUS(stat));
1446 + }
1447 + else
1448 + {
1449 + // Internal error happened inside the child process and it has been already reported.
1450 + box_exit(2);
1451 + }
1452 + }
1453 + if (timeout && total_ms > timeout)
1454 + err("TO: Time limit exceeded");
1455 + if (wall_timeout && wall_ms > wall_timeout)
1456 + err("TO: Time limit exceeded (wall clock)");
1457 + flush_line();
1458 + fprintf(stderr,"OK\n");
1459 + print_running_stat(
1460 + (double)wall_ms/1000,
1461 + (double)total_ms/1000,
1462 + (double)sys_ms/1000,
1463 + (mem_peak_kb + 1023) / 1024);
1464 + box_exit(0);
1465 + }
1466 + if (WIFSIGNALED(stat))
1467 + {
1468 + box_pid = 0;
1469 + meta_printf("exitsig:%d\n", WTERMSIG(stat));
1470 + final_stats(&rus);
1471 + err("SG: Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
1472 + }
1473 + if (WIFSTOPPED(stat))
1474 + {
1475 + int sig = WSTOPSIG(stat);
1476 + if (sig == SIGTRAP)
1477 + {
1478 + if (verbose > 2)
1479 + msg("[ptrace status %08x] ", stat);
1480 + static int stop_count;
1481 + if (!stop_count++) /* Traceme request */
1482 + msg(">> Traceme request caught\n");
1483 + else
1484 + err("SG: Breakpoint");
1485 + ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1486 + }
1487 + else if (sig == (SIGTRAP | 0x80))
1488 + {
1489 + if (verbose > 2)
1490 + msg("[ptrace status %08x] ", stat);
1491 + struct syscall_args a;
1492 + static unsigned int sys_tick, last_act;
1493 + static arg_t last_sys;
1494 + if (++sys_tick & 1) /* Syscall entry */
1495 + {
1496 + char namebuf[32];
1497 + int act;
1498 +
1499 + get_syscall_args(&a, 0);
1500 + arg_t sys = a.sys;
1501 + msg(">> Syscall %-12s (%08jx,%08jx,%08jx) ", syscall_name(sys, namebuf), (intmax_t) a.arg1, (intmax_t) a.arg2, (intmax_t) a.arg3);
1502 + if (!exec_seen)
1503 + {
1504 + msg("[master] ");
1505 + if (sys == NATIVE_NR_execve)
1506 + {
1507 + exec_seen = 1;
1508 + close_user_mem();
1509 + }
1510 + }
1511 + else if ((act = valid_syscall(&a)) >= 0)
1512 + {
1513 + last_act = act;
1514 + syscall_count++;
1515 + if (act & A_SAMPLE_MEM)
1516 + sample_mem_peak();
1517 + }
1518 + else
1519 + {
1520 + /*
1521 + * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
1522 + * so we have to change it to something harmless (e.g., an undefined
1523 + * syscall) and make the program continue.
1524 + */
1525 + set_syscall_nr(&a, ~(arg_t)0);
1526 + err("FO: Forbidden syscall %s", syscall_name(sys, namebuf));
1527 + }
1528 + last_sys = sys;
1529 + }
1530 + else /* Syscall return */
1531 + {
1532 + get_syscall_args(&a, 1);
1533 + if (a.sys == ~(arg_t)0)
1534 + {
1535 + /* Some syscalls (sigreturn et al.) do not return a value */
1536 + if (!(last_act & A_NO_RETVAL))
1537 + err("XX: Syscall does not return, but it should");
1538 + }
1539 + else
1540 + {
1541 + if (a.sys != last_sys)
1542 + err("XX: Mismatched syscall entry/exit");
1543 + }
1544 + if (last_act & A_NO_RETVAL)
1545 + msg("= ?\n");
1546 + else
1547 + msg("= %jd\n", (intmax_t) a.result);
1548 + }
1549 + ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1550 + }
1551 + else if (sig == SIGSTOP)
1552 + {
1553 + msg(">> SIGSTOP\n");
1554 + if (ptrace(PTRACE_SETOPTIONS, box_pid, NULL, (void *) PTRACE_O_TRACESYSGOOD) < 0)
1555 + die("ptrace(PTRACE_SETOPTIONS): %m");
1556 + ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1557 + }
1558 + else if (sig != SIGXCPU && sig != SIGXFSZ)
1559 + {
1560 + msg(">> Signal %d\n", sig);
1561 + sample_mem_peak(); /* Signal might be fatal, so update mem-peak */
1562 + ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
1563 + }
1564 + else
1565 + {
1566 + meta_printf("exitsig:%d", sig);
1567 + err("SG: Received signal %d", sig);
1568 + }
1569 + }
1570 + else
1571 + die("wait4: unknown status %x, giving up!", stat);
1572 + }
1573 + }
1574 +
1575 + static void
1576 + box_inside(int argc, char **argv)
1577 + {
1578 + struct rlimit rl;
1579 + char *args[argc+1];
1580 +
1581 + memcpy(args, argv, argc * sizeof(char *));
1582 + args[argc] = NULL;
1583 + if (set_cwd && chdir(set_cwd))
1584 + die("chdir: %m");
1585 + if (redir_stdin)
1586 + {
1587 + close(0);
1588 + if (open(redir_stdin, O_RDONLY) != 0)
1589 + die("open(\"%s\"): %m", redir_stdin);
1590 + }
1591 + if (redir_stdout)
1592 + {
1593 + close(1);
1594 + if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
1595 + die("open(\"%s\"): %m", redir_stdout);
1596 + }
1597 + if (redir_stderr)
1598 + {
1599 + close(2);
1600 + if (open(redir_stderr, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 2)
1601 + die("open(\"%s\"): %m", redir_stderr);
1602 + }
1603 + else
1604 + dup2(1, 2);
1605 + setpgrp();
1606 +
1607 + if (memory_limit)
1608 + {
1609 + rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
1610 + if (setrlimit(RLIMIT_AS, &rl) < 0)
1611 + die("setrlimit(RLIMIT_AS): %m");
1612 + }
1613 +
1614 + rl.rlim_cur = rl.rlim_max = (stack_limit ? (rlim_t)stack_limit * 1024 : RLIM_INFINITY);
1615 + if (setrlimit(RLIMIT_STACK, &rl) < 0)
1616 + die("setrlimit(RLIMIT_STACK): %m");
1617 +
1618 + rl.rlim_cur = rl.rlim_max = 64;
1619 + if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
1620 + die("setrlimit(RLIMIT_NOFILE): %m");
1621 +
1622 + char **env = setup_environment();
1623 + if (filter_syscalls)
1624 + {
1625 + if (ptrace(PTRACE_TRACEME) < 0)
1626 + die("ptrace(PTRACE_TRACEME): %m");
1627 + /* Trick: Make sure that we are stopped until the boxkeeper wakes up. */
1628 + raise(SIGSTOP);
1629 + }
1630 + execve(args[0], args, env);
1631 + die("execve(\"%s\"): %m", args[0]);
1632 + }
1633 +
1634 + static void
1635 + usage(void)
1636 + {
1637 + fprintf(stderr, "Invalid arguments!\n");
1638 + printf("\
1639 + Usage: box [<options>] -- <command> <arguments>\n\
1640 + \n\
1641 + Options:\n\
1642 + -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
1643 + -c <dir>\tChange directory to <dir> first\n\
1644 + -e\t\tInherit full environment of the parent process\n\
1645 + -E <var>\tInherit the environment variable <var> from the parent process\n\
1646 + -E <var>=<val>\tSet the environment variable <var> to <val>; unset it if <var> is empty\n\
1647 + -f\t\tFilter system calls (-ff=very restricted)\n\
1648 + -i <file>\tRedirect stdin from <file>\n\
1649 + -k <size>\tLimit stack size to <size> KB (default: 0=unlimited)\n\
1650 + -m <size>\tLimit address space to <size> KB\n\
1651 + -M <file>\tOutput process information to <file> (name:value)\n\
1652 + -o <file>\tRedirect stdout to <file>\n\
1653 + -p <path>\tPermit access to the specified path (or subtree if it ends with a `/')\n\
1654 + -p <path>=<act>\tDefine action for the specified path (<act>=yes/no)\n\
1655 + -r <file>\tRedirect stderr to <file>\n\
1656 + -s <sys>\tPermit the specified syscall (be careful)\n\
1657 + -s <sys>=<act>\tDefine action for the specified syscall (<act>=yes/no/file)\n\
1658 + -t <time>\tSet run time limit (seconds, fractions allowed)\n\
1659 + -T\t\tAllow syscalls for measuring run time\n\
1660 + -v\t\tBe verbose (use multiple times for even more verbosity)\n\
1661 + -w <time>\tSet wall clock time limit (seconds, fractions allowed)\n\
1662 + -x <time>\tSet extra timeout, before which a timing-out program is not yet killed,\n\
1663 + \t\tso that its real execution time is reported (seconds, fractions allowed)\n\
1664 + ");
1665 + exit(2);
1666 + }
1667 +
1668 + int
1669 + main(int argc, char **argv)
1670 + {
1671 + int c;
1672 + uid_t uid;
1673 +
1674 + while ((c = getopt(argc, argv, "a:c:eE:fi:k:m:M:o:p:r:s:t:Tvw:x:")) >= 0)
1675 + switch (c)
1676 + {
1677 + case 'a':
1678 + file_access = atol(optarg);
1679 + break;
1680 + case 'c':
1681 + set_cwd = optarg;
1682 + break;
1683 + case 'e':
1684 + pass_environ = 1;
1685 + break;
1686 + case 'E':
1687 + if (!set_env_action(optarg))
1688 + usage();
1689 + break;
1690 + case 'f':
1691 + filter_syscalls++;
1692 + break;
1693 + case 'k':
1694 + stack_limit = atol(optarg);
1695 + break;
1696 + case 'i':
1697 + redir_stdin = optarg;
1698 + break;
1699 + case 'm':
1700 + memory_limit = atol(optarg);
1701 + break;
1702 + case 'M':
1703 + meta_open(optarg);
1704 + break;
1705 + case 'o':
1706 + redir_stdout = optarg;
1707 + break;
1708 + case 'p':
1709 + if (!set_path_action(optarg))
1710 + usage();
1711 + break;
1712 + case 'r':
1713 + redir_stderr = optarg;
1714 + break;
1715 + case 's':
1716 + if (!set_syscall_action(optarg))
1717 + usage();
1718 + break;
1719 + case 't':
1720 + timeout = 1000*atof(optarg);
1721 + break;
1722 + case 'T':
1723 + syscall_action[__NR_times] = A_YES;
1724 + break;
1725 + case 'v':
1726 + verbose++;
1727 + break;
1728 + case 'w':
1729 + wall_timeout = 1000*atof(optarg);
1730 + break;
1731 + case 'x':
1732 + extra_timeout = 1000*atof(optarg);
1733 + break;
1734 + default:
1735 + usage();
1736 + }
1737 + if (optind >= argc)
1738 + usage();
1739 +
1740 + sanity_check();
1741 + uid = geteuid();
1742 + if (setreuid(uid, uid) < 0)
1743 + die("setreuid: %m");
1744 + box_pid = fork();
1745 + if (box_pid < 0)
1746 + die("fork: %m");
1747 + if (!box_pid)
1748 + box_inside(argc-optind, argv+optind);
1749 + else
1750 + boxkeeper();
1751 + die("Internal error: fell over edge of the world");
1752 + }
@@ -1,144 +1,165
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 require 'fileutils'
3 require 'fileutils'
4
4
5 ##############################
5 ##############################
6 #
6 #
7 # Standard Compile Script
7 # Standard Compile Script
8 #
8 #
9 # Supported compilers:
9 # Supported compilers:
10 # gcc, g++, and fpc.
10 # gcc, g++, and fpc.
11 #
11 #
12 ##############################
12 ##############################
13
13
14 def talk(str='')
14 def talk(str='')
15 if ENV['TALKATIVE']!=nil
15 if ENV['TALKATIVE']!=nil
16 puts str
16 puts str
17 end
17 end
18 if ENV['GRADER_LOGGING']!=nil
18 if ENV['GRADER_LOGGING']!=nil
19 log_fname = ENV['GRADER_LOGGING']
19 log_fname = ENV['GRADER_LOGGING']
20 fp = File.open(log_fname,"a")
20 fp = File.open(log_fname,"a")
21 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
21 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
22 fp.close
22 fp.close
23 end
23 end
24 end
24 end
25
25
26 C_COMPILER = "/usr/bin/gcc"
26 C_COMPILER = "/usr/bin/gcc"
27 CPLUSPLUS_COMPILER = "/usr/bin/g++"
27 CPLUSPLUS_COMPILER = "/usr/bin/g++"
28 PASCAL_COMPILER = "/usr/bin/fpc"
28 PASCAL_COMPILER = "/usr/bin/fpc"
29 JAVA_COMPILER = "/usr/bin/javac"
29 JAVA_COMPILER = "/usr/bin/javac"
30 - RUBY_INTEPRETER = "/home/dae/.rvm/rubies/ruby-1.9.2-p320/bin/ruby"
30 + RUBY_INTERPRETER = "/usr/bin/ruby"
31 + PYTHON_INTERPRETER = "/usr/bin/python"
32 + PYTHON_CHECKER = "/usr/bin/pyflakes"
31
33
32 C_OPTIONS = "-O2 -s -static -std=c99 -DCONTEST -lm -Wall"
34 C_OPTIONS = "-O2 -s -static -std=c99 -DCONTEST -lm -Wall"
33 CPLUSPLUS_OPTIONS = "-O2 -s -static -DCONTEST -lm -Wall"
35 CPLUSPLUS_OPTIONS = "-O2 -s -static -DCONTEST -lm -Wall"
34 PASCAL_OPTIONS = "-O1 -XS -dCONTEST"
36 PASCAL_OPTIONS = "-O1 -XS -dCONTEST"
35 JAVA_OPTIONS = ""
37 JAVA_OPTIONS = ""
38 + PYTHON_OPTIONS = ""
36
39
37 # Check for the correct number of arguments. Otherwise, print usage.
40 # Check for the correct number of arguments. Otherwise, print usage.
38 if ARGV.length == 0 or ARGV.length > 4
41 if ARGV.length == 0 or ARGV.length > 4
39 puts "Usage: compile <language> [<source-file>] [<output-file>] [<message-file>]"
42 puts "Usage: compile <language> [<source-file>] [<output-file>] [<message-file>]"
40 puts
43 puts
41 puts "<source-file> is defaulted to \"source\"."
44 puts "<source-file> is defaulted to \"source\"."
42 puts "<output-file> is defaulted to \"a.out\"."
45 puts "<output-file> is defaulted to \"a.out\"."
43 puts "<message-file> is defaulted to \"compiler_message\"."
46 puts "<message-file> is defaulted to \"compiler_message\"."
44 puts
47 puts
45 exit(127)
48 exit(127)
46 end
49 end
47
50
48 PARAMS = {
51 PARAMS = {
49 :source_file => [1,'source'],
52 :source_file => [1,'source'],
50 :output_file => [2,'a.out'],
53 :output_file => [2,'a.out'],
51 :message_file => [3,'compiler_message']
54 :message_file => [3,'compiler_message']
52 }
55 }
53
56
54 params = {}
57 params = {}
55 params[:prog_lang] = ARGV[0]
58 params[:prog_lang] = ARGV[0]
56 PARAMS.each_key do |param_name|
59 PARAMS.each_key do |param_name|
57 index, default = PARAMS[param_name]
60 index, default = PARAMS[param_name]
58 if ARGV.length > index
61 if ARGV.length > index
59 params[param_name] = ARGV[index]
62 params[param_name] = ARGV[index]
60 else
63 else
61 params[param_name] = default
64 params[param_name] = default
62 end
65 end
63 talk "#{param_name}: #{params[param_name]}"
66 talk "#{param_name}: #{params[param_name]}"
64 end
67 end
65
68
66 # Remove any remaining output files or message files.
69 # Remove any remaining output files or message files.
67 if FileTest.exists? params[:output_file]
70 if FileTest.exists? params[:output_file]
68 FileUtils.rm(params[:output_file])
71 FileUtils.rm(params[:output_file])
69 end
72 end
70 if FileTest.exists? params[:message_file]
73 if FileTest.exists? params[:message_file]
71 FileUtils.rm(params[:message_file])
74 FileUtils.rm(params[:message_file])
72 end
75 end
73
76
74 # Check if the source file exists before attempt compiling.
77 # Check if the source file exists before attempt compiling.
75 if !FileTest.exists? params[:source_file]
78 if !FileTest.exists? params[:source_file]
76 talk("ERROR: The source file does not exist!")
79 talk("ERROR: The source file does not exist!")
77 open(params[:message_file],"w") do |f|
80 open(params[:message_file],"w") do |f|
78 f.puts "ERROR: The source file did not exist."
81 f.puts "ERROR: The source file did not exist."
79 end
82 end
80 exit(127)
83 exit(127)
81 end
84 end
82
85
83 if params[:prog_lang]=='cpp'
86 if params[:prog_lang]=='cpp'
84 params[:prog_lang] = 'c++'
87 params[:prog_lang] = 'c++'
85 end
88 end
86
89
87 # Compile.
90 # Compile.
88 case params[:prog_lang]
91 case params[:prog_lang]
89
92
90 - when "c"
93 + when "c"
91 command = "#{C_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{C_OPTIONS} 2> #{params[:message_file]}"
94 command = "#{C_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{C_OPTIONS} 2> #{params[:message_file]}"
92 system(command)
95 system(command)
93
96
94 - when "c++"
97 + when "c++"
95 command = "#{CPLUSPLUS_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{CPLUSPLUS_OPTIONS} 2> #{params[:message_file]}"
98 command = "#{CPLUSPLUS_COMPILER} #{params[:source_file]} -o #{params[:output_file]} #{CPLUSPLUS_OPTIONS} 2> #{params[:message_file]}"
96 system(command)
99 system(command)
97
100
98 - when "pas"
101 + when "pas"
99 command = "#{PASCAL_COMPILER} #{params[:source_file]} -ooutpas #{PASCAL_OPTIONS} > #{params[:message_file]}"
102 command = "#{PASCAL_COMPILER} #{params[:source_file]} -ooutpas #{PASCAL_OPTIONS} > #{params[:message_file]}"
100 system(command)
103 system(command)
101 FileUtils.mv("output", params[:output_file])
104 FileUtils.mv("output", params[:output_file])
102
105
103 - when "java"
106 + when "java"
104 #rename the file to the public class name
107 #rename the file to the public class name
105
108
106 #get the class name
109 #get the class name
107 classname = 'DUMMY'
110 classname = 'DUMMY'
108 File.foreach(params[:source_file]) do |line|
111 File.foreach(params[:source_file]) do |line|
109 md = /\s*public\s*class\s*(\w*)/.match(line)
112 md = /\s*public\s*class\s*(\w*)/.match(line)
110 classname=md[1] if md
113 classname=md[1] if md
111 end
114 end
112 system("cp #{params[:source_file]} #{classname}.java")
115 system("cp #{params[:source_file]} #{classname}.java")
113 command = "#{JAVA_COMPILER} #{classname}.java 2> #{params[:message_file]}"
116 command = "#{JAVA_COMPILER} #{classname}.java 2> #{params[:message_file]}"
114 system(command)
117 system(command)
115 if File.exists?(classname + ".class")
118 if File.exists?(classname + ".class")
116 - File.open(params[:output_file],"w") {|file| file.write("#!/bin/sh\n/usr/bin/java #{classname}\n")}
119 + File.open(params[:output_file],"w") {|file| file.write("#{classname}")}
120 + end
121 + if classname == 'DUMMY'
122 + File.open(params[:message_file],"w") {|file| file.write("Cannot find any public class in the source code\n")}
123 + end
124 +
125 + when "ruby"
126 + command = "#{RUBY_INTERPRETER} -c #{params[:source_file]} 2> #{params[:message_file]}"
127 + if system(command)
128 + File.open(params[:output_file],"w") do |out_file|
129 + out_file.puts "#!#{RUBY_INTERPRETER}"
130 + File.open(params[:source_file],"r").each do |line|
131 + out_file.print line
132 + end
133 + end
117 File.chmod(0755, params[:output_file])
134 File.chmod(0755, params[:output_file])
118 end
135 end
119
136
120 - when "ruby"
137 + when "python"
121 - command = "#{RUBY_INTEPRETER} -c #{params[:source_file]} > #{params[:message_file]}"
138 + command = "#{PYTHON_CHECKER} #{params[:source_file]} > #{params[:message_file]}"
122 - system(command)
139 + if system(command)
123 - File.open(params[:output_file],"w") do |out_file|
140 + #compile to python bytecode
124 - out_file.puts "#!#{RUBY_INTEPRETER}"
141 + command = "#{PYTHON_INTERPRETER} -m py_compile #{params[:source_file]}"
125 - File.open(params[:source_file],"r").each do |line|
142 + puts "compile: #{command}"
126 - out_file.print line
143 + system(command)
144 + puts "pwd: " + Dir.pwd
145 + Dir.new('.').each {|file| puts file}
146 + File.open(params[:output_file],"w") do |out_file|
147 + out_file.puts "#!#{PYTHON_INTERPRETER} #{params[:source_file]}c"
127 end
148 end
149 + File.chmod(0755, params[:output_file])
128 end
150 end
129 - File.chmod(0755, params[:output_file])
130
151
131 - else
152 + else
132 talk("ERROR: Invalid language specified!")
153 talk("ERROR: Invalid language specified!")
133 open(params[:message_file],"w") do |f|
154 open(params[:message_file],"w") do |f|
134 f.puts "ERROR: Invalid language specified!"
155 f.puts "ERROR: Invalid language specified!"
135 end
156 end
136 exit(127)
157 exit(127)
137 end
158 end
138
159
139 # Report success or failure.
160 # Report success or failure.
140 if FileTest.exists? params[:output_file]
161 if FileTest.exists? params[:output_file]
141 talk "Compilation was successful!"
162 talk "Compilation was successful!"
142 else
163 else
143 talk "ERROR: Something was wrong during the compilation!"
164 talk "ERROR: Something was wrong during the compilation!"
144 end
165 end
@@ -1,106 +1,108
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 CORRECT_MARK = 'P'
3 CORRECT_MARK = 'P'
4 INCORRECT_MARK = '-'
4 INCORRECT_MARK = '-'
5 TIMEOUT_MARK = 'T'
5 TIMEOUT_MARK = 'T'
6 RUN_ERROR_MARK = 'x'
6 RUN_ERROR_MARK = 'x'
7
7
8 def log(str='')
8 def log(str='')
9 if ENV['TALKATIVE']!=nil
9 if ENV['TALKATIVE']!=nil
10 puts str
10 puts str
11 end
11 end
12 if ENV['GRADER_LOGGING']!=nil
12 if ENV['GRADER_LOGGING']!=nil
13 log_fname = ENV['GRADER_LOGGING']
13 log_fname = ENV['GRADER_LOGGING']
14 fp = File.open(log_fname,"a")
14 fp = File.open(log_fname,"a")
15 fp.puts("grade: #{Time.new.strftime("%H:%M")} #{str}")
15 fp.puts("grade: #{Time.new.strftime("%H:%M")} #{str}")
16 fp.close
16 fp.close
17 end
17 end
18 end
18 end
19
19
20 def char_comment(comment)
20 def char_comment(comment)
21 if comment =~ /[Ii]ncorrect/
21 if comment =~ /[Ii]ncorrect/
22 INCORRECT_MARK
22 INCORRECT_MARK
23 elsif comment =~ /[Cc]orrect/
23 elsif comment =~ /[Cc]orrect/
24 CORRECT_MARK
24 CORRECT_MARK
25 elsif comment =~ /[Tt]ime/
25 elsif comment =~ /[Tt]ime/
26 TIMEOUT_MARK
26 TIMEOUT_MARK
27 elsif res = /^[Cc]omment:(.*)$/.match(comment)
27 elsif res = /^[Cc]omment:(.*)$/.match(comment)
28 res[1]
28 res[1]
29 else
29 else
30 RUN_ERROR_MARK # these are run time errors
30 RUN_ERROR_MARK # these are run time errors
31 end
31 end
32 end
32 end
33
33
34 problem_home = ENV['PROBLEM_HOME']
34 problem_home = ENV['PROBLEM_HOME']
35 require "#{problem_home}/script/test_dsl.rb"
35 require "#{problem_home}/script/test_dsl.rb"
36 load "#{problem_home}/test_cases/all_tests.cfg"
36 load "#{problem_home}/test_cases/all_tests.cfg"
37 problem = Problem.get_instance
37 problem = Problem.get_instance
38
38
39 if problem.well_formed? == false
39 if problem.well_formed? == false
40 log "The problem specification is not well formed."
40 log "The problem specification is not well formed."
41 exit(127)
41 exit(127)
42 end
42 end
43
43
44 all_score = 0
44 all_score = 0
45 all_comment = ''
45 all_comment = ''
46 (1..(problem.runs.length-1)).each do |k|
46 (1..(problem.runs.length-1)).each do |k|
47 log "grade run #{k}"
47 log "grade run #{k}"
48 run = problem.runs[k]
48 run = problem.runs[k]
49 run_score = nil
49 run_score = nil
50 run_comment = ''
50 run_comment = ''
51 run_comment_short = ''
51 run_comment_short = ''
52 run.tests.each do |test_num|
52 run.tests.each do |test_num|
53 result_file_name = "#{test_num}/result"
53 result_file_name = "#{test_num}/result"
54 if not File.exists?(result_file_name)
54 if not File.exists?(result_file_name)
55 run_comment += "result file for test #{test_num} not found\n"
55 run_comment += "result file for test #{test_num} not found\n"
56 run_comment_short += RUN_ERROR_MARK
56 run_comment_short += RUN_ERROR_MARK
57 log "Cannot find the file #{test_num}/result!"
57 log "Cannot find the file #{test_num}/result!"
58 else
58 else
59 result_file = File.new(result_file_name, "r")
59 result_file = File.new(result_file_name, "r")
60 result_file_lines = result_file.readlines
60 result_file_lines = result_file.readlines
61 if result_file_lines.length>=2
61 if result_file_lines.length>=2
62 current_run_score = result_file_lines[1].to_i
62 current_run_score = result_file_lines[1].to_i
63 run_comment += result_file_lines[0]
63 run_comment += result_file_lines[0]
64 run_comment_short += char_comment(result_file_lines[0].chomp)
64 run_comment_short += char_comment(result_file_lines[0].chomp)
65 else
65 else
66 current_run_score = 0
66 current_run_score = 0
67 run_comment += "result file for test #{test_num} error\n"
67 run_comment += "result file for test #{test_num} error\n"
68 run_comment_short += RUN_ERROR_MARK
68 run_comment_short += RUN_ERROR_MARK
69 log "Error in #{test_num}/result!"
69 log "Error in #{test_num}/result!"
70 end
70 end
71
71
72 # the score of this run should be the minimum of the score for
72 # the score of this run should be the minimum of the score for
73 # each test case
73 # each test case
74 if (run_score==nil) or (run_score>current_run_score)
74 if (run_score==nil) or (run_score>current_run_score)
75 run_score = current_run_score
75 run_score = current_run_score
76 end
76 end
77 result_file.close
77 result_file.close
78 end
78 end
79 end
79 end
80
80
81 run_result_file = File.new("result-#{k}", "w")
81 run_result_file = File.new("result-#{k}", "w")
82 run_result_file.write run_score
82 run_result_file.write run_score
83 run_result_file.write "\n"
83 run_result_file.write "\n"
84 run_result_file.close
84 run_result_file.close
85
85
86 run_comment_file = File.new("comment-#{k}", "w")
86 run_comment_file = File.new("comment-#{k}", "w")
87 run_comment_file.write "#{run_comment}\n"
87 run_comment_file.write "#{run_comment}\n"
88 run_comment_file.close
88 run_comment_file.close
89
89
90 all_score = all_score + run_score
90 all_score = all_score + run_score
91
91
92 # append comment for test run with many test cases
92 # append comment for test run with many test cases
93 if run.tests.length > 1
93 if run.tests.length > 1
94 run_comment_short = '[' + run_comment_short + ']'
94 run_comment_short = '[' + run_comment_short + ']'
95 end
95 end
96 all_comment += run_comment_short
96 all_comment += run_comment_short
97 end
97 end
98
98
99 result_file = File.new("result", "w")
99 result_file = File.new("result", "w")
100 result_file.write all_score
100 result_file.write all_score
101 result_file.write "\n"
101 result_file.write "\n"
102 result_file.close
102 result_file.close
103
103
104 comment_file = File.new("comment", "w")
104 comment_file = File.new("comment", "w")
105 comment_file.write "#{all_comment}\n"
105 comment_file.write "#{all_comment}\n"
106 comment_file.close
106 comment_file.close
107 +
108 + log "score = #{all_score} comment = #{all_comment}"
@@ -1,177 +1,179
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 require 'fileutils'
3 require 'fileutils'
4
4
5 def log(str='')
5 def log(str='')
6 if ENV['TALKATIVE']!=nil
6 if ENV['TALKATIVE']!=nil
7 puts str
7 puts str
8 end
8 end
9 if ENV['GRADER_LOGGING']!=nil
9 if ENV['GRADER_LOGGING']!=nil
10 log_fname = ENV['GRADER_LOGGING']
10 log_fname = ENV['GRADER_LOGGING']
11 fp = File.open(log_fname,"a")
11 fp = File.open(log_fname,"a")
12 fp.puts("judge: #{Time.new.strftime("%H:%M")} #{str}")
12 fp.puts("judge: #{Time.new.strftime("%H:%M")} #{str}")
13 fp.close
13 fp.close
14 end
14 end
15 end
15 end
16
16
17 problem_home = ENV['PROBLEM_HOME']
17 problem_home = ENV['PROBLEM_HOME']
18
18
19 def execute(command, error_message="")
19 def execute(command, error_message="")
20 if not system(command)
20 if not system(command)
21 msg = "ERROR: #{error_message}"
21 msg = "ERROR: #{error_message}"
22 log msg
22 log msg
23 raise(msg)
23 raise(msg)
24 end
24 end
25 end
25 end
26
26
27 def call_and_log(error_message)
27 def call_and_log(error_message)
28 begin
28 begin
29 yield
29 yield
30 rescue
30 rescue
31 msg = "ERROR: #{error_message}"
31 msg = "ERROR: #{error_message}"
32 log msg
32 log msg
33 raise msg
33 raise msg
34 end
34 end
35 end
35 end
36
36
37 def clear_and_create_empty_dir(dir)
37 def clear_and_create_empty_dir(dir)
38 FileUtils.rm_rf(dir, :secure => true)
38 FileUtils.rm_rf(dir, :secure => true)
39 call_and_log("Cannot make directory #{dir}.") { FileUtils.mkdir(dir) }
39 call_and_log("Cannot make directory #{dir}.") { FileUtils.mkdir(dir) }
40 end
40 end
41
41
42 # ARGV[0] --- language
42 # ARGV[0] --- language
43 # ARGV[1] --- program source file
43 # ARGV[1] --- program source file
44 # ARGV[2] --- test result directory
44 # ARGV[2] --- test result directory
45 # ARGV[3] --- sandbox directory
45 # ARGV[3] --- sandbox directory
46
46
47 if ARGV.length < 2 || ARGV.length > 4
47 if ARGV.length < 2 || ARGV.length > 4
48 puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]"
48 puts "Usage: judge <language> <program-source> [<test-result-directory>] [<sandbox-directory>]"
49 puts " <sandbox-directory> is defaulted to ./sandbox"
49 puts " <sandbox-directory> is defaulted to ./sandbox"
50 puts " <test-result-directory> is defaulted to ./test-result"
50 puts " <test-result-directory> is defaulted to ./test-result"
51 puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it."
51 puts "WARNING: The judge script will forcefully create the (implicitly and explicitly) specified directories and remove anything inside it."
52 exit(127)
52 exit(127)
53 end
53 end
54
54
55 language = ARGV[0]
55 language = ARGV[0]
56 - if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby"
56 + if language != "c" && language != "c++" && language != "pas" && language != "java" && language != "ruby" && language != "python"
57 log "You specified a language that is not supported: #{language}."
57 log "You specified a language that is not supported: #{language}."
58 exit(127)
58 exit(127)
59 end
59 end
60
60
61 source_file = ARGV[1]
61 source_file = ARGV[1]
62 if File.exist?(source_file) == false
62 if File.exist?(source_file) == false
63 log "The source file does not exist."
63 log "The source file does not exist."
64 exit(127)
64 exit(127)
65 end
65 end
66
66
67 log "Making test result and sandbox directories..."
67 log "Making test result and sandbox directories..."
68
68
69 current_dir = FileUtils.pwd
69 current_dir = FileUtils.pwd
70 current_dir.strip!
70 current_dir.strip!
71
71
72 if ARGV.length >= 3
72 if ARGV.length >= 3
73 test_result_dir = ARGV[2]
73 test_result_dir = ARGV[2]
74 else
74 else
75 test_result_dir = "#{current_dir}/test-result"
75 test_result_dir = "#{current_dir}/test-result"
76 end
76 end
77
77
78 log "Test result directory: #{test_result_dir}"
78 log "Test result directory: #{test_result_dir}"
79 clear_and_create_empty_dir(test_result_dir)
79 clear_and_create_empty_dir(test_result_dir)
80
80
81 if ARGV.length >= 4
81 if ARGV.length >= 4
82 sandbox_dir = ARGV[3]
82 sandbox_dir = ARGV[3]
83 else
83 else
84 sandbox_dir = "#{current_dir}/sandbox"
84 sandbox_dir = "#{current_dir}/sandbox"
85 end
85 end
86 log "Sandbox directory: #{sandbox_dir}"
86 log "Sandbox directory: #{sandbox_dir}"
87 clear_and_create_empty_dir(sandbox_dir)
87 clear_and_create_empty_dir(sandbox_dir)
88
88
89 # Compile
89 # Compile
90 log
90 log
91 log "Compiling..."
91 log "Compiling..."
92 call_and_log("Cannot copy the source file to #{sandbox_dir}") {
92 call_and_log("Cannot copy the source file to #{sandbox_dir}") {
93 FileUtils.cp(source_file, sandbox_dir)
93 FileUtils.cp(source_file, sandbox_dir)
94 }
94 }
95 begin
95 begin
96 Dir.chdir sandbox_dir
96 Dir.chdir sandbox_dir
97 rescue
97 rescue
98 log "ERROR: Cannot change directory to #{sandbox_dir}."
98 log "ERROR: Cannot change directory to #{sandbox_dir}."
99 exit(127)
99 exit(127)
100 end
100 end
101 execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!")
101 execute("#{problem_home}/script/compile #{language} #{source_file}", "Compilation error!")
102 compile_message = open("compiler_message").read
102 compile_message = open("compiler_message").read
103 compile_message.strip!
103 compile_message.strip!
104 call_and_log("Cannot move the compiler message to #{test_result_dir}.") {
104 call_and_log("Cannot move the compiler message to #{test_result_dir}.") {
105 FileUtils.mv("compiler_message", test_result_dir)
105 FileUtils.mv("compiler_message", test_result_dir)
106 }
106 }
107 if !FileTest.exist?("a.out")
107 if !FileTest.exist?("a.out")
108 log "Cannot compile the source code. See message in #{test_result_dir}/compile_message"
108 log "Cannot compile the source code. See message in #{test_result_dir}/compile_message"
109 exit(127)
109 exit(127)
110 else
110 else
111 call_and_log("Cannot move the compiled program to #{test_result_dir}") {
111 call_and_log("Cannot move the compiled program to #{test_result_dir}") {
112 FileUtils.mv("a.out",test_result_dir)
112 FileUtils.mv("a.out",test_result_dir)
113 if language == "java" then Dir["*.class"].each { |file| FileUtils.mv(file,test_result_dir)} end
113 if language == "java" then Dir["*.class"].each { |file| FileUtils.mv(file,test_result_dir)} end
114 + if language == "python" then Dir["*.pyc"].each { |file| FileUtils.mv(file,test_result_dir)} end
114 }
115 }
115 FileUtils.rm_rf("#{sandbox_dir}/.")
116 FileUtils.rm_rf("#{sandbox_dir}/.")
116 end
117 end
117
118
118 require "#{problem_home}/script/test_dsl.rb"
119 require "#{problem_home}/script/test_dsl.rb"
119 load "#{problem_home}/test_cases/all_tests.cfg"
120 load "#{problem_home}/test_cases/all_tests.cfg"
120 problem = Problem.get_instance
121 problem = Problem.get_instance
121
122
122 if problem.well_formed? == false
123 if problem.well_formed? == false
123 log "The problem specification is not well formed."
124 log "The problem specification is not well formed."
124 exit(127)
125 exit(127)
125 end
126 end
126
127
127 # Doing the testing.
128 # Doing the testing.
128 (1..(problem.num_tests)).each do |test_num|
129 (1..(problem.num_tests)).each do |test_num|
129
130
130 $stdout.print "[#{test_num}]"
131 $stdout.print "[#{test_num}]"
131 $stdout.flush
132 $stdout.flush
132
133
133 log "Test number: #{test_num}"
134 log "Test number: #{test_num}"
134
135
135 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
136 call_and_log("Cannot copy the compiled program into #{sandbox_dir}") {
136 FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir, :preserve => true)
137 FileUtils.cp("#{test_result_dir}/a.out", sandbox_dir, :preserve => true)
137 if language == "java" then Dir["#{test_result_dir}/*.class"].each { |file| FileUtils.cp(file,sandbox_dir)} end
138 if language == "java" then Dir["#{test_result_dir}/*.class"].each { |file| FileUtils.cp(file,sandbox_dir)} end
139 + if language == "python" then Dir["#{test_result_dir}/*.pyc"].each { |file| FileUtils.cp(file,sandbox_dir)} end
138 }
140 }
139
141
140 begin
142 begin
141 execute("#{problem_home}/script/run #{language} #{test_num}", "Error occured during execution of the run script")
143 execute("#{problem_home}/script/run #{language} #{test_num}", "Error occured during execution of the run script")
142 rescue
144 rescue
143 # do nothing
145 # do nothing
144 end
146 end
145
147
146 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
148 call_and_log("Cannot create directory #{test_result_dir}/#{test_num}") {
147 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
149 FileUtils.mkdir "#{test_result_dir}/#{test_num}"
148 }
150 }
149 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
151 call_and_log("Cannot copy the result file into #{test_result_dir}/#{test_num}") {
150 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
152 FileUtils.mv "#{sandbox_dir}/result", "#{test_result_dir}/#{test_num}"
151 }
153 }
152 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
154 call_and_log("Cannot copy the comment file into #{test_result_dir}/#{test_num}") {
153 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
155 FileUtils.mv "#{sandbox_dir}/comment", "#{test_result_dir}/#{test_num}"
154 }
156 }
155 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
157 call_and_log("Cannot copy the output file into #{test_result_dir}/#{test_num}") {
156 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
158 FileUtils.mv "#{sandbox_dir}/output.txt", "#{test_result_dir}/#{test_num}"
157 }
159 }
158 call_and_log("Cannot clear #{sandbox_dir}") {
160 call_and_log("Cannot clear #{sandbox_dir}") {
159 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
161 FileUtils.rm_rf(Dir.glob("#{sandbox_dir}/*"), :secure => true)
160 }
162 }
161 end
163 end
162
164
163 $stdout.print "[done]\n"
165 $stdout.print "[done]\n"
164
166
165 # Grade
167 # Grade
166 log
168 log
167 log "Grading..."
169 log "Grading..."
168 begin
170 begin
169 Dir.chdir test_result_dir
171 Dir.chdir test_result_dir
170 rescue
172 rescue
171 log "ERROR: Cannot change directory to #{test_result_dir}."
173 log "ERROR: Cannot change directory to #{test_result_dir}."
172 exit(127)
174 exit(127)
173 end
175 end
174 execute("#{problem_home}/script/grade", "An error occured during grading!")
176 execute("#{problem_home}/script/grade", "An error occured during grading!")
175
177
176 log
178 log
177 log "All done!"
179 log "All done!"
@@ -1,175 +1,181
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2
2
3 require 'fileutils'
3 require 'fileutils'
4
4
5 def log(str='')
5 def log(str='')
6 if ENV['TALKATIVE']!=nil
6 if ENV['TALKATIVE']!=nil
7 puts str
7 puts str
8 end
8 end
9 if ENV['GRADER_LOGGING']!=nil
9 if ENV['GRADER_LOGGING']!=nil
10 log_fname = ENV['GRADER_LOGGING']
10 log_fname = ENV['GRADER_LOGGING']
11 fp = File.open(log_fname,"a")
11 fp = File.open(log_fname,"a")
12 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
12 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
13 fp.close
13 fp.close
14 end
14 end
15 end
15 end
16
16
17 def extract_time(t)
17 def extract_time(t)
18 - #puts "TIME: #{t}"
18 + # puts "TIME: #{t}"
19 - #if (result=/^(.*)real(.*)wall(.*)s/.match(t))
19 + if (result=/^(.*)r(.*)u(.*)s/.match(t))
20 - if (result=/^OK \((.*) sec real\, (.*) sec wall,.*MB, (.*) syscalls/.match(t))
21 {:real => result[1], :user => result[2], :sys => result[3]}
20 {:real => result[1], :user => result[2], :sys => result[3]}
22 else
21 else
23 #{:real => 0, :user => 0, :sys => 0}
22 #{:real => 0, :user => 0, :sys => 0}
24 #puts "ERROR READING RUNNING TIME: #{t}"
23 #puts "ERROR READING RUNNING TIME: #{t}"
25 raise "Error reading running time: #{t}"
24 raise "Error reading running time: #{t}"
26 end
25 end
27 end
26 end
28
27
29 def compile_box(source,bin)
28 def compile_box(source,bin)
30 system("g++ #{source} -o #{bin}")
29 system("g++ #{source} -o #{bin}")
31 end
30 end
32
31
33 if ARGV.length < 2 || ARGV.length > 3
32 if ARGV.length < 2 || ARGV.length > 3
34 puts "Usage: run <language> <test-num> [<program-name>]"
33 puts "Usage: run <language> <test-num> [<program-name>]"
35 exit(127)
34 exit(127)
36 end
35 end
37
36
38 language = ARGV[0]
37 language = ARGV[0]
39 test_num = ARGV[1].to_i
38 test_num = ARGV[1].to_i
40 if ARGV.length > 2
39 if ARGV.length > 2
41 program_name = ARGV[2]
40 program_name = ARGV[2]
42 else
41 else
43 program_name = "a.out"
42 program_name = "a.out"
44 end
43 end
45
44
46 problem_home = ENV['PROBLEM_HOME']
45 problem_home = ENV['PROBLEM_HOME']
47 require "#{problem_home}/script/test_dsl.rb"
46 require "#{problem_home}/script/test_dsl.rb"
48 load "#{problem_home}/test_cases/all_tests.cfg"
47 load "#{problem_home}/test_cases/all_tests.cfg"
49 problem = Problem.get_instance
48 problem = Problem.get_instance
50
49
51 if problem.well_formed? == false
50 if problem.well_formed? == false
52 log "The problem specification is not well formed."
51 log "The problem specification is not well formed."
53 exit(127)
52 exit(127)
54 end
53 end
55
54
56 # Check if the test number is okay.
55 # Check if the test number is okay.
57 if test_num <= 0 || test_num > problem.num_tests
56 if test_num <= 0 || test_num > problem.num_tests
58 log "You have specified a wrong test number."
57 log "You have specified a wrong test number."
59 exit(127)
58 exit(127)
60 end
59 end
61
60
62 #####################################
61 #####################################
63 # Set the relavant file names here. #
62 # Set the relavant file names here. #
64 #####################################
63 #####################################
65
64
66 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
65 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
67
66
68 #####################################
67 #####################################
69
68
70 time_limit = problem.get_time_limit test_num
69 time_limit = problem.get_time_limit test_num
71 mem_limit = problem.get_mem_limit(test_num) * 1024
70 mem_limit = problem.get_mem_limit(test_num) * 1024
72
71
73 # Copy the input file.
72 # Copy the input file.
74 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
73 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
75
74
76 # check if box is there, if not, compile it!
75 # check if box is there, if not, compile it!
77 if !File.exists?("#{problem_home}/script/box")
76 if !File.exists?("#{problem_home}/script/box")
78 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
77 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
79 compile_box("#{problem_home}/script/box.cc",
78 compile_box("#{problem_home}/script/box.cc",
80 "#{problem_home}/script/box")
79 "#{problem_home}/script/box")
81 end
80 end
82
81
83 # Hide PROBLEM_HOME
82 # Hide PROBLEM_HOME
84 ENV['PROBLEM_HOME'] = nil
83 ENV['PROBLEM_HOME'] = nil
85
84
86 # Run the program.
85 # Run the program.
87 #run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box_new -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}"
86 #run_command = "/usr/bin/time -f \"#{time_output_format}\" 2>run_result #{problem_home}/script/box_new -a 2 -f -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name}"
88 #
87 #
89
88
90
89
91
90
92
91
93 case language
92 case language
94 when "java"
93 when "java"
95 - # for java, we have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
94 +
96 - run_command = "#{problem_home}/script/box -T -t #{time_limit} -s getppid -s clone -s wait4 -p /usr/bin/ -p ./ -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
95 + # for java, extract the classname
96 + # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
97 + classname = 'DUMMY'
98 + File.open(program_name,"r").each do |line|
99 + classname = line
100 + end
101 + run_command = "#{problem_home}/script/box -T -t #{time_limit} -s getppid -s clone -s wait4 -p /usr/bin/ -p ./ -i #{input_file_name} -o output.txt /usr/bin/java #{classname} 2>run_result"
97 when "ruby"
102 when "ruby"
98 run_command = "#{problem_home}/script/box -T -t #{time_limit} -s getppid -s wait4 -s clone -s set_robust_list -s futex -s sigaltstack -p /dev/urandom -p ./ -p /home/dae/.rvm/rubies/ruby-1.9.2-p320/ -p #{problem_home}/ -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
103 run_command = "#{problem_home}/script/box -T -t #{time_limit} -s getppid -s wait4 -s clone -s set_robust_list -s futex -s sigaltstack -p /dev/urandom -p ./ -p /home/dae/.rvm/rubies/ruby-1.9.2-p320/ -p #{problem_home}/ -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
99 - when "c++"
104 + when "python"
100 - run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
105 + #this code just run without any checking
101 - else
106 + run_command = "#{problem_home}/script/box -T -t #{time_limit} -p #{problem_home}/ -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
107 + else # for c++, pascal, we do the normal checking
102 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
108 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit} -m #{mem_limit} -i #{input_file_name} -o output.txt #{program_name} 2>run_result"
103 end
109 end
104
110
105
111
106 log "Running test #{test_num}..."
112 log "Running test #{test_num}..."
107 log run_command
113 log run_command
108 log
114 log
109 system(run_command)
115 system(run_command)
110
116
111 # Restore PROBLEM_HOME
117 # Restore PROBLEM_HOME
112 ENV['PROBLEM_HOME'] = problem_home
118 ENV['PROBLEM_HOME'] = problem_home
113
119
114 # Create the result file.
120 # Create the result file.
115 result_file = File.new("result", "w")
121 result_file = File.new("result", "w")
116 comment_file = File.new("comment", "w")
122 comment_file = File.new("comment", "w")
117
123
118 # Check if the program actually produced any output.
124 # Check if the program actually produced any output.
119 run_result_file = File.new("run_result", "r")
125 run_result_file = File.new("run_result", "r")
120 run_result = run_result_file.readlines
126 run_result = run_result_file.readlines
121 run_result_file.close
127 run_result_file.close
122
128
123 run_stat = run_result[run_result.length-1]
129 run_stat = run_result[run_result.length-1]
124 running_time = extract_time(run_stat)
130 running_time = extract_time(run_stat)
125
131
126 report = lambda{ |status, points, comment|
132 report = lambda{ |status, points, comment|
127 result_file.write status.strip
133 result_file.write status.strip
128 result_file.write "\n"
134 result_file.write "\n"
129 result_file.write points.to_s.strip
135 result_file.write points.to_s.strip
130 result_file.write "\n"
136 result_file.write "\n"
131 result_file.write run_stat.strip
137 result_file.write run_stat.strip
132 result_file.write "\n"
138 result_file.write "\n"
133 result_file.close
139 result_file.close
134 FileUtils.rm "run_result"
140 FileUtils.rm "run_result"
135 # `rm output.txt` --- keep the output
141 # `rm output.txt` --- keep the output
136
142
137 comment_file.write comment
143 comment_file.write comment
138
144
139 # added for debuggin --- jittat
145 # added for debuggin --- jittat
140 comment_file.write "--run-result--\n"
146 comment_file.write "--run-result--\n"
141 run_result.each do |l|
147 run_result.each do |l|
142 comment_file.write l
148 comment_file.write l
143 end
149 end
144
150
145 comment_file.close
151 comment_file.close
146
152
147 log "Done!"
153 log "Done!"
148 exit(0)
154 exit(0)
149 }
155 }
150
156
151 if run_result[0][0,2] != "OK"
157 if run_result[0][0,2] != "OK"
152 log "There was a runtime error."
158 log "There was a runtime error."
153 report.call(run_result[0], 0, "No comment.\n")
159 report.call(run_result[0], 0, "No comment.\n")
154 end
160 end
155
161
156 if running_time[:user].to_f > time_limit
162 if running_time[:user].to_f > time_limit
157 log "Time limit exceeded."
163 log "Time limit exceeded."
158 report.call("Time limit exceeded", 0, "No comment.\n")
164 report.call("Time limit exceeded", 0, "No comment.\n")
159 end
165 end
160
166
161 # Run 'check' to evaluate the output.
167 # Run 'check' to evaluate the output.
162 #puts "There was no runtime error. Proceed to checking the output."
168 #puts "There was no runtime error. Proceed to checking the output."
163 check_command = "#{problem_home}/script/check #{language} #{test_num}"
169 check_command = "#{problem_home}/script/check #{language} #{test_num}"
164 log "Checking the output..."
170 log "Checking the output..."
165 log check_command
171 log check_command
166 if not system(check_command)
172 if not system(check_command)
167 log "Problem with check script"
173 log "Problem with check script"
168 report.call("Incorrect",0,"Check script error.\n")
174 report.call("Incorrect",0,"Check script error.\n")
169 exit(127)
175 exit(127)
170 end
176 end
171
177
172 check_file = File.new("check_result", "r")
178 check_file = File.new("check_result", "r")
173 check_file_lines = check_file.readlines
179 check_file_lines = check_file.readlines
174
180
175 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
181 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
You need to be logged in to leave comments. Login now