Description:
add run_stat checking
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r160:f0502be39e3b - - 3 files changed: 49 inserted, 11 deleted

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