Description:
update java-bm
Commit status:
[Not Reviewed]
References:
merge java
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r214:336e928de90e - - 9 files changed: 60 inserted, 21 deleted

@@ -1,154 +1,180
1 1 #!/bin/sh
2 2
3 3 echo "This script will install and configure Cafe grader."
4 4
5 - echo "This will install Ruby 1.9.2 under rvm"
5 + RUBY_VERSION=2.1.2
6 + echo "This will install Ruby $RUBY_VERSION under RVM"
6 7
7 8 echo "Installing required apts"
8 9
9 10 sudo apt-get update
10 11 sudo apt-get install mysql-server mysql-client \
11 12 g++ gcc apache2 libmysqlclient15-dev build-essential \
12 13 git-core openssl libreadline6 libreadline6-dev \
13 14 zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev \
14 15 sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev \
15 16 ncurses-dev automake libtool bison subversion \
16 - pkg-config curl nodejs unzip
17 + pkg-config curl nodejs unzip pyflakes ruby default-jdk
17 18
18 19 echo "Installing RVM"
19 20 curl -k -L https://get.rvm.io | bash -s stable
20 - ~/.rvm/scripts/rvm
21 + source ~/.rvm/scripts/rvm
21 22
22 - echo "Installing Ruby 1.9.2 in RVM"
23 + echo "Installing Ruby $RUBY_VERSION in RVM"
23 24
24 - rvm install 1.9.2
25 - rvm use 1.9.2
25 + rvm install $RUBY_VERSION
26 + rvm use $RUBY_VERSION
26 27
27 28 echo "Fetching Cafe Grader from Git repositories"
28 29
29 30 echo "Fetching web interface"
30 31
31 32 mkdir cafe_grader
32 33 cd cafe_grader
33 34 git clone -q git://github.com/jittat/cafe-grader-web.git web
34 35
35 36 echo "Configuring rails app"
36 37
37 38 cp web/config/application.rb.SAMPLE web/config/application.rb
38 39 cp web/config/initializers/cafe_grader_config.rb.SAMPLE web/config/initializers/cafe_grader_config.rb
39 40
41 + #replace UTC in application.rb with the system timezone
42 + timezone='UTC'
43 + if [ -f '/etc/timezone' ]; then
44 + timezone=\"`cat /etc/timezone`\"
45 + else
46 + if [ -f '/etc/sysconfig/clock' ]; then
47 + timezone=`grep -e '^TIMEZONE' /etc/sysconfig/clock | grep -o -e '\".*\"'`
48 + fi
49 + fi
50 + replace="s!'UTC'!$timezone!g"
51 + sed -i $replace web/config/application.rb
52 +
40 53 echo "At this point we will need MySQL user and database."
41 54 echo "Have you created MySQL user and database for Cafe grader? (Y/N) "
42 55 read ch
43 56
44 57 if [ "$ch" = "n" -o "$ch" = "N" ]
45 58 then
46 59 echo "Please open another terminal and create the user and database for Cafe grader."
47 60 echo "Don't forget to grant access to that database for the user."
48 61 echo "Please have username, password, and database name ready before continue."
49 62 echo
50 63 echo "The following are instructions:"
51 64 echo "1. Run mysql:"
52 65 echo
53 66 echo " mysql -u root -p"
54 67 echo
55 68 echo " if you have just installed mysql, the root password is the one that you have just entered"
56 69 echo "2. Create a new database, a new user, and grant access to grader database:"
57 70 echo
58 71 echo " create user 'USERNAME'@'localhost' identified by 'PASSWORD';"
59 72 echo " create database \`DATABASENEME\`;"
60 73 echo " grant all on \`DATABASENAME\`.* to 'USERNAME'@'localhost';"
61 74 echo
62 75 echo " Replace USERNAME, PASSWORD, and DATABASENAME accordingly."
63 76 echo
64 77 echo "Hit enter when ready..."
65 78 read dummy
66 79 fi
67 80
68 81 CAFE_PATH=`pwd`
69 82
70 83 cd web
71 84
72 85 echo "Please provide grader database:"
73 86 read database
74 87
75 88 echo "Please provide grader username:"
76 89 read username
77 90
78 91 echo "Please provide $username password:"
79 92 read password
80 93
81 94 echo "development:" > config/database.yml
82 95 echo " adapter: mysql2" >> config/database.yml
83 96 echo " encoding: utf8" >> config/database.yml
84 97 echo " reconnect: false" >> config/database.yml
85 98 echo " database: $database" >> config/database.yml
86 99 echo " pool: 5" >> config/database.yml
87 100 echo " username: $username" >> config/database.yml
88 101 echo " password: $password" >> config/database.yml
89 102 echo " host: localhost" >> config/database.yml
90 103 echo " socket: /var/run/mysqld/mysqld.sock" >> config/database.yml
91 104 echo "" >> config/database.yml
92 105 echo "production:" >> config/database.yml
93 106 echo " adapter: mysql2" >> config/database.yml
94 107 echo " encoding: utf8" >> config/database.yml
95 108 echo " reconnect: false" >> config/database.yml
96 109 echo " database: $database" >> config/database.yml
97 110 echo " pool: 5" >> config/database.yml
98 111 echo " username: $username" >> config/database.yml
99 112 echo " password: $password" >> config/database.yml
100 113 echo " host: localhost" >> config/database.yml
101 114 echo " socket: /var/run/mysqld/mysqld.sock" >> config/database.yml
102 115
103 116 echo "Object.instance_eval{remove_const :GRADER_ROOT_DIR}" >> config/initializers/cafe_grader_config.rb
104 117 echo "Object.instance_eval{remove_const :GRADING_RESULT_DIR}" >> config/initializers/cafe_grader_config.rb
105 118 echo "GRADER_ROOT_DIR = '$CAFE_PATH/judge'" >> config/initializers/cafe_grader_config.rb
106 119 echo "GRADING_RESULT_DIR = '$CAFE_PATH/judge/result'" >> config/initializers/cafe_grader_config.rb
107 120
108 121 echo "Installing required gems"
109 122 gem install bundler
110 123 bundle install
111 124
112 125 echo "Running rake tasks to initialize database"
113 126
114 127 rake db:migrate
115 128 rake db:seed
116 129
130 + echo "Running rake tasks to precompile the assets"
131 +
132 + rake assets:precompile
133 +
117 134 echo "Intalling web interface complete..."
118 135 echo
119 136 echo "Fetching grader"
120 137
121 138 cd ..
122 139
123 140 mkdir judge
124 141 cd judge
125 142 git clone -q git://github.com/jittat/cafe-grader-judge-scripts.git scripts
126 143 mkdir raw
127 144 mkdir ev-exam
128 145 mkdir ev
129 146 mkdir result
130 147 mkdir log
131 148
132 149 echo "Configuring grader"
133 150
134 151 cp scripts/config/env_exam.rb.SAMPLE scripts/config/env_exam.rb
135 152 cp scripts/config/env_grading.rb.SAMPLE scripts/config/env_grading.rb
136 153
137 154 # create new environment.rb file
138 155 echo "RAILS_ROOT = '$CAFE_PATH/web'" > scripts/config/environment.rb
139 156 echo "GRADER_ROOT = '$CAFE_PATH/judge/scripts'" >> scripts/config/environment.rb
140 157 echo "require File.join(File.dirname(__FILE__),'../lib/boot')" >> scripts/config/environment.rb
141 158 echo "require File.dirname(__FILE__) + \"/env_#{GRADER_ENV}.rb\"" >> scripts/config/environment.rb
142 159
160 + # compiling box
161 + MACHINE_TYPE=`uname -m`
162 + if [ ${MACHINE_TYPE} == 'x86_64' ]; then
163 + gcc -std=c99 -o scripts/std-script/box scripts/std-script/box64-new.c
164 + else
165 + g++ -o scripts/std-script/box scripts/std-script/box.cc
166 + fi
167 +
168 +
143 169 cd ..
144 170
145 171 echo "Now you are ready to run cafe grader...."
146 172 echo
147 173 echo "Try:"
148 174 echo
149 175 echo " cd web"
150 176 echo " rails s"
151 177 echo
152 178 echo "and access web at http://localhost:3000/"
153 179 echo "The root username is 'root', its password is 'ioionrails'."
154 180
@@ -1,73 +1,73
1 - #!/usr/bin/ruby
1 + #!/usr/bin/env ruby
2 2
3 3 # new_problem:
4 4 # * creates a directory for a problem in the current directory,
5 5 # * create standard testcase config file
6 6
7 7 require 'erb'
8 8
9 9 def process_options(options)
10 10 i = 2
11 11 while i<ARGV.length
12 12 if ARGV[i]=='-t'
13 13 options[:time_limit] = ARGV[i+1].to_i if ARGV.length>i+1
14 14 i += 1
15 15 end
16 16 if ARGV[i]=='-m'
17 17 options[:mem_limit] = ARGV[i+1].to_i if ARGV.length>i+1
18 18 i += 1
19 19 end
20 20 i += 1
21 21 end
22 22 end
23 23
24 24
25 25 puts "This script is out of dated, shall be fixed soon"
26 26 puts "Right now, you can create raw_ev and import"
27 27 exit(0)
28 28
29 29 GRADER_DIR = File.dirname(__FILE__)
30 30
31 31 # print usage
32 32 if ARGV.length < 2
33 33 puts <<USAGE
34 34 using: new_problem problem number_of_testcase [options]
35 35 * creates a directory for a problem in the current directory,
36 36 * create standard testcase config file
37 37 * options: -t time-limit (in seconds)
38 38 -m mem-limit (in MB)
39 39 USAGE
40 40 exit(127)
41 41 end
42 42
43 43 # processing arguments
44 44 problem = ARGV[0]
45 45 num_testcases = ARGV[1].to_i
46 46
47 47 options = {:time_limit => 1, :mem_limit => 16}
48 48 process_options(options)
49 49
50 50 # start working
51 51 puts "creating directories"
52 52
53 53 system("mkdir #{problem}")
54 54 system("mkdir #{problem}/script")
55 55 system("mkdir #{problem}/test_cases")
56 56
57 57 puts "creating testcases directories"
58 58
59 59 1.upto(num_testcases) do |i|
60 60 system("mkdir #{problem}/test_cases/#{i}")
61 61 end
62 62
63 63 # generating all_tests.cfg
64 64 puts "generating testcase config file"
65 65
66 66 template = File.open(File.dirname(__FILE__) + "/templates/all_tests.cfg.erb").read
67 67 all_test_cfg = ERB.new(template)
68 68
69 69 cfg_file = File.open("#{problem}/test_cases/all_tests.cfg","w")
70 70 cfg_file.puts all_test_cfg.result
71 71 cfg_file.close
72 72
73 73 puts "done"
@@ -1,1747 +1,1760
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%dkbytes\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);
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 1459 box_exit(0);
1460 1460 }
1461 1461 if (WIFSIGNALED(stat))
1462 1462 {
1463 1463 box_pid = 0;
1464 1464 meta_printf("exitsig:%d\n", WTERMSIG(stat));
1465 1465 final_stats(&rus);
1466 1466 err("SG: Caught fatal signal %d%s", WTERMSIG(stat), (syscall_count ? "" : " during startup"));
1467 1467 }
1468 1468 if (WIFSTOPPED(stat))
1469 1469 {
1470 1470 int sig = WSTOPSIG(stat);
1471 1471 if (sig == SIGTRAP)
1472 1472 {
1473 1473 if (verbose > 2)
1474 1474 msg("[ptrace status %08x] ", stat);
1475 1475 static int stop_count;
1476 1476 if (!stop_count++) /* Traceme request */
1477 1477 msg(">> Traceme request caught\n");
1478 1478 else
1479 1479 err("SG: Breakpoint");
1480 1480 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1481 1481 }
1482 1482 else if (sig == (SIGTRAP | 0x80))
1483 1483 {
1484 1484 if (verbose > 2)
1485 1485 msg("[ptrace status %08x] ", stat);
1486 1486 struct syscall_args a;
1487 1487 static unsigned int sys_tick, last_act;
1488 1488 static arg_t last_sys;
1489 1489 if (++sys_tick & 1) /* Syscall entry */
1490 1490 {
1491 1491 char namebuf[32];
1492 1492 int act;
1493 1493
1494 1494 get_syscall_args(&a, 0);
1495 1495 arg_t sys = a.sys;
1496 1496 msg(">> Syscall %-12s (%08jx,%08jx,%08jx) ", syscall_name(sys, namebuf), (intmax_t) a.arg1, (intmax_t) a.arg2, (intmax_t) a.arg3);
1497 1497 if (!exec_seen)
1498 1498 {
1499 1499 msg("[master] ");
1500 1500 if (sys == NATIVE_NR_execve)
1501 1501 {
1502 1502 exec_seen = 1;
1503 1503 close_user_mem();
1504 1504 }
1505 1505 }
1506 1506 else if ((act = valid_syscall(&a)) >= 0)
1507 1507 {
1508 1508 last_act = act;
1509 1509 syscall_count++;
1510 1510 if (act & A_SAMPLE_MEM)
1511 1511 sample_mem_peak();
1512 1512 }
1513 1513 else
1514 1514 {
1515 1515 /*
1516 1516 * Unfortunately, PTRACE_KILL kills _after_ the syscall completes,
1517 1517 * so we have to change it to something harmless (e.g., an undefined
1518 1518 * syscall) and make the program continue.
1519 1519 */
1520 1520 set_syscall_nr(&a, ~(arg_t)0);
1521 1521 err("FO: Forbidden syscall %s", syscall_name(sys, namebuf));
1522 1522 }
1523 1523 last_sys = sys;
1524 1524 }
1525 1525 else /* Syscall return */
1526 1526 {
1527 1527 get_syscall_args(&a, 1);
1528 1528 if (a.sys == ~(arg_t)0)
1529 1529 {
1530 1530 /* Some syscalls (sigreturn et al.) do not return a value */
1531 1531 if (!(last_act & A_NO_RETVAL))
1532 1532 err("XX: Syscall does not return, but it should");
1533 1533 }
1534 1534 else
1535 1535 {
1536 1536 if (a.sys != last_sys)
1537 1537 err("XX: Mismatched syscall entry/exit");
1538 1538 }
1539 1539 if (last_act & A_NO_RETVAL)
1540 1540 msg("= ?\n");
1541 1541 else
1542 1542 msg("= %jd\n", (intmax_t) a.result);
1543 1543 }
1544 1544 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1545 1545 }
1546 1546 else if (sig == SIGSTOP)
1547 1547 {
1548 1548 msg(">> SIGSTOP\n");
1549 1549 if (ptrace(PTRACE_SETOPTIONS, box_pid, NULL, (void *) PTRACE_O_TRACESYSGOOD) < 0)
1550 1550 die("ptrace(PTRACE_SETOPTIONS): %m");
1551 1551 ptrace(PTRACE_SYSCALL, box_pid, 0, 0);
1552 1552 }
1553 1553 else if (sig != SIGXCPU && sig != SIGXFSZ)
1554 1554 {
1555 1555 msg(">> Signal %d\n", sig);
1556 1556 sample_mem_peak(); /* Signal might be fatal, so update mem-peak */
1557 1557 ptrace(PTRACE_SYSCALL, box_pid, 0, sig);
1558 1558 }
1559 1559 else
1560 1560 {
1561 1561 meta_printf("exitsig:%d", sig);
1562 1562 err("SG: Received signal %d", sig);
1563 1563 }
1564 1564 }
1565 1565 else
1566 1566 die("wait4: unknown status %x, giving up!", stat);
1567 1567 }
1568 1568 }
1569 1569
1570 1570 static void
1571 1571 box_inside(int argc, char **argv)
1572 1572 {
1573 1573 struct rlimit rl;
1574 1574 char *args[argc+1];
1575 1575
1576 1576 memcpy(args, argv, argc * sizeof(char *));
1577 1577 args[argc] = NULL;
1578 1578 if (set_cwd && chdir(set_cwd))
1579 1579 die("chdir: %m");
1580 1580 if (redir_stdin)
1581 1581 {
1582 1582 close(0);
1583 1583 if (open(redir_stdin, O_RDONLY) != 0)
1584 1584 die("open(\"%s\"): %m", redir_stdin);
1585 1585 }
1586 1586 if (redir_stdout)
1587 1587 {
1588 1588 close(1);
1589 1589 if (open(redir_stdout, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 1)
1590 1590 die("open(\"%s\"): %m", redir_stdout);
1591 1591 }
1592 1592 if (redir_stderr)
1593 1593 {
1594 1594 close(2);
1595 1595 if (open(redir_stderr, O_WRONLY | O_CREAT | O_TRUNC, 0666) != 2)
1596 1596 die("open(\"%s\"): %m", redir_stderr);
1597 1597 }
1598 1598 else
1599 1599 dup2(1, 2);
1600 1600 setpgrp();
1601 1601
1602 1602 if (memory_limit)
1603 1603 {
1604 1604 rl.rlim_cur = rl.rlim_max = memory_limit * 1024;
1605 1605 if (setrlimit(RLIMIT_AS, &rl) < 0)
1606 1606 die("setrlimit(RLIMIT_AS): %m");
1607 1607 }
1608 1608
1609 1609 rl.rlim_cur = rl.rlim_max = (stack_limit ? (rlim_t)stack_limit * 1024 : RLIM_INFINITY);
1610 1610 if (setrlimit(RLIMIT_STACK, &rl) < 0)
1611 1611 die("setrlimit(RLIMIT_STACK): %m");
1612 1612
1613 1613 rl.rlim_cur = rl.rlim_max = 64;
1614 1614 if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
1615 1615 die("setrlimit(RLIMIT_NOFILE): %m");
1616 1616
1617 1617 char **env = setup_environment();
1618 1618 if (filter_syscalls)
1619 1619 {
1620 1620 if (ptrace(PTRACE_TRACEME) < 0)
1621 1621 die("ptrace(PTRACE_TRACEME): %m");
1622 1622 /* Trick: Make sure that we are stopped until the boxkeeper wakes up. */
1623 1623 raise(SIGSTOP);
1624 1624 }
1625 1625 execve(args[0], args, env);
1626 1626 die("execve(\"%s\"): %m", args[0]);
1627 1627 }
1628 1628
1629 1629 static void
1630 1630 usage(void)
1631 1631 {
1632 1632 fprintf(stderr, "Invalid arguments!\n");
1633 1633 printf("\
1634 1634 Usage: box [<options>] -- <command> <arguments>\n\
1635 1635 \n\
1636 1636 Options:\n\
1637 1637 -a <level>\tSet file access level (0=none, 1=cwd, 2=/etc,/lib,..., 3=whole fs, 9=no checks; needs -f)\n\
1638 1638 -c <dir>\tChange directory to <dir> first\n\
1639 1639 -e\t\tInherit full environment of the parent process\n\
1640 1640 -E <var>\tInherit the environment variable <var> from the parent process\n\
1641 1641 -E <var>=<val>\tSet the environment variable <var> to <val>; unset it if <var> is empty\n\
1642 1642 -f\t\tFilter system calls (-ff=very restricted)\n\
1643 1643 -i <file>\tRedirect stdin from <file>\n\
1644 1644 -k <size>\tLimit stack size to <size> KB (default: 0=unlimited)\n\
1645 1645 -m <size>\tLimit address space to <size> KB\n\
1646 1646 -M <file>\tOutput process information to <file> (name:value)\n\
1647 1647 -o <file>\tRedirect stdout to <file>\n\
1648 1648 -p <path>\tPermit access to the specified path (or subtree if it ends with a `/')\n\
1649 1649 -p <path>=<act>\tDefine action for the specified path (<act>=yes/no)\n\
1650 1650 -r <file>\tRedirect stderr to <file>\n\
1651 1651 -s <sys>\tPermit the specified syscall (be careful)\n\
1652 1652 -s <sys>=<act>\tDefine action for the specified syscall (<act>=yes/no/file)\n\
1653 1653 -t <time>\tSet run time limit (seconds, fractions allowed)\n\
1654 1654 -T\t\tAllow syscalls for measuring run time\n\
1655 1655 -v\t\tBe verbose (use multiple times for even more verbosity)\n\
1656 1656 -w <time>\tSet wall clock time limit (seconds, fractions allowed)\n\
1657 1657 -x <time>\tSet extra timeout, before which a timing-out program is not yet killed,\n\
1658 1658 \t\tso that its real execution time is reported (seconds, fractions allowed)\n\
1659 + -A <opt>\tPass <opt> as additional argument to the <command>\n\
1660 + \t\tBe noted that this option will be appended after <arguments> respectively\n\
1659 1661 ");
1660 1662 exit(2);
1661 1663 }
1662 1664
1663 1665 int
1664 1666 main(int argc, char **argv)
1665 1667 {
1666 1668 int c;
1667 1669 uid_t uid;
1670 + char **prog_argv = xmalloc(sizeof(char*) * argc);
1671 + int prog_argc = 0;
1668 1672
1669 - while ((c = getopt(argc, argv, "a:c:eE:fi:k:m:M:o:p:r:s:t:Tvw:x:")) >= 0)
1673 + while ((c = getopt(argc, argv, "a:c:eE:fi:k:m:M:o:p:r:s:t:Tvw:x:A:")) >= 0)
1670 1674 switch (c)
1671 1675 {
1672 1676 case 'a':
1673 1677 file_access = atol(optarg);
1674 1678 break;
1675 1679 case 'c':
1676 1680 set_cwd = optarg;
1677 1681 break;
1678 1682 case 'e':
1679 1683 pass_environ = 1;
1680 1684 break;
1681 1685 case 'E':
1682 1686 if (!set_env_action(optarg))
1683 1687 usage();
1684 1688 break;
1685 1689 case 'f':
1686 1690 filter_syscalls++;
1687 1691 break;
1688 1692 case 'k':
1689 1693 stack_limit = atol(optarg);
1690 1694 break;
1691 1695 case 'i':
1692 1696 redir_stdin = optarg;
1693 1697 break;
1694 1698 case 'm':
1695 1699 memory_limit = atol(optarg);
1696 1700 break;
1697 1701 case 'M':
1698 1702 meta_open(optarg);
1699 1703 break;
1700 1704 case 'o':
1701 1705 redir_stdout = optarg;
1702 1706 break;
1703 1707 case 'p':
1704 1708 if (!set_path_action(optarg))
1705 1709 usage();
1706 1710 break;
1707 1711 case 'r':
1708 1712 redir_stderr = optarg;
1709 1713 break;
1710 1714 case 's':
1711 1715 if (!set_syscall_action(optarg))
1712 1716 usage();
1713 1717 break;
1714 1718 case 't':
1715 1719 timeout = 1000*atof(optarg);
1716 1720 break;
1717 1721 case 'T':
1718 1722 syscall_action[__NR_times] = A_YES;
1719 1723 break;
1720 1724 case 'v':
1721 1725 verbose++;
1722 1726 break;
1723 1727 case 'w':
1724 1728 wall_timeout = 1000*atof(optarg);
1725 1729 break;
1726 1730 case 'x':
1727 1731 extra_timeout = 1000*atof(optarg);
1732 + case 'A':
1733 + prog_argv[prog_argc++] = strdup(optarg);
1734 + break;
1728 1735 break;
1729 1736 default:
1730 1737 usage();
1731 1738 }
1732 1739 if (optind >= argc)
1733 1740 usage();
1734 1741
1735 1742 sanity_check();
1736 1743 uid = geteuid();
1737 1744 if (setreuid(uid, uid) < 0)
1738 1745 die("setreuid: %m");
1739 1746 box_pid = fork();
1740 1747 if (box_pid < 0)
1741 1748 die("fork: %m");
1742 - if (!box_pid)
1743 - box_inside(argc-optind, argv+optind);
1744 - else
1749 + if (!box_pid) {
1750 + int real_argc = prog_argc + argc - optind;
1751 + char **real_argv = xmalloc(sizeof(char*) * (real_argc));
1752 + for (int i = 0;i < argc-optind;i++)
1753 + real_argv[i] = strdup(argv[i+optind]);
1754 + for (int i = 0;i < prog_argc;i++)
1755 + real_argv[argc - optind + i] = strdup(prog_argv[i]);
1756 + box_inside(real_argc, real_argv);
1757 + } else
1745 1758 boxkeeper();
1746 1759 die("Internal error: fell over edge of the world");
1747 1760 }
@@ -1,189 +1,189
1 1 #!/usr/bin/env ruby
2 2
3 3 require 'fileutils'
4 4
5 5 def log(str='')
6 6 if ENV['TALKATIVE']!=nil
7 7 puts str
8 8 end
9 9 if ENV['GRADER_LOGGING']!=nil
10 10 log_fname = ENV['GRADER_LOGGING']
11 11 fp = File.open(log_fname,"a")
12 12 fp.puts("run: #{Time.new.strftime("%H:%M")} #{str}")
13 13 fp.close
14 14 end
15 15 end
16 16
17 17 def extract_time(t)
18 18 # puts "TIME: #{t}"
19 19 if (result=/^(.*)r(.*)u(.*)s/.match(t))
20 20 {:real => result[1], :user => result[2], :sys => result[3]}
21 21 else
22 22 #{:real => 0, :user => 0, :sys => 0}
23 23 #puts "ERROR READING RUNNING TIME: #{t}"
24 24 raise "Error reading running time: #{t}"
25 25 end
26 26 end
27 27
28 28 def compile_box(source,bin)
29 29 system("g++ #{source} -o #{bin}")
30 30 end
31 31
32 32 if ARGV.length < 2 || ARGV.length > 3
33 33 puts "Usage: run <language> <test-num> [<program-name>]"
34 34 exit(127)
35 35 end
36 36
37 37 language = ARGV[0]
38 38 test_num = ARGV[1].to_i
39 39 if ARGV.length > 2
40 40 program_name = ARGV[2]
41 41 else
42 42 program_name = "a.out"
43 43 end
44 44
45 45 problem_home = ENV['PROBLEM_HOME']
46 46 source_name = ENV['SOURCE_NAME']
47 47 require "#{problem_home}/script/test_dsl.rb"
48 48 load "#{problem_home}/test_cases/all_tests.cfg"
49 49 problem = Problem.get_instance
50 50
51 51 sandbox_dir = Dir.getwd
52 52
53 53 if problem.well_formed? == false
54 54 log "The problem specification is not well formed."
55 55 exit(127)
56 56 end
57 57
58 58 # Check if the test number is okay.
59 59 if test_num <= 0 || test_num > problem.num_tests
60 60 log "You have specified a wrong test number."
61 61 exit(127)
62 62 end
63 63
64 64 #####################################
65 65 # Set the relavant file names here. #
66 66 #####################################
67 67
68 68 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
69 69
70 70 #####################################
71 71
72 72 time_limit = problem.get_time_limit test_num
73 73 mem_limit = problem.get_mem_limit(test_num) * 1024
74 74
75 75 # Copy the input file.
76 76 #`cp #{problem_home}/test_cases/#{test_num}/#{input_file_name} .`
77 77
78 78 # check if box is there, if not, compile it!
79 79 if !File.exists?("#{problem_home}/script/box")
80 80 log "WARNING: Compiling box: to increase efficiency, it should be compile manually"
81 81 compile_box("#{problem_home}/script/box.cc",
82 82 "#{problem_home}/script/box")
83 83 end
84 84
85 85 # Hide PROBLEM_HOME
86 86 ENV['PROBLEM_HOME'] = nil
87 87 ENV['SOURCE_NAME'] = nil
88 88
89 89 # Run the program.
90 90 #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}"
91 91 #
92 92
93 93 JAVA_OPTION = "-s set_robust_list -s futex -s clone -s getppid -s clone -s wait4 -p /usr/bin/ -p ./"
94 94 RUBY_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /lib64/ -p /dev/urandom -p #{sandbox_dir}/#{program_name} -p #{sandbox_dir}/ -s set_robust_list -s sched_getaffinity -s clock_gettime -s sigaltstack -s pipe2 -s clone -s futex -s openat -s pipe"
95 95 PYTHON_OPTION = "-p /usr/lib64/ -p /usr/local/lib64/ -p /usr/local/lib/ -p /usr/bin/ -p /lib64/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p #{sandbox_dir}/#{source_name} -s set_robust_list -s openat -s recvmsg -s connect -s socket -s sendto -s futex -E PYTHONNOUSERSITE=yes"
96 96 PHP_OPTION = "-p /usr/lib64/ -p/lib64/ -p /usr/bin/ -p #{sandbox_dir}/#{program_name} -p ./#{program_name} -p /usr/share/ -s setfsuid -s setfsgid -s openat -s set_robust_list -s futex -s clone -s socket -s connect"
97 97
98 98 case language
99 99 when "java"
100 100 # for java, extract the classname
101 101 # wne have to add additional systemcall and we don't check the mem limit (dunno how to fix...)
102 102 classname = 'DUMMY'
103 103 File.open(program_name,"r").each do |line|
104 104 classname = line
105 105 end
106 106 #for java, we cannot really check the memory limit...
107 - run_command = "#{problem_home}/script/box -a 3 -f -T -t #{time_limit} #{JAVA_OPTION} -i #{input_file_name} -o output.txt /usr/bin/java #{classname} "
107 + run_command = "#{problem_home}/script/box -a 3 -f -T -t #{time_limit} #{JAVA_OPTION} -i #{input_file_name} -o output.txt /usr/bin/java -A -Xmx#{mem_limit}k -A #{classname} "
108 108 when "ruby"
109 109 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{mem_limit} #{RUBY_OPTION} -i #{input_file_name} -o output.txt /usr/bin/ruby #{program_name} "
110 110 when "python"
111 111 run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{mem_limit} #{PYTHON_OPTION} -i #{input_file_name} -o output.txt /usr/bin/python #{program_name} "
112 112 when "php"
113 - run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} #{PHP_OPTION} -i #{input_file_name} -o output.txt /usr/bin/php #{program_name} "
113 + run_command = "#{problem_home}/script/box -a 2 -f -T -t #{time_limit*=2} -m #{[128*1024,mem_limit].max} #{PHP_OPTION} -i #{input_file_name} -o output.txt /usr/bin/php -A -d -A memory_limit=#{mem_limit}k -A #{program_name} "
114 114 else # for c++, pascal, we do the normal checking
115 115 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} "
116 116 end
117 117
118 118
119 119 log "Running test #{test_num}..."
120 120 log run_command
121 121 log
122 122 system(run_command,err: 'run_result')
123 123
124 124 # Restore PROBLEM_HOME
125 125 ENV['PROBLEM_HOME'] = problem_home
126 126
127 127 # Create the result file.
128 128 result_file = File.new("result", "w")
129 129 comment_file = File.new("comment", "w")
130 130
131 131 # Check if the program actually produced any output.
132 132 run_result_file = File.new("run_result", "r")
133 133 run_result = run_result_file.readlines
134 134 run_result_file.close
135 135
136 136 run_stat = run_result[run_result.length-1]
137 137 running_time = extract_time(run_stat)
138 138
139 139 report = lambda{ |status, points, comment|
140 140 result_file.write status.strip
141 141 result_file.write "\n"
142 142 result_file.write points.to_s.strip
143 143 result_file.write "\n"
144 144 result_file.write run_stat.strip
145 145 result_file.write "\n"
146 146 result_file.close
147 147 FileUtils.rm "run_result"
148 148 # `rm output.txt` --- keep the output
149 149
150 150 comment_file.write comment
151 151
152 152 # added for debuggin --- jittat
153 153 comment_file.write "--run-result--\n"
154 154 run_result.each do |l|
155 155 comment_file.write l
156 156 end
157 157
158 158 comment_file.close
159 159
160 160 log "Done!"
161 161 exit(0)
162 162 }
163 163
164 164
165 165 if run_result[0][0,2] != "OK"
166 166 log "There was a runtime error."
167 167 report.call(run_result[0], 0, "No comment.\n")
168 168 end
169 169
170 170 if running_time[:user].to_f > time_limit
171 171 log "Time limit exceeded."
172 172 report.call("Time limit exceeded", 0, "No comment.\n")
173 173 end
174 174
175 175 # Run 'check' to evaluate the output.
176 176 #puts "There was no runtime error. Proceed to checking the output."
177 177 check_command = "#{problem_home}/script/check #{language} #{test_num}"
178 178 log "Checking the output..."
179 179 log check_command
180 180 if not system(check_command)
181 181 log "Problem with check script"
182 182 report.call("Incorrect",0,"Check script error.\n")
183 183 exit(127)
184 184 end
185 185
186 186 check_file = File.new("check_result", "r")
187 187 check_file_lines = check_file.readlines
188 188
189 189 report.call(check_file_lines[0], check_file_lines[1], "No comment.\n")
@@ -1,48 +1,48
1 - #!/usr/bin/ruby
1 + #!/usr/bin/env ruby
2 2
3 3 #
4 4 # This is a check script wrapper. It read all required information
5 5 # and call a real check script call REAL_CHECK_SCRIPT in directory
6 6 # [problem_home]/script
7 7 #
8 8
9 9 REAL_CHECK_SCRIPT = "<%= script_name %>"
10 10
11 11 # The REAL_CHECK_SCRIPT is called with:
12 12 #
13 13 # (script) <lang> <test-num> <in-file> <out-file> <ans-file> <full-score>
14 14 #
15 15 # and REAL_CHECK_SCRIPT's output to standard out is redirected to
16 16 # 'check_result' as required by normal check script.
17 17
18 18 problem_home = ENV['PROBLEM_HOME']
19 19 require "#{problem_home}/script/test_dsl.rb"
20 20
21 21 if ARGV.length < 2
22 22 puts "Usage: check <language> <test-number> [<output-file>]"
23 23 exit(0)
24 24 end
25 25
26 26 language = ARGV[0]
27 27 test_num = ARGV[1].to_i
28 28 if ARGV.length >= 3
29 29 output_file_name = ARGV[2]
30 30 else
31 31 output_file_name = "output.txt"
32 32 end
33 33
34 34 load "#{problem_home}/test_cases/all_tests.cfg"
35 35 problem = Problem.get_instance
36 36
37 37 answer_file_name = "#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt"
38 38 input_file_name = "#{problem_home}/test_cases/#{test_num}/input-#{test_num}.txt"
39 39
40 40 score = problem.get_score(test_num)
41 41
42 42 cmd = "#{problem_home}/script/#{REAL_CHECK_SCRIPT} " +
43 43 "#{language} #{test_num} #{input_file_name} #{output_file_name} " +
44 44 "#{answer_file_name} #{score} > check_result"
45 45
46 46 #puts "wrapper-CMD: #{cmd}"
47 47
48 48 system(cmd)
@@ -1,59 +1,59
1 - #!/usr/bin/ruby
1 + #!/usr/bin/env ruby
2 2
3 3 problem_home = ENV['PROBLEM_HOME']
4 4 require "#{problem_home}/script/test_dsl.rb"
5 5
6 6 if ARGV.length < 2
7 7 puts "Usage: check <language> <test-number> [<output-file>]"
8 8 exit(0)
9 9 end
10 10
11 11 language = ARGV[0]
12 12 test_num = ARGV[1].to_i
13 13 if ARGV.length >= 3
14 14 output_file_name = ARGV[2]
15 15 else
16 16 output_file_name = "output.txt"
17 17 end
18 18
19 19 load "#{problem_home}/test_cases/all_tests.cfg"
20 20 problem = Problem.get_instance
21 21
22 22 output_file = File.new(output_file_name, "r")
23 23 answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt")
24 24 result_file = File.new("check_result", "w")
25 25
26 26 output_file_content = output_file.read
27 27 answer_file_content = answer_file.read
28 28
29 29 report_correct = lambda {
30 30 result_file.write "Correct\n"
31 31 result_file.write problem.get_score(test_num)
32 32 result_file.write "\n"
33 33 result_file.close
34 34 exit(0)
35 35 }
36 36
37 37 report_wrong = lambda {
38 38 result_file.write "Incorrect\n"
39 39 result_file.write "0\n"
40 40 result_file.close
41 41 exit(0)
42 42 }
43 43
44 44 ##################
45 45 # Your code here #
46 46 ##################
47 47 num_pattern = /^[0-9]*/
48 48 if (output_file_content =~ num_pattern) == nil
49 49 report_wrong.call
50 50 end
51 51
52 52 output_i = output_file_content.to_i
53 53 answer_i = answer_file_content.to_i
54 54
55 55 if output_i == answer_i
56 56 report_correct.call
57 57 else
58 58 report_wrong.call
59 59 end
@@ -1,59 +1,59
1 - #!/usr/bin/ruby
1 + #!/usr/bin/env ruby
2 2
3 3 problem_home = ENV['PROBLEM_HOME']
4 4 require "#{problem_home}/script/test_dsl.rb"
5 5
6 6 if ARGV.length < 2
7 7 puts "Usage: check <language> <test-number> [<output-file>]"
8 8 exit(0)
9 9 end
10 10
11 11 language = ARGV[0]
12 12 test_num = ARGV[1].to_i
13 13 if ARGV.length >= 3
14 14 output_file_name = ARGV[2]
15 15 else
16 16 output_file_name = "output.txt"
17 17 end
18 18
19 19 load "#{problem_home}/test_cases/all_tests.cfg"
20 20 problem = Problem.get_instance
21 21
22 22 output_file = File.new(output_file_name, "r")
23 23 answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt")
24 24 result_file = File.new("check_result", "w")
25 25
26 26 output_file_content = output_file.read
27 27 answer_file_content = answer_file.read
28 28
29 29 report_correct = lambda {
30 30 result_file.write "Correct\n"
31 31 result_file.write problem.get_score(test_num)
32 32 result_file.write "\n"
33 33 result_file.close
34 34 exit(0)
35 35 }
36 36
37 37 report_wrong = lambda {
38 38 result_file.write "Incorrect\n"
39 39 result_file.write "0\n"
40 40 result_file.close
41 41 exit(0)
42 42 }
43 43
44 44 ##################
45 45 # Your code here #
46 46 ##################
47 47 num_pattern = /^[0-9]*/
48 48 if (output_file_content =~ num_pattern) == nil
49 49 report_wrong.call
50 50 end
51 51
52 52 output_i = output_file_content.to_i
53 53 answer_i = answer_file_content.to_i
54 54
55 55 if output_i == answer_i
56 56 report_correct.call
57 57 else
58 58 report_wrong.call
59 59 end
@@ -1,59 +1,59
1 - #!/usr/bin/ruby
1 + #!/usr/bin/env ruby
2 2
3 3 problem_home = ENV['PROBLEM_HOME']
4 4 require "#{problem_home}/script/test_dsl.rb"
5 5
6 6 if ARGV.length < 2
7 7 puts "Usage: check <language> <test-number> [<output-file>]"
8 8 exit(0)
9 9 end
10 10
11 11 language = ARGV[0]
12 12 test_num = ARGV[1].to_i
13 13 if ARGV.length >= 3
14 14 output_file_name = ARGV[2]
15 15 else
16 16 output_file_name = "output.txt"
17 17 end
18 18
19 19 load "#{problem_home}/test_cases/all_tests.cfg"
20 20 problem = Problem.get_instance
21 21
22 22 output_file = File.new(output_file_name, "r")
23 23 answer_file = File.new("#{problem_home}/test_cases/#{test_num}/answer-#{test_num}.txt")
24 24 result_file = File.new("check_result", "w")
25 25
26 26 output_file_content = output_file.read
27 27 answer_file_content = answer_file.read
28 28
29 29 report_correct = lambda {
30 30 result_file.write "Correct\n"
31 31 result_file.write problem.get_score(test_num)
32 32 result_file.write "\n"
33 33 result_file.close
34 34 exit(0)
35 35 }
36 36
37 37 report_wrong = lambda {
38 38 result_file.write "Incorrect\n"
39 39 result_file.write "0\n"
40 40 result_file.close
41 41 exit(0)
42 42 }
43 43
44 44 ##################
45 45 # Your code here #
46 46 ##################
47 47 num_pattern = /^[0-9]*/
48 48 if (output_file_content =~ num_pattern) == nil
49 49 report_wrong.call
50 50 end
51 51
52 52 output_i = output_file_content.to_i
53 53 answer_i = answer_file_content.to_i
54 54
55 55 if output_i == answer_i
56 56 report_correct.call
57 57 else
58 58 report_wrong.call
59 59 end
@@ -1,328 +1,328
1 1 require File.join(File.dirname(__FILE__),'spec_helper')
2 2 require File.join(File.dirname(__FILE__),'engine_spec_helper')
3 3
4 4 describe "A grader engine, when grading submissions" do
5 5
6 6 include GraderEngineHelperMethods
7 7
8 8 before(:each) do
9 9 @config = Grader::Configuration.get_instance
10 10
11 11 # this test is from Pong
12 12 @problem_test_normal = stub(Problem,
13 13 :id => 1, :name => 'test_normal',
14 14 :full_score => 135)
15 15 @user_user1 = stub(User,
16 16 :id => 1, :login => 'user1')
17 17
18 18 @engine = Grader::Engine.new
19 19 init_sandbox
20 20 end
21 21
22 22 it "should grade normal submission" do
23 23 grader_should(:grade => "test1_correct.c",
24 24 :on => @problem_test_normal,
25 25 :and_report => {
26 26 :score => 135,
27 27 :comment => /^(\[|P|\])+/})
28 28 end
29 29
30 30
31 31 it "should grade normal submission in C++" do
32 32 cpp_lang = stub(Language, :name => 'cpp', :ext => 'cpp')
33 33
34 34 grader_should(:grade => "test1_correct_cc.cc",
35 35 :language => cpp_lang,
36 36 :on => @problem_test_normal,
37 37 :and_report => {
38 38 :score => 135,
39 39 :comment => /^(\[|P|\])+/})
40 40 end
41 41
42 42 it "should produce error message when submission cannot compile" do
43 43 grader_should(:grade => "test1_compile_error.c",
44 44 :on => @problem_test_normal,
45 45 :and_report => {
46 46 :score => 0,
47 47 :comment => 'compilation error',
48 48 :compiler_message => /[Ee]rror/})
49 49 end
50 50
51 51 it "should produce timeout error when submission runs forever" do
52 52 @problem_test_timeout = stub(Problem,
53 53 :id => 1, :name => 'test_timeout',
54 54 :full_score => 10)
55 55 grader_should(:grade => "test2_timeout.c",
56 56 :on => @problem_test_timeout,
57 57 :and_report => {
58 58 :score => 0,
59 59 :comment => 'TT'})
60 60 end
61 61
62 62 it "should produce timeout error correctly with fractional running time and fractional time limits" do
63 63 @problem_test_timeout = stub(Problem,
64 64 :id => 1, :name => 'test_timeout',
65 65 :full_score => 20)
66 66 grader_should(:grade => "test2_05sec.c",
67 67 :on => @problem_test_timeout,
68 68 :and_report => {
69 69 :score => 10,
70 70 :comment => 'TP'})
71 71 end
72 72
73 73 it "should produce runtime error when submission uses too much static memory" do
74 74 @problem_test_memory = stub(Problem,
75 75 :id => 1, :name => 'test_memory',
76 76 :full_score => 20)
77 77 grader_should(:grade => "add_too_much_memory_static.c",
78 78 :on => @problem_test_memory,
79 79 :and_report => {
80 80 :score => 10,
81 81 :comment => /[^P]P/})
82 82 end
83 83
84 84 it "should not allow submission to allocate too much dynamic memory" do
85 85 @problem_test_memory = stub(Problem,
86 86 :id => 1, :name => 'test_memory',
87 87 :full_score => 20)
88 88 grader_should(:grade => "add_too_much_memory_dynamic.c",
89 89 :on => @problem_test_memory,
90 90 :and_report => {
91 91 :score => 10,
92 92 :comment => /[^P]P/})
93 93 end
94 94
95 95 it "should score test runs correctly when submission fails in some test case" do
96 96 grader_should(:grade => "add_fail_test_case_1.c",
97 97 :on => @problem_test_normal,
98 98 :and_report => {
99 99 :score => 105,
100 100 :comment => /^.*(-|x).*$/})
101 101 end
102 102
103 103 it "should fail submission with non-zero exit status" do
104 104 grader_should(:grade => "add_nonzero_exit_status.c",
105 105 :on => @problem_test_normal,
106 106 :and_report => {
107 107 :score => 0,
108 108 :comment => /^(\[|x|\])+$/})
109 109 end
110 110
111 111 it "should not allow malicious submission to see PROBLEM_HOME" do
112 112 problem_test_yesno = stub(Problem,
113 113 :id => 1, :name => 'test_yesno',
114 114 :full_score => 10)
115 115 grader_should(:grade => "yesno_access_problem_home.c",
116 116 :on => problem_test_yesno,
117 117 :and_report => {
118 118 :score => 0,
119 119 :comment => /(-|x)/})
120 120 end
121 121
122 122 it "should not allow malicious submission to open files" do
123 123 problem_test_yesno = stub(Problem,
124 124 :id => 1, :name => 'test_yesno',
125 125 :full_score => 10)
126 126 grader_should(:grade => "yesno_open_file.c",
127 127 :on => problem_test_yesno,
128 128 :and_report => {
129 129 :score => 0,
130 130 :comment => /(-|x)/})
131 131 end
132 132
133 133 def grader_should(args)
134 134 @user1 = stub(User,
135 135 :id => 1, :login => 'user1')
136 136
137 137 submission =
138 138 create_submission_from_file(1, @user1, args[:on], args[:grade], args[:language])
139 139 submission.should_receive(:graded_at=)
140 140
141 141 expected_score = args[:and_report][:score]
142 142 expected_comment = args[:and_report][:comment]
143 143 if args[:and_report][:compiler_message]!=nil
144 144 expected_compiler_message = args[:and_report][:compiler_message]
145 145 else
146 146 expected_compiler_message = ''
147 147 end
148 148
149 149 submission.should_receive(:points=).with(expected_score)
150 150 submission.should_receive(:grader_comment=).with(expected_comment)
151 151 submission.should_receive(:compiler_message=).with(expected_compiler_message)
152 152 submission.should_receive(:save)
153 153
154 154 @engine.grade(submission)
155 155 end
156 156
157 157 protected
158 158
159 159 def create_normal_submission_mock_from_file(source_fname)
160 160 create_submission_from_file(1, @user_user1, @problem_test_normal, source_fname)
161 161 end
162 162
163 163 end
164 164
165 165 describe "A grader engine, when grading test requests" do
166 166
167 167 include GraderEngineHelperMethods
168 168
169 169 before(:each) do
170 170 @config = Grader::Configuration.get_instance
171 171 @engine = Grader::Engine.new(:room_maker => Grader::TestRequestRoomMaker.new,
172 172 :reporter => Grader::TestRequestReporter.new)
173 173 init_sandbox
174 174 end
175 175
176 176 it "should report error if there is no problem template" do
177 177 problem = stub(Problem,
178 178 :id => 1, :name => 'nothing')
179 179 grader_should(:grade => 'test1_correct.c',
180 180 :on => problem,
181 181 :with => 'in1.txt',
182 182 :and_report => {
183 183 :graded_at= => nil,
184 184 :compiler_message= => '',
185 185 :grader_comment= => '',
186 186 :running_stat= => /template not found/,
187 187 :running_time= => nil,
188 188 :exit_status= => nil,
189 189 :memory_usage= => nil,
190 190 :save => nil})
191 191 end
192 192
193 193 it "should run test request and produce output file" do
194 194 problem = stub(Problem,
195 195 :id => 1, :name => 'test_normal')
196 196 grader_should(:grade => 'test1_correct.c',
197 197 :on => problem,
198 198 :with => 'in1.txt',
199 199 :and_report => {
200 200 :graded_at= => nil,
201 201 :compiler_message= => '',
202 202 :grader_comment= => '',
203 203 :running_stat= => /0.0\d* sec./,
204 204 :output_file_name= => lambda { |fname|
205 - File.exists?(fname).should be_true
205 + File.exists?(fname).should be true
206 206 },
207 207 :running_time= => nil,
208 208 :exit_status= => nil,
209 209 :memory_usage= => nil,
210 210 :save => nil})
211 211 end
212 212
213 213 it "should clean up problem directory after running test request" do
214 214 problem = stub(Problem,
215 215 :id => 1, :name => 'test_normal')
216 216 grader_should(:grade => 'test1_correct.c',
217 217 :on => problem,
218 218 :with => 'in1.txt',
219 219 :and_report => {
220 220 :graded_at= => nil,
221 221 :compiler_message= => '',
222 222 :grader_comment= => '',
223 223 :running_stat= => nil,
224 224 :output_file_name= => nil,
225 225 :running_time= => nil,
226 226 :exit_status= => nil,
227 227 :memory_usage= => nil,
228 228 :save => nil})
229 - File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be_false
229 + File.exists?(@config.user_result_dir + "/test_request/test_normal/test_cases/1/input-1.txt").should be false
230 230 end
231 231
232 232 it "should compile test request with error and report compilation error" do
233 233 problem = stub(Problem,
234 234 :id => 1, :name => 'test_normal')
235 235 grader_should(:grade => 'test1_compile_error.c',
236 236 :on => problem,
237 237 :with => 'in1.txt',
238 238 :and_report => {
239 239 :graded_at= => nil,
240 240 :compiler_message= => /.+/,
241 241 :grader_comment= => /[Cc]ompil.*error/,
242 242 :running_stat= => '',
243 243 :save => nil})
244 244 end
245 245
246 246 it "should report exit status" do
247 247 problem = stub(Problem,
248 248 :id => 1, :name => 'test_normal')
249 249 grader_should(:grade => 'add_nonzero_exit_status.c',
250 250 :on => problem,
251 251 :with => 'in1.txt',
252 252 :and_report => {
253 253 :graded_at= => nil,
254 254 :compiler_message= => '',
255 255 :grader_comment= => '',
256 256 :running_stat= => /[Ee]xit.*status.*10.*0\.0\d* sec/m,
257 257 :output_file_name= => lambda { |fname|
258 - File.exists?(fname).should be_true
258 + File.exists?(fname).should be true
259 259 },
260 260 :running_time= => nil,
261 261 :exit_status= => /10/,
262 262 :memory_usage= => nil,
263 263 :save => nil})
264 264 end
265 265
266 266 it "should produce running statistics for normal submission" do
267 267 problem = stub(Problem,
268 268 :id => 1, :name => 'test_normal')
269 269 grader_should(:grade => 'test_run_stat.c',
270 270 :on => problem,
271 271 :with => 'in1.txt',
272 272 :and_report => {
273 273 :graded_at= => nil,
274 274 :compiler_message= => '',
275 275 :grader_comment= => '',
276 276 :running_stat= => nil,
277 277 :output_file_name= => lambda { |fname|
278 - File.exists?(fname).should be_true
278 + File.exists?(fname).should be true
279 279 },
280 280 :running_time= => lambda { |t|
281 281 (t>=0.14) and (t<=0.16)
282 282 },
283 283 :exit_status= => nil,
284 284 :memory_usage= => lambda { |s|
285 285 (s>=500) and (s<=1000)
286 286 },
287 287 :save => nil})
288 288 end
289 289
290 290 protected
291 291 def grader_should(args)
292 292 @user1 = stub(User,
293 293 :id => 1, :login => 'user1')
294 294
295 295 problem = args[:on]
296 296 input_file = @config.test_request_input_base_dir + "/" + args[:with]
297 297
298 298 submission =
299 299 create_submission_from_file(1, @user1, args[:on], args[:grade])
300 300
301 301 test_request = stub(TestRequest,
302 302 :id => 1,
303 303 :user => @user1,
304 304 :problem => problem,
305 305 :submission => submission,
306 306 :input_file_name => input_file,
307 307 :language => submission.language,
308 308 :problem_name => problem.name)
309 309
310 310 expectations = args[:and_report]
311 311
312 312 expectations.each do |key,val|
313 313 if val==nil
314 314 test_request.should_receive(key)
315 315 elsif val.class == Proc
316 316 test_request.should_receive(key) { |fname|
317 317 val.call(fname)
318 318 }
319 319 else
320 320 test_request.should_receive(key).with(val)
321 321 end
322 322 end
323 323
324 324 @engine.grade(test_request)
325 325 end
326 326
327 327 end
328 328
You need to be logged in to leave comments. Login now