void luasandbox_timer_start(luasandbox_timer_set * lts) { if (lts->is_running) { // Already running return; } lts->is_running = 1; // Initialise usage timer clock_gettime(LUASANDBOX_CLOCK_ID, <s->usage_start); // Create normal timer if requested if (!luasandbox_timer_is_zero(<s->normal_remaining)) { lts->normal_running = 1; luasandbox_timer_create_one(<s->normal_timer, lts->sandbox, LUASANDBOX_TIMER_NORMAL); luasandbox_timer_set_one_time(<s->normal_timer, <s->normal_remaining); } else { lts->normal_running = 0; } // Create emergency timer if requested if (!luasandbox_timer_is_zero(<s->emergency_remaining)) { lts->emergency_running = 1; luasandbox_timer_create_one(<s->emergency_timer, lts->sandbox, LUASANDBOX_TIMER_EMERGENCY); luasandbox_timer_set_one_time(<s->emergency_timer, <s->emergency_remaining); } else { lts->emergency_running = 0; } }
int luasandbox_timer_is_expired(luasandbox_timer_set * lts) { if (!luasandbox_timer_is_zero(<s->limiter_limit)) { if (luasandbox_timer_is_zero(<s->limiter_remaining)) { return 1; } } return 0; }
int luasandbox_timer_is_expired(luasandbox_timer_set * lts) { if (!luasandbox_timer_is_zero(<s->normal_limit)) { if (luasandbox_timer_is_zero(<s->normal_remaining)) { return 1; } } if (!luasandbox_timer_is_zero(<s->emergency_limit)) { if (luasandbox_timer_is_zero(<s->emergency_remaining)) { return 1; } } return 0; }
int luasandbox_timer_start(luasandbox_timer_set * lts) { if (lts->is_running) { // Already running return 1; } lts->is_running = 1; // Initialise usage timer clock_gettime(LUASANDBOX_CLOCK_ID, <s->usage_start); // Create limiter timer if requested if (!luasandbox_timer_is_zero(<s->limiter_remaining)) { luasandbox_timer * timer = luasandbox_timer_create_one( lts->sandbox, LUASANDBOX_TIMER_LIMITER); if (!timer) { lts->limiter_running = 0; return 0; } lts->limiter_timer = timer; lts->limiter_running = 1; luasandbox_timer_set_one_time(timer, <s->limiter_remaining); } else { lts->limiter_running = 0; } return 1; }
static void luasandbox_timer_set_one_time(luasandbox_timer * lt, struct timespec * ts) { struct itimerspec its; luasandbox_timer_zero(&its.it_interval); its.it_value = *ts; if (luasandbox_timer_is_zero(&its.it_value)) { // Sanity check: make sure there is at least 1 nanosecond on the timer. its.it_value.tv_nsec = 1; } timer_settime(lt->timer, 0, &its, NULL); }
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); } } }
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_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); } }
int luasandbox_timer_is_paused(luasandbox_timer_set * lts) { return !luasandbox_timer_is_zero(<s->pause_start); }
void luasandbox_timer_pause(luasandbox_timer_set * lts) { if (luasandbox_timer_is_zero(<s->pause_start)) { clock_gettime(LUASANDBOX_CLOCK_ID, <s->pause_start); } }