-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add support for limiting maximum execution time based on wall-time #6504
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1260,9 +1260,14 @@ ZEND_API zend_result zend_eval_string_ex(const char *str, zval *retval_ptr, cons | |||||
/* }}} */ | ||||||
|
||||||
static void zend_set_timeout_ex(zend_long seconds, bool reset_signals); | ||||||
static void zend_set_wall_timeout_ex(zend_long seconds, bool reset_signals); | ||||||
|
||||||
ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void) /* {{{ */ | ||||||
{ | ||||||
zend_long original_timed_out = EG(timed_out); | ||||||
zend_long original_timeout_seconds = EG(timeout_seconds); | ||||||
zend_long original_wall_timeout_seconds = EG(wall_timeout_seconds); | ||||||
|
||||||
#if defined(PHP_WIN32) | ||||||
# ifndef ZTS | ||||||
/* No action is needed if we're timed out because zero seconds are | ||||||
|
@@ -1278,10 +1283,18 @@ ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void) /* {{{ */ | |||||
# endif | ||||||
#else | ||||||
EG(timed_out) = 0; | ||||||
zend_set_timeout_ex(0, 1); | ||||||
if (original_timed_out == 1) { | ||||||
zend_set_timeout_ex(0, 1); | ||||||
} else { | ||||||
zend_set_wall_timeout_ex(0, 1); | ||||||
} | ||||||
#endif | ||||||
|
||||||
zend_error_noreturn(E_ERROR, "Maximum execution time of " ZEND_LONG_FMT " second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s"); | ||||||
if (original_timed_out == 1) { | ||||||
zend_error_noreturn(E_ERROR, "Maximum execution time of " ZEND_LONG_FMT " second%s exceeded", original_timeout_seconds, original_timeout_seconds == 1 ? "" : "s"); | ||||||
} else { | ||||||
zend_error_noreturn(E_ERROR, "Maximum execution wall-time of " ZEND_LONG_FMT " second%s exceeded", original_wall_timeout_seconds, original_wall_timeout_seconds == 1 ? "" : "s"); | ||||||
} | ||||||
} | ||||||
/* }}} */ | ||||||
|
||||||
|
@@ -1337,6 +1350,57 @@ static void zend_timeout_handler(int dummy) /* {{{ */ | |||||
/* }}} */ | ||||||
#endif | ||||||
|
||||||
#ifndef ZEND_WIN32 | ||||||
static void zend_wall_timeout_handler(int dummy) /* {{{ */ | ||||||
{ | ||||||
#ifndef ZTS | ||||||
if (EG(timed_out)) { | ||||||
/* Die on hard timeout */ | ||||||
const char *error_filename = NULL; | ||||||
uint32_t error_lineno = 0; | ||||||
char log_buffer[2048]; | ||||||
int output_len = 0; | ||||||
|
||||||
if (zend_is_compiling()) { | ||||||
error_filename = ZSTR_VAL(zend_get_compiled_filename()); | ||||||
error_lineno = zend_get_compiled_lineno(); | ||||||
} else if (zend_is_executing()) { | ||||||
error_filename = zend_get_executed_filename(); | ||||||
if (error_filename[0] == '[') { /* [no active file] */ | ||||||
error_filename = NULL; | ||||||
error_lineno = 0; | ||||||
} else { | ||||||
error_lineno = zend_get_executed_lineno(); | ||||||
} | ||||||
} | ||||||
if (!error_filename) { | ||||||
error_filename = "Unknown"; | ||||||
} | ||||||
|
||||||
output_len = snprintf(log_buffer, sizeof(log_buffer), "\nFatal error: Maximum execution wall-time of " ZEND_LONG_FMT "+" ZEND_LONG_FMT " seconds exceeded (terminated) in %s on line %d\n", EG(wall_timeout_seconds), EG(hard_timeout), error_filename, error_lineno); | ||||||
if (output_len > 0) { | ||||||
zend_quiet_write(2, log_buffer, MIN(output_len, sizeof(log_buffer))); | ||||||
} | ||||||
_exit(124); | ||||||
} | ||||||
#endif | ||||||
|
||||||
if (zend_on_timeout) { | ||||||
zend_on_timeout(EG(wall_timeout_seconds)); | ||||||
} | ||||||
|
||||||
EG(timed_out) = 2; | ||||||
EG(vm_interrupt) = 1; | ||||||
|
||||||
#ifndef ZTS | ||||||
if (EG(hard_timeout)) { | ||||||
zend_set_wall_timeout_ex(EG(hard_timeout), 1); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Regarding https://externals.io/message/112492#112783, my understanding is that the following happens:
Another thing I've just become unsure about is whether it is really a good idea to restart the real-time timer instead of the original CPU-time one:
Suggested change
|
||||||
} | ||||||
#endif | ||||||
} | ||||||
/* }}} */ | ||||||
#endif | ||||||
|
||||||
#ifdef ZEND_WIN32 | ||||||
VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out) | ||||||
{ | ||||||
|
@@ -1431,15 +1495,89 @@ static void zend_set_timeout_ex(zend_long seconds, bool reset_signals) /* {{{ */ | |||||
} | ||||||
/* }}} */ | ||||||
|
||||||
void zend_set_timeout(zend_long seconds, bool reset_signals) /* {{{ */ | ||||||
static void zend_set_wall_timeout_ex(zend_long seconds, bool reset_signals) /* {{{ */ | ||||||
{ | ||||||
#ifdef ZEND_WIN32 | ||||||
zend_executor_globals *eg; | ||||||
|
||||||
if (!seconds) { | ||||||
return; | ||||||
} | ||||||
|
||||||
/* Don't use ChangeTimerQueueTimer() as it will not restart an expired | ||||||
kocsismate marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
* timer, so we could end up with just an ignored timeout. Instead | ||||||
* delete and recreate. */ | ||||||
if (NULL != tq_timer) { | ||||||
if (!DeleteTimerQueueTimer(NULL, tq_timer, INVALID_HANDLE_VALUE)) { | ||||||
tq_timer = NULL; | ||||||
zend_error_noreturn(E_ERROR, "Could not delete queued timer"); | ||||||
return; | ||||||
} | ||||||
tq_timer = NULL; | ||||||
} | ||||||
|
||||||
/* XXX passing NULL means the default timer queue provided by the system is used */ | ||||||
eg = ZEND_MODULE_GLOBALS_BULK(executor); | ||||||
if (!CreateTimerQueueTimer(&tq_timer, NULL, (WAITORTIMERCALLBACK)tq_timer_cb, (VOID*)eg, seconds*1000, 0, WT_EXECUTEONLYONCE)) { | ||||||
tq_timer = NULL; | ||||||
zend_error_noreturn(E_ERROR, "Could not queue new timer"); | ||||||
return; | ||||||
} | ||||||
#elif defined(HAVE_SETITIMER) | ||||||
{ | ||||||
struct itimerval t_r; /* timeout requested */ | ||||||
int signo; | ||||||
|
||||||
if(seconds) { | ||||||
t_r.it_value.tv_sec = seconds; | ||||||
t_r.it_value.tv_usec = t_r.it_interval.tv_sec = t_r.it_interval.tv_usec = 0; | ||||||
|
||||||
setitimer(ITIMER_REAL, &t_r, NULL); | ||||||
} | ||||||
signo = SIGALRM; | ||||||
|
||||||
if (reset_signals) { | ||||||
|
||||||
# ifdef ZEND_SIGNALS | ||||||
zend_signal(signo, zend_wall_timeout_handler); | ||||||
# else | ||||||
sigset_t sigset; | ||||||
# ifdef HAVE_SIGACTION | ||||||
struct sigaction act; | ||||||
|
||||||
act.sa_handler = zend_wall_timeout_handler; | ||||||
sigemptyset(&act.sa_mask); | ||||||
act.sa_flags = SA_RESETHAND | SA_NODEFER; | ||||||
sigaction(signo, &act, NULL); | ||||||
# else | ||||||
signal(signo, zend_wall_timeout_handler); | ||||||
# endif /* HAVE_SIGACTION */ | ||||||
sigemptyset(&sigset); | ||||||
sigaddset(&sigset, signo); | ||||||
sigprocmask(SIG_UNBLOCK, &sigset, NULL); | ||||||
# endif /* ZEND_SIGNALS */ | ||||||
} | ||||||
} | ||||||
#endif /* HAVE_SETITIMER */ | ||||||
} | ||||||
/* }}} */ | ||||||
|
||||||
void zend_set_timeout(zend_long seconds, bool reset_signals) /* {{{ */ | ||||||
{ | ||||||
EG(timeout_seconds) = seconds; | ||||||
zend_set_timeout_ex(seconds, reset_signals); | ||||||
EG(timed_out) = 0; | ||||||
} | ||||||
/* }}} */ | ||||||
|
||||||
void zend_set_wall_timeout(zend_long seconds, bool reset_signals) /* {{{ */ | ||||||
{ | ||||||
EG(wall_timeout_seconds) = seconds; | ||||||
zend_set_wall_timeout_ex(seconds, reset_signals); | ||||||
EG(timed_out) = 0; | ||||||
} | ||||||
/* }}} */ | ||||||
|
||||||
void zend_unset_timeout(void) /* {{{ */ | ||||||
{ | ||||||
#ifdef ZEND_WIN32 | ||||||
|
@@ -1469,6 +1607,31 @@ void zend_unset_timeout(void) /* {{{ */ | |||||
} | ||||||
/* }}} */ | ||||||
|
||||||
void zend_unset_wall_timeout(void) /* {{{ */ | ||||||
{ | ||||||
#ifdef ZEND_WIN32 | ||||||
if (NULL != tq_timer) { | ||||||
if (!DeleteTimerQueueTimer(NULL, tq_timer, INVALID_HANDLE_VALUE)) { | ||||||
EG(timed_out) = 0; | ||||||
tq_timer = NULL; | ||||||
zend_error_noreturn(E_ERROR, "Could not delete queued timer"); | ||||||
return; | ||||||
} | ||||||
tq_timer = NULL; | ||||||
} | ||||||
#elif defined(HAVE_SETITIMER) | ||||||
if (EG(wall_timeout_seconds)) { | ||||||
struct itimerval no_timeout; | ||||||
|
||||||
no_timeout.it_value.tv_sec = no_timeout.it_value.tv_usec = no_timeout.it_interval.tv_sec = no_timeout.it_interval.tv_usec = 0; | ||||||
|
||||||
setitimer(ITIMER_REAL, &no_timeout, NULL); | ||||||
} | ||||||
#endif | ||||||
EG(timed_out) = 0; | ||||||
} | ||||||
/* }}} */ | ||||||
|
||||||
zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type) /* {{{ */ | ||||||
{ | ||||||
zend_class_entry *ce, *scope; | ||||||
|
Uh oh!
There was an error while loading. Please reload this page.