/** * @brief Inserts in the circular debug trace buffer an halt record. * * @param[in] reason the halt error string * * @notapi */ void _trace_halt(const char *reason) { if ((ch.dbg.trace_buffer.suspended & CH_DBG_TRACE_MASK_HALT) == 0U) { ch.dbg.trace_buffer.ptr->type = CH_TRACE_TYPE_HALT; ch.dbg.trace_buffer.ptr->state = 0; ch.dbg.trace_buffer.ptr->u.halt.reason = reason; trace_next(); } }
/** * @brief Inserts in the circular debug trace buffer a context switch record. * * @param[in] otp the thread being switched out * * @notapi */ void _dbg_trace_switch(thread_t *otp) { if ((ch.dbg.trace_buffer.suspended & CH_TRACE_SUSPEND_SWITCH) == 0U) { ch.dbg.trace_buffer.ptr->type = CH_TRACE_TYPE_SWITCH; ch.dbg.trace_buffer.ptr->state = (uint8_t)otp->state; ch.dbg.trace_buffer.ptr->u.sw.ntp = currp; ch.dbg.trace_buffer.ptr->u.sw.wtobjp = otp->u.wtobjp; trace_next(); } }
/** * @brief Inserts in the circular debug trace buffer an ISR-leave record. * * @param[in] isr name of the isr * * @notapi */ void _trace_isr_leave(const char *isr) { if ((ch.dbg.trace_buffer.suspended & CH_DBG_TRACE_MASK_ISR) == 0U) { port_lock_from_isr(); ch.dbg.trace_buffer.ptr->type = CH_TRACE_TYPE_ISR_LEAVE; ch.dbg.trace_buffer.ptr->state = 0U; ch.dbg.trace_buffer.ptr->u.isr.name = isr; trace_next(); port_unlock_from_isr(); } }
/** * @brief Inserts in the circular debug trace buffer an ISR-enter record. * * @param[in] isr name of the isr * * @notapi */ void _dbg_trace_isr_enter(const char *isr) { if ((ch.dbg.trace_buffer.suspended & CH_TRACE_SUSPEND_ISR_ENTER) == 0U) { port_lock_from_isr(); ch.dbg.trace_buffer.ptr->type = CH_TRACE_TYPE_ISR_ENTER; ch.dbg.trace_buffer.ptr->state = 0U; ch.dbg.trace_buffer.ptr->u.isr.name = isr; trace_next(); port_unlock_from_isr(); } }
/** * @brief Adds an user trace record to the trace buffer. * * @param[in] up1 user parameter 1 * @param[in] up2 user parameter 2 * * @iclass */ void chDbgWriteTraceI(void *up1, void *up2) { chDbgCheckClassI(); if ((ch.dbg.trace_buffer.suspended & CH_DBG_TRACE_MASK_USER) == 0U) { ch.dbg.trace_buffer.ptr->type = CH_TRACE_TYPE_USER; ch.dbg.trace_buffer.ptr->state = 0; ch.dbg.trace_buffer.ptr->u.user.up1 = up1; ch.dbg.trace_buffer.ptr->u.user.up2 = up2; trace_next(); } }
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; }
static void * __sandbox_tracer(sandbox_t * psbox) { FUNC_BEGIN("sandbox_tracer(%p)", psbox); assert(psbox); #define UPDATE_RESULT(psbox,res) \ {{{ \ if (((psbox)->result) != (result_t)(res)) \ { \ ((psbox)->result) = (result_t)(res); \ } \ DBG("result: %s", s_result_name((result_t)(res))); \ }}} /* UPDATE_RESULT */ #define UPDATE_STATUS(psbox,sta) \ {{{ \ P(&((psbox)->mutex)); \ if (((psbox)->status) != (status_t)(sta)) \ { \ ((psbox)->status) = (status_t)(sta); \ pthread_cond_broadcast(&((psbox)->update)); \ } \ V(&((psbox)->mutex)); \ DBG("status: %s", s_status_name((status_t)(sta))); \ }}} /* UPDATE_STATUS */ #define SIGMLE SIGUSR1 #define TIMEVAL_INPLACE_SUBTRACT(x,y) \ {{{ \ if ((x).tv_usec < (y).tv_usec) \ { \ int nsec = ((y).tv_usec - (x).tv_usec) / 1000000 + 1; \ (x).tv_sec -= nsec; \ (x).tv_usec += 1000000 * nsec; \ } \ if ((x).tv_usec - (y).tv_usec >= 1000000) \ { \ int nsec = ((y).tv_usec - (x).tv_usec) / 1000000; \ (x).tv_usec -= 1000000 * nsec; \ (x).tv_sec += nsec; \ } \ (x).tv_sec -= (y).tv_sec; \ (x).tv_usec -= (y).tv_usec; \ }}} /* TIMEVAL_INPLACE_SUBTRACT */ #define CLEAR_EVENT(pctrl) \ {{{ \ P(&((pctrl)->mutex)); \ ((pctrl)->idle) = true; \ pthread_cond_broadcast(&((pctrl)->sched)); \ V(&((pctrl)->mutex)); \ }}} /* CLEAR_EVENT */ #define POST_EVENT(pctrl,type,x...) \ {{{ \ P(&((pctrl)->mutex)); \ ((pctrl)->event) = (event_t){(S_EVENT ## type), {{x}}}; \ ((pctrl)->idle) = false; \ pthread_cond_broadcast(&((pctrl)->sched)); \ V(&((pctrl)->mutex)); \ }}} /* POST_EVENT */ #ifdef WITH_CUSTOM_MONITOR #define SINK_EVENT(pctrl) \ {{{ \ P(&((pctrl)->mutex)); \ while (!IS_IDLE(pctrl)) \ { \ pthread_cond_wait(&((pctrl)->sched), &((pctrl)->mutex)); \ } \ V(&((pctrl)->mutex)); \ }}} /* SINK_EVENT */ #endif /* WITH_CUSTOM_MONITOR */ #ifdef WITH_NATIVE_MONITOR #define SINK_EVENT(pctrl) \ {{{ \ P(&((pctrl)->mutex)); \ if (IS_IDLE(pctrl)) \ { \ DBG("no event detected"); \ V(&((pctrl)->mutex)); \ goto cont; \ } \ V(&((pctrl)->mutex)); \ \ DBG("detected event: %s {%lu %lu %lu %lu %lu %lu %lu}", \ s_event_type_name((pctrl)->event.type), \ (pctrl)->event.data.__bitmap__.A, \ (pctrl)->event.data.__bitmap__.B, \ (pctrl)->event.data.__bitmap__.C, \ (pctrl)->event.data.__bitmap__.D, \ (pctrl)->event.data.__bitmap__.E, \ (pctrl)->event.data.__bitmap__.F, \ (pctrl)->event.data.__bitmap__.G); \ \ ((policy_entry_t)(pctrl)->policy.entry)(&(pctrl)->policy, \ &(pctrl)->event, \ &(pctrl)->action); \ \ DBG("decided action: %s {%lu %lu}", \ s_action_type_name((pctrl)->action.type), \ (pctrl)->action.data.__bitmap__.A, \ (pctrl)->action.data.__bitmap__.B); \ \ P(&((pctrl)->mutex)); \ ((pctrl)->idle) = true; \ pthread_cond_broadcast(&((pctrl)->sched)); \ V(&((pctrl)->mutex)); \ }}} /* SINK_EVENT */ #endif /* WITH_NATIVE_MONITOR */ DBG("entering: the tracing thread"); /* The controller should contain pid of the prisoner process */ pid_t pid = psbox->ctrl.pid; /* Check if the prisoner process was correctly forked */ if (pid < 0) { WARNING("error forking the prisoner process"); UPDATE_RESULT(psbox, S_RESULT_IE); UPDATE_STATUS(psbox, S_STATUS_FIN); FUNC_RET((void *)&psbox->result, "sandbox_tracer()"); } /* Have signals kill the prisoner but not self (if possible). */ sighandler_t terminate_signal; sighandler_t interrupt_signal; sighandler_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.started, NULL); UPDATE_RESULT(psbox, S_RESULT_PD); UPDATE_STATUS(psbox, S_STATUS_EXE); /* Resume the control logic */ CLEAR_EVENT(&psbox->ctrl); /* Temporary variables. */ struct rusage initru; int waitstatus = 0; pid_t waitresult = 0; proc_t proc = {0}; /* System call stack. The prisoner process initially stops at *SYSRET* mode * after making the first call to SYS_execve. Therefore, we initialize the * stack as it is. */ int sc_stack[16] = {0, SYS_execve}; int sc_top = 1; /* Make an initial wait to verify the first system call as well as to * collect resource usage overhead. */ waitresult = wait4(pid, &waitstatus, 0, &psbox->stat.ru); UPDATE_STATUS(psbox, S_STATUS_BLK); /* Save the initial resource usage for further reference */ memcpy(&initru, &psbox->stat.ru, sizeof(struct rusage)); /* Clear cache in order to increase timing accuracy */ flush_cache(); flush_cache(); /* Entering the tracing loop */ do { /* Trace state refresh of the prisoner program */ UPDATE_STATUS(psbox, S_STATUS_BLK); /* In case nothing happened (possible when the 3rd argument of *wait4* * contains the WNOHANG flag), we just go on with next wait(). */ if (waitresult == 0) { DBG("wait: nothing happened"); goto cont; } /* Figure *net* resource usage (eliminate initru) */ TIMEVAL_INPLACE_SUBTRACT(psbox->stat.ru.ru_utime, initru.ru_utime); TIMEVAL_INPLACE_SUBTRACT(psbox->stat.ru.ru_stime, initru.ru_stime); psbox->stat.ru.ru_majflt -= initru.ru_majflt; psbox->stat.ru.ru_minflt -= initru.ru_minflt; psbox->stat.ru.ru_nswap -= initru.ru_nswap; DBG("ru.ru_utime.tv_sec % 10ld", psbox->stat.ru.ru_utime.tv_sec); DBG("ru.ru_utime.tv_usec % 10ld", psbox->stat.ru.ru_utime.tv_usec); DBG("ru.ru_stime.tv_sec % 10ld", psbox->stat.ru.ru_stime.tv_sec); DBG("ru.ru_stime.tv_usec % 10ld", psbox->stat.ru.ru_stime.tv_usec); DBG("ru.ru_majflt % 10ld", psbox->stat.ru.ru_majflt); DBG("ru.ru_minflt % 10ld", psbox->stat.ru.ru_minflt); DBG("ru.ru_nswap % 10ld", psbox->stat.ru.ru_nswap); /* Raise appropriate events judging each wait status */ if (WIFSTOPPED(waitstatus)) { DBG("wait: stopped (%d)", WSTOPSIG(waitstatus)); psbox->stat.signal = WSTOPSIG(waitstatus); /* Collect additional information of the prisoner process */ if (!proc_probe(pid, PROBE_STAT, &proc)) { WARNING("failed to probe the prisoner process"); kill(-pid, SIGKILL); UPDATE_RESULT(psbox, S_RESULT_IE); goto done; } /* Raise appropriate event judging stop signal */ switch (WSTOPSIG(waitstatus)) { case SIGALRM: /* real timer expired */ POST_EVENT(&psbox->ctrl, _QUOTA, S_QUOTA_WALLCLOCK); break; case SIGXCPU: /* CPU resource limit exceed */ case SIGPROF: /* profile timer expired */ case SIGVTALRM: /* virtual timer expired */ POST_EVENT(&psbox->ctrl, _QUOTA, S_QUOTA_CPU); break; case SIGMLE: /* SIGUSR1 used for reporting ML */ POST_EVENT(&psbox->ctrl, _QUOTA, S_QUOTA_MEMORY); break; case SIGXFSZ: /* Output file size exceeded */ POST_EVENT(&psbox->ctrl, _QUOTA, S_QUOTA_DISK); break; case SIGTRAP: /* Update the tsc instructions counter */ #ifdef WITH_TSC_COUNTER psbox->stat.tsc++; DBG("tsc %010llu", psbox->stat.tsc); #endif /* WITH_TSC_COUNTER */ /* Collect additional information of prisoner process */ if (!proc_probe(pid, PROBE_REGS, &proc)) { WARNING("failed to probe the prisoner process"); kill(-pid, SIGKILL); UPDATE_RESULT(psbox, S_RESULT_IE); goto done; } /* Update sandbox stat with the process runtime info */ { psbox->stat.vsize = proc.vsize; if (psbox->stat.vsize_peak < proc.vsize) { psbox->stat.vsize_peak = proc.vsize; } psbox->stat.rss = proc.rss * getpagesize(); } /* Detect memory usage against quota */ if (psbox->stat.vsize_peak > psbox->task.quota[S_QUOTA_MEMORY]) { kill(-pid, SIGMLE); } /* For `single step' tracing mode, we have to probe the current * op code (i.e. INT80 for i386 platforms) to tell whether the * prisoner process is invoking a system call. For `system call' * tracing mode, however, every *SIGTRAP* indicates a system * call currently being invoked or just returned. */ #ifdef WITH_TSC_COUNTER if (IS_SYSCALL(&proc) || IS_SYSRET(&proc)) #endif /* WITH_TSC_COUNTER */ { int scno = THE_SYSCALL(&proc); if (scno != sc_stack[sc_top]) { sc_stack[++sc_top] = scno; psbox->stat.syscall = scno; SET_IN_SYSCALL(&proc); POST_EVENT(&psbox->ctrl, _SYSCALL, scno, SYSCALL_ARG1(&proc), SYSCALL_ARG2(&proc), SYSCALL_ARG3(&proc), SYSCALL_ARG4(&proc), SYSCALL_ARG5(&proc)); } else { CLR_IN_SYSCALL(&proc); sc_stack[sc_top--] = 0; POST_EVENT(&psbox->ctrl, _SYSRET, scno, SYSRET_RETVAL(&proc)); } } #ifdef WITH_TSC_COUNTER else { goto next; } #endif /* WITH_TSC_COUNTER */ break; default: /* Other runtime error */ POST_EVENT(&psbox->ctrl, _SIGNAL, WSTOPSIG(waitstatus)); break; } } /* stopped */ else if (WIFSIGNALED(waitstatus)) { DBG("wait: signaled (%d)", WTERMSIG(waitstatus)); psbox->stat.signal = WTERMSIG(waitstatus); POST_EVENT(&psbox->ctrl, _SIGNAL, WTERMSIG(waitstatus)); } else if (WIFEXITED(waitstatus)) { DBG("wait: exited (%d)", WEXITSTATUS(waitstatus)); psbox->stat.exitcode = WEXITSTATUS(waitstatus); POST_EVENT(&psbox->ctrl, _EXIT, WEXITSTATUS(waitstatus)); } /* Wait for the policy to determine the next action */ SINK_EVENT(&psbox->ctrl); /* Perform the desired action */ switch (psbox->ctrl.action.type) { case S_ACTION_CONT: next: #ifdef WITH_TSC_COUNTER if (!trace_next(&proc, TRACE_SINGLE_STEP)) #else if (!trace_next(&proc, TRACE_SYSTEM_CALL)) #endif /* WITH_TSC_COUNTER */ { WARNING("trace_next"); kill(-pid, SIGKILL); UPDATE_RESULT(psbox, S_RESULT_IE); break; } /* There is no need to update state here! */ goto cont; /* Continue with next wait() */ case S_ACTION_FINI: UPDATE_RESULT(psbox, psbox->ctrl.action.data._FINI.result); break; case S_ACTION_KILL: /* Using trace_kill can effectively prevent overrun of undesired * behavior, i.e. illegal system call. */ trace_kill(&proc, SIGKILL); UPDATE_RESULT(psbox, psbox->ctrl.action.data._KILL.result); break; } break; /* Exiting the tracing loop! */ cont: /* Wait until the prisoner process is trapped */ UPDATE_STATUS(psbox, S_STATUS_EXE); DBG("----------------------------------------------------------------"); DBG("wait4(%d,%p,%d,%p)", pid, &waitstatus, 0, &psbox->stat.ru); waitresult = waitstatus = 0; } while ((waitresult = wait4(pid, &waitstatus, 0, &psbox->stat.ru)) >= 0); done: /* Get wallclock stop time (call a second time to compensate overhead) */ gettimeofday(&psbox->stat.stopped, NULL); UPDATE_STATUS(psbox, S_STATUS_FIN); /* Resume the control logic */ CLEAR_EVENT(&psbox->ctrl); DBG("leaving: the tracing thread"); /* Restore signal handlers */ signal(SIGTERM, interrupt_signal); signal(SIGINT, interrupt_signal); signal(SIGQUIT, quit_signal); FUNC_RET((void *)&psbox->result, "sandbox_tracer()"); }