/** * Abort current process, producing an abnormal program termination. * The function raises the SIGABRT signal. */ void __attr_noreturn___ __attr_used___ abort (void) { syscall_1 (SYSCALL_NO (close), (long int) stdin); syscall_1 (SYSCALL_NO (close), (long int) stdout); syscall_1 (SYSCALL_NO (close), (long int) stderr); raise (SIGABRT); while (true) { /* unreachable */ } } /* abort */
/** * Exit - cause normal process termination with specified status code */ void __attr_noreturn___ __attr_used___ exit (int status) /**< status code */ { syscall_1 (SYSCALL_NO (close), (long int) stdin); syscall_1 (SYSCALL_NO (close), (long int) stdout); syscall_1 (SYSCALL_NO (close), (long int) stderr); syscall_1 (SYSCALL_NO (exit_group), status); while (true) { /* unreachable */ } } /* exit */
/** * fwrite * * @return number of elements written */ size_t fwrite (const void *ptr, /**< data to write */ size_t size, /**< size of elements to write */ size_t nmemb, /**< number of elements */ FILE *stream) /**< stream pointer */ { size_t bytes_written = 0; if (size == 0) { return 0; } do { long int ret = syscall_3 (SYSCALL_NO (write), (long int) stream, (long int) ((uint8_t *) ptr + bytes_written), (long int) (size * nmemb - bytes_written)); bytes_written += (size_t) ret; } while (bytes_written != size * nmemb); return bytes_written / size; } /* fwrite */
/** * fread * * @return number of elements read */ size_t fread (void *ptr, /**< address of buffer to read to */ size_t size, /**< size of elements to read */ size_t nmemb, /**< number of elements to read */ FILE *stream) /**< stream pointer */ { long int ret; size_t bytes_read = 0; if (size == 0) { return 0; } do { ret = syscall_3 (SYSCALL_NO (read), (long int) stream, (long int) ((uint8_t *) ptr + bytes_read), (long int) (size * nmemb - bytes_read)); bytes_read += (size_t) ret; } while (bytes_read != size * nmemb && ret != 0); return bytes_read / size; } /* fread */
/** * fclose * * @return 0 - upon successful completion, * non-zero value - otherwise. */ int fclose (FILE *fp) /**< stream pointer */ { syscall_2 (SYSCALL_NO (close), (long int) fp, 0); return 0; } /* fclose */
/** * Exit - cause normal process termination with specified status code */ void __attr_noreturn___ __attr_used___ exit (int status) /**< status code */ { #ifdef ENABLE_INIT_FINI libc_fini_array (); #endif /* ENABLE_INIT_FINI */ syscall_1 (SYSCALL_NO (close), (long int) stdin); syscall_1 (SYSCALL_NO (close), (long int) stdout); syscall_1 (SYSCALL_NO (close), (long int) stderr); syscall_1 (SYSCALL_NO (exit_group), status); while (true) { /* unreachable */ } } /* exit */
/** * Setup new memory limits */ void jrt_set_mem_limits (size_t data_size, /**< limit for data + bss + brk heap */ size_t stack_size) /**< limit for stack */ { struct { unsigned long long rlim_cur; unsigned long long rlim_max; } data_limit = { data_size, data_size }; struct { unsigned long long rlim_cur; unsigned long long rlim_max; } stack_limit = { stack_size, stack_size }; long int ret; #if defined (__TARGET_HOST_x64) ret = syscall_2 (SYSCALL_NO (setrlimit), RLIMIT_DATA, (intptr_t) &data_limit); assert (ret == 0); ret = syscall_2 (SYSCALL_NO (setrlimit), RLIMIT_STACK, (intptr_t) &stack_limit); assert (ret == 0); #elif defined (__TARGET_HOST_ARMv7) ret = syscall_3 (SYSCALL_NO (prlimit64), 0, RLIMIT_DATA, (intptr_t) &data_limit); assert (ret == 0); ret = syscall_3 (SYSCALL_NO (prlimit64), 0, RLIMIT_STACK, (intptr_t) &stack_limit); assert (ret == 0); #elif defined (__TARGET_HOST_x86) # error "__TARGET_HOST_x86 case is not implemented" #else /* !__TARGET_HOST_x64 && !__TARGET_HOST_ARMv7 && !__TARGET_HOST_x86 */ # error "!__TARGET_HOST_x64 && !__TARGET_HOST_ARMv7 && !__TARGET_HOST_x86" #endif /* __TARGET_HOST_x64 */ } /* jrt_set_mem_limits */
/** * Send a signal to the current process. */ int __attr_used___ raise (int sig) { return (int) syscall_2 (SYSCALL_NO (kill), syscall_0 (SYSCALL_NO (getpid)), sig); } /* raise */
/** * This function can get the time as well as a timezone. * * @return 0 if success, -1 otherwise */ int gettimeofday (void *tp, /**< struct timeval */ void *tzp) /**< struct timezone */ { return (int) syscall_2 (SYSCALL_NO (gettimeofday), (long int) tp, (long int) tzp); } /* gettimeofday */
/** * fopen * * @return FILE pointer - upon successful completion, * NULL - otherwise */ FILE * fopen (const char *path, /**< file path */ const char *mode) /**< file open mode */ { bool may_read = false; bool may_write = false; bool truncate = false; bool create_if_not_exist = false; bool position_at_end = false; assert (path != NULL && mode != NULL); assert (mode[1] == '+' || mode[1] == '\0'); switch (mode[0]) { case 'r': { may_read = true; may_write = (mode[1] == '+'); break; } case 'w': { may_write = true; truncate = true; create_if_not_exist = true; may_read = (mode[1] == '+'); break; } case 'a': { may_write = true; position_at_end = true; create_if_not_exist = true; if (mode[1] == '+') { assert (false && "unsupported mode a+"); } break; } default: { assert (false && "unsupported mode"); } } int flags = 0; int access = S_IRUSR | S_IWUSR; if (may_read && !may_write) { flags = O_RDONLY; } else if (!may_read && may_write) { flags = O_WRONLY; } else { assert (may_read && may_write); flags = O_RDWR; } if (truncate) { flags |= O_TRUNC; } if (create_if_not_exist) { flags |= O_CREAT; } if (position_at_end) { flags |= O_APPEND; } long int ret = syscall_3 (SYSCALL_NO (open), (long int) path, flags, access); return ((ret < 0) ? NULL : (void *) (uintptr_t) (ret)); } /* fopen */
int sandbox_excute(sandbox_t *psbox) { pid_t child; child = fork(); if(child < 0) { psbox->result = INTERNAL_ERROR; __TRACE_LN(__TRACE_KEY, "Error : fork target process failed"); return -1; } else if(child == 0) { _exit(_sandbox_excute(&psbox->task)); } else { __TRACE_LN(__TRACE_DBG, "child pid : %d", child); pid_t wait_result = 0; int wait_status = 0; proc_t proc_data = { 0 }; int in_syscall = 1; event_t e = { 0 }; action_t action = { 0 }; /* Have signals kill the prisoner but not self (if possible). */ /* In fact, I have deleted the '-' before "child" so this may not be necessary */ sig_t terminate_signal; sig_t interrupt_signal; sig_t quit_signal; terminate_signal = signal(SIGTERM, SIG_IGN); interrupt_signal = signal(SIGINT, SIG_IGN); quit_signal = signal(SIGQUIT, SIG_IGN); /* Get wallclock start time */ gettimeofday(&psbox->stat.start_timestamp, NULL); /* get initial resource usage */ wait_result = wait4(child, &wait_status, 0, &psbox->stat.ru); memcpy(&psbox->stat.init_ru, &psbox->stat.ru, sizeof(struct rusage)); /* trace loop */ __TRACE_LN(__TRACE_DBG, "Enter trace loop"); bool internal_error = false; do{ if(!wait_result) /* wait_result == 0 : noting happent */ { goto cont; } memset(&e, 0, sizeof(event_t)); memset(&action, 0, sizeof(action_t)); if(WIFSTOPPED(wait_status)) { switch(WSTOPSIG(wait_status)) { case SIGALRM: POST_EVENT(e, EVENT_QUOTA, QUOTA_WALLCLOCK); break; case SIGXCPU: case SIGPROF: case SIGVTALRM: POST_EVENT(e, EVENT_QUOTA, QUOTA_CPUTIME); break; case SIGML: POST_EVENT(e, EVENT_QUOTA, QUOTA_MEMORY); break; case SIGXFSZ: POST_EVENT(e, EVENT_QUOTA, QUOTA_OUTPUT); break; case SIGTRAP: if(!proc_probe(child, &proc_data)) { /* error */ internal_error = true; psbox->result = INTERNAL_ERROR; kill(child, SIGKILL); __TRACE_LN(__TRACE_KEY, "Internal Error : porc_probe() failed"); break; } if(psbox->stat.vsize_peak < proc_data.vsize) { psbox->stat.vsize_peak = proc_data.vsize; if(psbox->stat.vsize_peak > psbox->task.quota[QUOTA_MEMORY]) { kill(child, SIGML); } } /* system call or system call return */ if(in_syscall) { in_syscall = 0; POST_EVENT(e, EVENT_SYSTEM_CALL_RETURN, SYSCALL_NO(&proc_data), SYSCALL_RETVAL(&proc_data)); } else { in_syscall = 1; POST_EVENT(e, EVENT_SYSTEM_CALL, SYSCALL_NO(&proc_data), SYSCALL_ARG1(&proc_data), SYSCALL_ARG2(&proc_data), SYSCALL_ARG3(&proc_data), SYSCALL_ARG4(&proc_data), SYSCALL_ARG5(&proc_data)); } break; default: POST_EVENT(e, EVENT_SIGNAL, WSTOPSIG(wait_status)); } } else if(WIFSIGNALED(wait_status)) { POST_EVENT(e, EVENT_SIGNAL, WTERMSIG(wait_status)); } else if(WIFEXITED(wait_status)) { psbox->stat.data.exitcode = WEXITSTATUS(wait_status); POST_EVENT(e, EVENT_EXIT, WEXITSTATUS(wait_status)); } /* sink event */ psbox->policy(&e, &action); __TRACE(__TRACE_DBG, "Event type : %s", event_name(e.type)); __TRACE_LN(__TRACE_DBG, "\tEvent data : %ld %ld %ld %ld %ld %ld", e.data._bitmap_.a, e.data._bitmap_.b, e.data._bitmap_.c, e.data._bitmap_.d, e.data._bitmap_.e, e.data._bitmap_.f); __TRACE(__TRACE_DBG, "Action type : %s", action_name(action.type)); __TRACE_LN(__TRACE_DBG, "\tAction data : %ld %ld", action.data._bitmap_.a, action.data._bitmap_.b); /* action */ switch(action.type) { case ACTION_CONTINUE: if(!trace_next(&proc_data)) { internal_error = true; psbox->result = INTERNAL_ERROR; kill(child, SIGKILL); __TRACE_LN(__TRACE_KEY, "Internal Error : trace_next() failed"); break; } goto cont; case ACTION_KILL: trace_kill(&proc_data, 0); psbox->result = action.data._kill.result; if(psbox->result == RUNTIME_ERROR) { psbox->stat.data.signo = action.data._kill.data; } else if(psbox->result == RESTRICTED_FUNCTION) { psbox->stat.data.rf = action.data._kill.data; } kill(child, SIGKILL); break; case ACTION_EXIT: psbox->stat.data.exitcode = action.data._exit.code; if(psbox->stat.data.exitcode) { __TRACE_LN(__TRACE_DBG, "exit code : %ld", psbox->stat.data.exitcode); psbox->result = ABNORMAL_TERMINATION; } /* else { psbox->result = PENDED; } */ break; } break; cont: /* trace infomation */ __TRACE_LN(__TRACE_DBG, "----------------------wait4()----------------------"); }while(!internal_error && (wait_result = wait4(child, &wait_status, 0, &psbox->stat.ru)) >= 0); /* if the sandbox's result is PENDED, the prisoned process is exited nomally */ /* Get wallclock stop time (call a second time to compensate overhead) */ gettimeofday(&psbox->stat.end_timestamp, NULL); /* Restore signal handlers */ signal(SIGTERM, interrupt_signal); signal(SIGINT, interrupt_signal); signal(SIGQUIT, quit_signal); } return 0; }