void luasandbox_timer_stop(luasandbox_timer_set * lts) { struct timespec usage, delta; if (lts->is_running) { lts->is_running = 0; } else { return; } // Make sure timers aren't paused, and extract the delta luasandbox_timer_unpause(lts); delta = lts->pause_delta; luasandbox_timer_zero(<s->pause_delta); // Stop the interval timers and save the time remaining if (lts->emergency_running) { luasandbox_timer_stop_one(<s->emergency_timer, <s->emergency_remaining); lts->emergency_running = 0; } if (lts->normal_running) { luasandbox_timer_stop_one(<s->normal_timer, <s->normal_remaining); lts->normal_running = 0; luasandbox_timer_add(<s->normal_remaining, &delta); } // Update the usage luasandbox_update_usage(lts); clock_gettime(LUASANDBOX_CLOCK_ID, &usage); luasandbox_timer_subtract(&usage, <s->usage_start); luasandbox_timer_add(<s->usage, &usage); luasandbox_timer_subtract(<s->usage, &delta); }
void luasandbox_timer_get_usage(luasandbox_timer_set * lts, struct timespec * ts) { struct timespec delta; if (lts->is_running) { luasandbox_update_usage(lts); } *ts = lts->usage; // Subtract the pause delta from the usage luasandbox_timer_subtract(ts, <s->pause_delta); // If currently paused, subtract the time-since-pause too if (!luasandbox_timer_is_zero(<s->pause_start)) { clock_gettime(LUASANDBOX_CLOCK_ID, &delta); luasandbox_timer_subtract(&delta, <s->pause_start); luasandbox_timer_subtract(ts, &delta); } }
static void luasandbox_update_usage(luasandbox_timer_set * lts) { struct timespec current, usage; clock_gettime(LUASANDBOX_CLOCK_ID, ¤t); usage = current; luasandbox_timer_subtract(&usage, <s->usage_start); luasandbox_timer_add(<s->usage, &usage); lts->usage_start = current; }
static void luasandbox_timer_handle_signal(int signo, siginfo_t * info, void * context) { luasandbox_timer_callback_data * data; if (signo != LUASANDBOX_SIGNAL || info->si_code != SI_TIMER || !info->si_value.sival_ptr) { return; } data = (luasandbox_timer_callback_data*)info->si_value.sival_ptr; lua_State * L = data->sandbox->state; if (data->type == LUASANDBOX_TIMER_EMERGENCY) { sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); data->sandbox->timed_out = 1; data->sandbox->emergency_timed_out = 1; if (data->sandbox->in_php) { // The whole PHP request context is dirty now. We need to kill it, // like what happens if there is a max_execution_time timeout. zend_error(E_ERROR, "The maximum execution time for a Lua sandbox script " "was exceeded and a PHP callback failed to return"); } else { // The Lua state is dirty now and can't be used again. lua_pushstring(L, "emergency timeout"); luasandbox_wrap_fatal(L); lua_error(L); } } else { luasandbox_timer_set * lts = &data->sandbox->timer; if (luasandbox_timer_is_paused(lts)) { // The timer is paused. luasandbox_timer_unpause will reschedule when unpaused. clock_gettime(LUASANDBOX_CLOCK_ID, <s->normal_expired_at); } else if (!luasandbox_timer_is_zero(<s->pause_delta)) { // The timer is not paused, but we have a pause delta. Reschedule. luasandbox_timer_subtract(<s->usage, <s->pause_delta); lts->normal_remaining = lts->pause_delta; luasandbox_timer_zero(<s->pause_delta); luasandbox_timer_set_one_time(<s->normal_timer, <s->normal_remaining); } else { // Set a hook which will terminate the script execution in a graceful way data->sandbox->timed_out = 1; lua_sethook(L, luasandbox_timer_timeout_hook, LUA_MASKCOUNT | LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE, 1); } } }
void luasandbox_timer_unpause(luasandbox_timer_set * lts) { struct timespec delta; if (!luasandbox_timer_is_zero(<s->pause_start)) { clock_gettime(LUASANDBOX_CLOCK_ID, &delta); luasandbox_timer_subtract(&delta, <s->pause_start); if (luasandbox_timer_is_zero(<s->normal_expired_at)) { // Easy case, timer didn't expire while paused. Throw the whole delta // into pause_delta for later timer and usage adjustment. luasandbox_timer_add(<s->pause_delta, &delta); luasandbox_timer_zero(<s->pause_start); } else { // If the normal limit expired, we need to fold the whole // accumulated delta into usage immediately, and then restart the // timer with the portion before the expiry. // adjust usage luasandbox_timer_subtract(<s->usage, &delta); luasandbox_timer_subtract(<s->usage, <s->pause_delta); // calculate timer delta delta = lts->normal_expired_at; luasandbox_timer_subtract(&delta, <s->pause_start); luasandbox_timer_add(&delta, <s->pause_delta); // Zero out pause vars and expired timestamp (since we handled it) luasandbox_timer_zero(<s->pause_start); luasandbox_timer_zero(<s->pause_delta); luasandbox_timer_zero(<s->normal_expired_at); // Restart timer lts->normal_remaining = delta; luasandbox_timer_set_one_time(<s->normal_timer, <s->normal_remaining); } } }
static void luasandbox_timer_handle_limiter(luasandbox_timer * lt) { lua_State * L = lt->sandbox->state; luasandbox_timer_set * lts = <->sandbox->timer; if (luasandbox_timer_is_paused(lts)) { // The timer is paused. luasandbox_timer_unpause will reschedule when unpaused. // Note that we need to use lt->clock_id here since CLOCK_THREAD_CPUTIME_ID // would get the time usage of the timer thread rather than the Lua thread. clock_gettime(lt->clock_id, <s->limiter_expired_at); } else if (!luasandbox_timer_is_zero(<s->pause_delta)) { // The timer is not paused, but we have a pause delta. Reschedule. luasandbox_timer_subtract(<s->usage, <s->pause_delta); lts->limiter_remaining = lts->pause_delta; luasandbox_timer_zero(<s->pause_delta); luasandbox_timer_set_one_time(lts->limiter_timer, <s->limiter_remaining); } else { // Set a hook which will terminate the script execution in a graceful way lt->sandbox->timed_out = 1; lua_sethook(L, luasandbox_timer_timeout_hook, LUA_MASKCOUNT | LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE, 1); } }