diff --git a/Makefile b/Makefile index 7a1f8b8..1e0db31 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,10 @@ isolate: isolate.c gcc -o isolate isolate.c -O2 -Wall -Wno-parentheses -Wno-unused-result -g -std=c99 +install: + cp isolate /usr/local/bin/isolate + chown root /usr/local/bin/isolate + chmod u+s /usr/local/bin/isolate isolate.1: isolate.1.txt a2x -f manpage -D . $< diff --git a/isolate.c b/isolate.c index 0023cbf..022bc4c 100644 --- a/isolate.c +++ b/isolate.c @@ -52,6 +52,7 @@ static int pass_environ; static int verbose; static int fsize_limit; static int memory_limit; +static int soft_memory_limit; static int stack_limit; static int block_quota; static int inode_quota; @@ -77,7 +78,9 @@ static int cleanup_ownership; static struct timeval start_time; static int ticks_per_sec; +static long int page_size_kb; static int total_ms, wall_ms; +static unsigned long total_memory; static volatile sig_atomic_t timer_tick; static int error_pipes[2]; @@ -996,6 +999,7 @@ signal_int(int unused UNUSED) } #define PROC_BUF_SIZE 4096 + static void read_proc_file(char *buf, char *name, int *fdp) { @@ -1016,6 +1020,17 @@ read_proc_file(char *buf, char *name, int *fdp) buf[c] = 0; } +unsigned long memusage (pid_t pid) +{ + char buf[PROC_BUF_SIZE]; + unsigned long data = 0; + static int fd; + read_proc_file(buf, "statm", &fd); + if(sscanf(buf, "%*u %*u %*u %*u %*u %lu", &data) != 1) + die("proc statm syntax error 2"); + return data * page_size_kb; +} + static int get_wall_time_ms(void) { @@ -1081,6 +1096,23 @@ check_timeout(void) } } +static void +check_memory(void) +{ + if(soft_memory_limit) { + unsigned long m = memusage(box_pid); + if(m > total_memory){ + total_memory = m; + if(verbose > 1) + fprintf(stderr, "[memory check: %lu kB]\n", total_memory); + if(total_memory > soft_memory_limit) { + err("ML: Memory limit exceeded"); + } + } + } +} + +#define INTERVAL 67 // The polling interval (roughly 13 times/sec) static void box_keeper(void) { @@ -1096,6 +1128,9 @@ box_keeper(void) ticks_per_sec = sysconf(_SC_CLK_TCK); if (ticks_per_sec <= 0) die("Invalid ticks_per_sec!"); + page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; + if (page_size_kb <= 0) + die("Invalid page_size_kb!"); if (timeout || wall_timeout) { @@ -1104,44 +1139,49 @@ box_keeper(void) alarm(1); } - for(;;) + + struct rusage rus; + int stat; + pid_t p; + do { + if(timer_tick) { - struct rusage rus; - int stat; - pid_t p; - if (timer_tick) - { - check_timeout(); - timer_tick = 0; - } - p = wait4(box_pid, &stat, 0, &rus); - if (p < 0) - { - if (errno == EINTR) - continue; - die("wait4: %m"); - } - if (p != box_pid) - die("wait4: unknown pid %d exited!", p); - box_pid = 0; - - // Check error pipe if there is an internal error passed from inside the box - char interr[1024]; - int n = read(read_errors_from_fd, interr, sizeof(interr) - 1); - if (n > 0) + check_timeout(); + timer_tick = 0; + } + usleep(INTERVAL); + check_memory(); + do + p = wait4(box_pid, &stat, WNOHANG | WUNTRACED, &rus); + while ((p < 0 && (errno != EINTR))); + if(p < 0) + { + if(errno == EINTR) + continue; + die("wait4: %m"); + } + } while (p == 0); + if (p != box_pid) + die("wait4: unknown pid %d exited!", p); + box_pid = 0; + + // Check error pipe if there is an internal error passed from inside the box + char interr[1024]; + int n = read(read_errors_from_fd, interr, sizeof(interr) - 1); + if (n > 0) { interr[n] = 0; die("%s", interr); } - if (WIFEXITED(stat)) + if (WIFEXITED(stat)) { final_stats(&rus); if (WEXITSTATUS(stat)) - { - meta_printf("exitcode:%d\n", WEXITSTATUS(stat)); - err("RE: Exited with error status %d", WEXITSTATUS(stat)); - } + { + meta_printf("exitcode:%d\n", WEXITSTATUS(stat)); + err("RE: Exited with error status %d", WEXITSTATUS(stat)); + } if (timeout && total_ms > timeout) err("TO: Time limit exceeded"); if (wall_timeout && wall_ms > wall_timeout) @@ -1152,21 +1192,20 @@ box_keeper(void) wall_ms/1000, wall_ms%1000); box_exit(0); } - else if (WIFSIGNALED(stat)) + else if (WIFSIGNALED(stat)) { meta_printf("exitsig:%d\n", WTERMSIG(stat)); final_stats(&rus); err("SG: Caught fatal signal %d", WTERMSIG(stat)); } - else if (WIFSTOPPED(stat)) + else if (WIFSTOPPED(stat)) { meta_printf("exitsig:%d\n", WSTOPSIG(stat)); final_stats(&rus); err("SG: Stopped by signal %d", WSTOPSIG(stat)); } - else - die("wait4: unknown status %x, giving up!", stat); - } + else + die("wait4: unknown status %x, giving up!", stat); } /*** The process running inside the box ***/ @@ -1437,7 +1476,7 @@ enum opt_code { OPT_CG_TIMING, }; -static const char short_opts[] = "b:c:d:eE:i:k:m:M:o:p::q:r:t:vw:x:"; +static const char short_opts[] = "b:c:d:eE:i:k:m:M:o:p::q:r:t:vw:x:s:"; static const struct option long_opts[] = { { "box-id", 1, NULL, 'b' }, @@ -1465,6 +1504,7 @@ static const struct option long_opts[] = { { "verbose", 0, NULL, 'v' }, { "version", 0, NULL, OPT_VERSION }, { "wall-time", 1, NULL, 'w' }, + { "soft-mem", 1, NULL, 's'}, { NULL, 0, NULL, 0 } }; @@ -1545,6 +1585,9 @@ main(int argc, char **argv) break; case 'x': extra_timeout = 1000*atof(optarg); + break; + case 's': + soft_memory_limit = atoi(optarg); break; case OPT_INIT: case OPT_RUN: