예제 #1
0
/**
 * @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();
  }
}
예제 #2
0
/**
 * @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();
  }
}
예제 #3
0
/**
 * @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();
  }
}
예제 #4
0
/**
 * @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();
  }
}
예제 #5
0
/**
 * @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();
  }
}
예제 #6
0
파일: sandbox.c 프로젝트: sw072/myoj
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;
}
예제 #7
0
파일: sandbox.c 프로젝트: LiuCihang/OJ
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()");
}