From: Dmitry Stogov Date: Wed, 20 Apr 2016 10:52:21 +0000 (+0300) Subject: Safe execution timeout handling. X-Git-Tag: php-7.1.0alpha1~290 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=650c1c0a7d94d3bb052a93407b6e280df9c265a4;p=php Safe execution timeout handling. --- diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 6ba72b324e..be4b039d89 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -2100,22 +2100,17 @@ void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */ } /* }}} */ -#ifdef ZEND_WIN32 static zend_never_inline ZEND_COLD ZEND_NORETURN void ZEND_FASTCALL zend_interrupt(void) /* {{{ */ { zend_timeout(0); } /* }}} */ -# define ZEND_VM_INTERRUPT_CHECK() do { \ +#define ZEND_VM_INTERRUPT_CHECK() do { \ if (UNEXPECTED(EG(timed_out))) { \ zend_interrupt(); \ } \ } while (0) -#else -# define ZEND_VM_INTERRUPT_CHECK() do { \ - } while (0) -#endif /* * Stack Frame Layout (the whole stack frame is allocated at once) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 55981894cc..99cc601950 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -38,6 +38,9 @@ #ifdef HAVE_SYS_TIME_H #include #endif +#ifdef HAVE_UNISTD_H +#include +#endif ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data); ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value); @@ -169,9 +172,7 @@ void init_executor(void) /* {{{ */ zend_objects_store_init(&EG(objects_store), 1024); EG(full_tables_cleanup) = 0; -#ifdef ZEND_WIN32 EG(timed_out) = 0; -#endif EG(exception) = NULL; EG(prev_exception) = NULL; @@ -1183,8 +1184,47 @@ ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name, } /* }}} */ +static void zend_set_timeout_ex(zend_long seconds, int reset_signals); + ZEND_API void zend_timeout(int dummy) /* {{{ */ { + EG(timed_out) = 0; + zend_set_timeout_ex(0, 1); + zend_error_noreturn(E_ERROR, "Maximum execution time of %pd second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s"); +} +/* }}} */ + +#ifndef ZEND_WIN32 +static void zend_timeout_handler(int dummy) /* {{{ */ +{ +#ifndef ZTS + if (EG(timed_out)) { + /* Die on hard timeout */ + const char *error_filename = NULL; + uint error_lineno = 0; + char *log_buffer = NULL; + + 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"; + } + + zend_spprintf(&log_buffer, 0, "\nFatal error: Maximum execution time of %pd+%pd seconds exceeded (terminated) in %s on line %d\n", EG(timeout_seconds), EG(hard_timeout), error_filename, error_lineno); + write(2, log_buffer, strlen(log_buffer)); + _exit(1); + } +#endif if (zend_on_timeout) { #ifdef ZEND_SIGNALS @@ -1199,9 +1239,17 @@ ZEND_API void zend_timeout(int dummy) /* {{{ */ zend_on_timeout(EG(timeout_seconds)); } - zend_error_noreturn(E_ERROR, "Maximum execution time of %pd second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s"); + EG(timed_out) = 1; + +#ifndef ZTS + if (EG(hard_timeout) > 0) { + /* Set hard timeout */ + zend_set_timeout_ex(EG(hard_timeout), 1); + } +#endif } /* }}} */ +#endif #ifdef ZEND_WIN32 VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out) @@ -1224,11 +1272,9 @@ VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out) #define SIGPROF 27 #endif -void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */ +static void zend_set_timeout_ex(zend_long seconds, int reset_signals) /* {{{ */ { - EG(timeout_seconds) = seconds; - #ifdef ZEND_WIN32 if(!seconds) { return; @@ -1239,7 +1285,6 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */ delete and recreate. */ if (NULL != tq_timer) { if (!DeleteTimerQueueTimer(NULL, tq_timer, NULL)) { - EG(timed_out) = 0; tq_timer = NULL; zend_error_noreturn(E_ERROR, "Could not delete queued timer"); return; @@ -1249,12 +1294,10 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */ /* XXX passing NULL means the default timer queue provided by the system is used */ if (!CreateTimerQueueTimer(&tq_timer, NULL, (WAITORTIMERCALLBACK)tq_timer_cb, (VOID*)&EG(timed_out), seconds*1000, 0, WT_EXECUTEONLYONCE)) { - EG(timed_out) = 0; tq_timer = NULL; zend_error_noreturn(E_ERROR, "Could not queue new timer"); return; } - EG(timed_out) = 0; #else # ifdef HAVE_SETITIMER { @@ -1277,15 +1320,23 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */ if (reset_signals) { # ifdef ZEND_SIGNALS - zend_signal(signo, zend_timeout); + zend_signal(signo, zend_timeout_handler); # else sigset_t sigset; - - signal(signo, zend_timeout); +# ifdef HAVE_SIGACTION + struct sigaction act; + + act.sa_handler = zend_timeout_handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESETHAND | SA_NODEFER; + sigaction(signo, &act, NULL); +# else + signal(signo, zend_timeout_handler); +# endif /* HAVE_SIGACTION */ sigemptyset(&sigset); sigaddset(&sigset, signo); sigprocmask(SIG_UNBLOCK, &sigset, NULL); -# endif +# endif /* ZEND_SIGNALS */ } } # endif /* HAVE_SETITIMER */ @@ -1293,6 +1344,15 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */ } /* }}} */ +void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */ +{ + + EG(timeout_seconds) = seconds; + zend_set_timeout_ex(seconds, reset_signals); + EG(timed_out) = 0; +} +/* }}} */ + void zend_unset_timeout(void) /* {{{ */ { #ifdef ZEND_WIN32 @@ -1320,6 +1380,7 @@ void zend_unset_timeout(void) /* {{{ */ #endif } # endif + EG(timed_out) = 0; #endif } /* }}} */ diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index a6a89377f7..6c1280117a 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -174,8 +174,10 @@ struct _zend_executor_globals { /* for extended information support */ zend_bool no_extensions; -#ifdef ZEND_WIN32 zend_bool timed_out; + zend_long hard_timeout; + +#ifdef ZEND_WIN32 OSVERSIONINFOEX windows_version_info; #endif diff --git a/main/main.c b/main/main.c index fcb42b43b4..5762fce690 100644 --- a/main/main.c +++ b/main/main.c @@ -572,7 +572,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("user_ini.filename", ".user.ini", PHP_INI_SYSTEM, OnUpdateString, user_ini_filename, php_core_globals, core_globals) STD_PHP_INI_ENTRY("user_ini.cache_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, user_ini_cache_ttl, php_core_globals, core_globals) - STD_PHP_INI_BOOLEAN("exit_on_timeout", "0", PHP_INI_ALL, OnUpdateBool, exit_on_timeout, php_core_globals, core_globals) + STD_PHP_INI_ENTRY("hard_timeout", "2", PHP_INI_SYSTEM, OnUpdateLong, hard_timeout, zend_executor_globals, executor_globals) #ifdef PHP_WIN32 STD_PHP_INI_BOOLEAN("windows.show_crt_warning", "0", PHP_INI_ALL, OnUpdateBool, windows_show_crt_warning, php_core_globals, core_globals) #endif @@ -1505,8 +1505,6 @@ static ZEND_COLD void php_message_handler_for_zend(zend_long message, const void void php_on_timeout(int seconds) { PG(connection_status) |= PHP_CONNECTION_TIMEOUT; - zend_set_timeout(EG(timeout_seconds), 1); - if(PG(exit_on_timeout)) sapi_terminate_process(); } #if PHP_SIGCHILD diff --git a/main/php_globals.h b/main/php_globals.h index 057ec6679f..e50ea2ebfb 100644 --- a/main/php_globals.h +++ b/main/php_globals.h @@ -147,7 +147,6 @@ struct _php_core_globals { char *disable_functions; char *disable_classes; zend_bool allow_url_include; - zend_bool exit_on_timeout; #ifdef PHP_WIN32 zend_bool com_initialized; #endif