]> granicus.if.org Git - php/commitdiff
Safe execution timeout handling.
authorDmitry Stogov <dmitry@zend.com>
Wed, 20 Apr 2016 10:52:21 +0000 (13:52 +0300)
committerDmitry Stogov <dmitry@zend.com>
Wed, 20 Apr 2016 10:52:21 +0000 (13:52 +0300)
Zend/zend_execute.c
Zend/zend_execute_API.c
Zend/zend_globals.h
main/main.c
main/php_globals.h

index 6ba72b324e6ad3ccb228469c7d4abe65c38f9ea4..be4b039d892a51e8738da2bad755fb0eb3e0f9f6 100644 (file)
@@ -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)
index 55981894cc5d9c959b53c8eb6e6888d2b591a422..99cc601950115bfa68bb7c62b9a3717dc73e6f3b 100644 (file)
@@ -38,6 +38,9 @@
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#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
 }
 /* }}} */
index a6a89377f757b1d9863e454edc67ac0f450fa062..6c1280117a7f5f90c43612b0a107292a3acacd8c 100644 (file)
@@ -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
 
index fcb42b43b40c683533284b0d22c0ed81b11bd740..5762fce690070a32cde72fd6ad0275f8a5e5c660 100644 (file)
@@ -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
index 057ec6679fb930c3a2d8352b2cc9741aad85227b..e50ea2ebfb33ca8502cb3a2e56de953bdfd3d1b9 100644 (file)
@@ -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