From de519e5f88661926e313836e29bdbb1194355e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 8 Dec 2020 16:50:58 +0100 Subject: [PATCH 1/2] Add support for limiting maximum execution time based on wall-time --- Zend/zend_execute.h | 2 + Zend/zend_execute_API.c | 169 +++++++++++++++++++++- Zend/zend_globals.h | 3 +- Zend/zend_signal.c | 9 +- main/main.c | 20 ++- tests/basic/wall_timeout_variation_0.phpt | 15 ++ tests/basic/wall_timeout_variation_1.phpt | 21 +++ tests/basic/wall_timeout_variation_2.phpt | 31 ++++ tests/basic/wall_timeout_variation_3.phpt | 30 ++++ 9 files changed, 287 insertions(+), 13 deletions(-) create mode 100644 tests/basic/wall_timeout_variation_0.phpt create mode 100644 tests/basic/wall_timeout_variation_1.phpt create mode 100644 tests/basic/wall_timeout_variation_2.phpt create mode 100644 tests/basic/wall_timeout_variation_3.phpt diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 3339f3992da35..b2340228af8bb 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -321,7 +321,9 @@ ZEND_API bool zend_is_executing(void); ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_pass_by_reference(uint32_t arg_num); ZEND_API void zend_set_timeout(zend_long seconds, bool reset_signals); +ZEND_API void zend_set_wall_timeout(zend_long seconds, bool reset_signals); ZEND_API void zend_unset_timeout(void); +ZEND_API void zend_unset_wall_timeout(void); ZEND_API ZEND_NORETURN void ZEND_FASTCALL zend_timeout(void); ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, int fetch_type); ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, int fetch_type); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 7c32f3a7fbf55..b669fd15764d1 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -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); + } +#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 + * 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; diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h index 825fad833c635..3b54ec827c34e 100644 --- a/Zend/zend_globals.h +++ b/Zend/zend_globals.h @@ -184,7 +184,7 @@ struct _zend_executor_globals { bool no_extensions; bool vm_interrupt; - bool timed_out; + zend_long timed_out; zend_long hard_timeout; #ifdef ZEND_WIN32 @@ -206,6 +206,7 @@ struct _zend_executor_globals { /* timeout support */ zend_long timeout_seconds; + zend_long wall_timeout_seconds; int capture_warnings_during_sccp; diff --git a/Zend/zend_signal.c b/Zend/zend_signal.c index af3ef8cbc6d7c..a6da0ebe4ab7b 100644 --- a/Zend/zend_signal.c +++ b/Zend/zend_signal.c @@ -62,14 +62,7 @@ ZEND_API zend_signal_globals_t zend_signal_globals; static void zend_signal_handler(int signo, siginfo_t *siginfo, void *context); static int zend_signal_register(int signo, void (*handler)(int, siginfo_t*, void*)); -#if defined(__CYGWIN__) || defined(__PASE__) -/* Matches zend_excute_API.c; these platforms don't support ITIMER_PROF. */ -#define TIMEOUT_SIG SIGALRM -#else -#define TIMEOUT_SIG SIGPROF -#endif - -static int zend_sigs[] = { TIMEOUT_SIG, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2 }; +static int zend_sigs[] = { SIGPROF, SIGALRM, SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGUSR1, SIGUSR2 }; #define SA_FLAGS_MASK ~(SA_NODEFER | SA_RESETHAND) diff --git a/main/main.c b/main/main.c index ac7170c88aacd..97c2bed302b4e 100644 --- a/main/main.c +++ b/main/main.c @@ -400,6 +400,21 @@ static PHP_INI_MH(OnUpdateTimeout) } /* }}} */ +/* {{{ PHP_INI_MH */ +static PHP_INI_MH(OnUpdateWallTimeout) +{ + if (stage==PHP_INI_STAGE_STARTUP) { + /* Don't set a timeout on startup, only per-request */ + ZEND_ATOL(EG(wall_timeout_seconds), ZSTR_VAL(new_value)); + return SUCCESS; + } + zend_unset_wall_timeout(); + ZEND_ATOL(EG(wall_timeout_seconds), ZSTR_VAL(new_value)); + zend_set_wall_timeout(EG(wall_timeout_seconds), 0); + return SUCCESS; +} +/* }}} */ + /* {{{ php_get_display_errors_mode() helper function */ static zend_uchar php_get_display_errors_mode(char *value, size_t value_length) { @@ -688,6 +703,7 @@ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("sys_temp_dir", NULL, PHP_INI_SYSTEM, OnUpdateStringUnempty, sys_temp_dir, php_core_globals, core_globals) STD_PHP_INI_ENTRY("include_path", PHP_INCLUDE_PATH, PHP_INI_ALL, OnUpdateStringUnempty, include_path, php_core_globals, core_globals) PHP_INI_ENTRY("max_execution_time", "30", PHP_INI_ALL, OnUpdateTimeout) + PHP_INI_ENTRY("max_execution_wall_time", "0", PHP_INI_ALL, OnUpdateWallTimeout) STD_PHP_INI_ENTRY("open_basedir", NULL, PHP_INI_ALL, OnUpdateBaseDir, open_basedir, php_core_globals, core_globals) STD_PHP_INI_BOOLEAN("file_uploads", "1", PHP_INI_SYSTEM, OnUpdateBool, file_uploads, php_core_globals, core_globals) @@ -1686,6 +1702,7 @@ int php_request_startup(void) } else { zend_set_timeout(PG(max_input_time), 1); } + zend_set_wall_timeout(EG(wall_timeout_seconds), 1); /* Disable realpath cache if an open_basedir is set */ if (PG(open_basedir) && *PG(open_basedir)) { @@ -1772,9 +1789,10 @@ void php_request_shutdown(void *dummy) } } zend_end_try(); - /* 4. Reset max_execution_time (no longer executing php code after response sent) */ + /* 4. Reset max_execution_time and max_execution_wall_time (no longer executing php code after response sent) */ zend_try { zend_unset_timeout(); + zend_unset_wall_timeout(); } zend_end_try(); /* 5. Call all extensions RSHUTDOWN functions */ diff --git a/tests/basic/wall_timeout_variation_0.phpt b/tests/basic/wall_timeout_variation_0.phpt new file mode 100644 index 0000000000000..6be8cb14e9fb9 --- /dev/null +++ b/tests/basic/wall_timeout_variation_0.phpt @@ -0,0 +1,15 @@ +--TEST-- +Retrieve max_execution_wall_time ini value +--FILE-- + +--EXPECTF-- +string(1) "0" +string(1) "1" diff --git a/tests/basic/wall_timeout_variation_1.phpt b/tests/basic/wall_timeout_variation_1.phpt new file mode 100644 index 0000000000000..2cc9192480cfd --- /dev/null +++ b/tests/basic/wall_timeout_variation_1.phpt @@ -0,0 +1,21 @@ +--TEST-- +Timeout in loop +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Maximum execution wall-time of 1 second exceeded in %s on line %d diff --git a/tests/basic/wall_timeout_variation_2.phpt b/tests/basic/wall_timeout_variation_2.phpt new file mode 100644 index 0000000000000..f1e97c60d889a --- /dev/null +++ b/tests/basic/wall_timeout_variation_2.phpt @@ -0,0 +1,31 @@ +--TEST-- +Safe shutdown when hard timeout is not reached +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +hard_timeout=5 +--FILE-- + +--EXPECTF-- +Fatal error: Maximum execution wall-time of 1 second exceeded in %s on line %d +OK diff --git a/tests/basic/wall_timeout_variation_3.phpt b/tests/basic/wall_timeout_variation_3.phpt new file mode 100644 index 0000000000000..9a5fdce7e5f0f --- /dev/null +++ b/tests/basic/wall_timeout_variation_3.phpt @@ -0,0 +1,30 @@ +--TEST-- +Unsafe shutdown when hard timeout is reached +--SKIPIF-- + +--INI-- +allow_url_fopen=1 +hard_timeout=1 +--FILE-- + +--EXPECTF-- +Fatal error: Maximum execution wall-time of 1+1 seconds exceeded (terminated) in %s on line %d From 2f948fac649a9388a19f4a109423b401ddf904c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 27 Dec 2020 00:21:25 +0100 Subject: [PATCH 2/2] Fix behavior on platforms which already measure wall-clock time --- Zend/zend_ini.c | 11 +++--- Zend/zend_ini.h | 2 +- ext/session/session.c | 2 +- ext/standard/assert.c | 8 ++-- ext/standard/basic_functions.c | 4 +- main/main.c | 37 ++++++++++++++++--- main/php_ini.c | 2 +- sapi/phpdbg/phpdbg_wait.c | 2 +- tests/basic/wall_timeout_variation_4.phpt | 30 +++++++++++++++ tests/basic/wall_timeout_variation_4_win.phpt | 30 +++++++++++++++ 10 files changed, 107 insertions(+), 21 deletions(-) create mode 100644 tests/basic/wall_timeout_variation_4.phpt create mode 100644 tests/basic/wall_timeout_variation_4_win.phpt diff --git a/Zend/zend_ini.c b/Zend/zend_ini.c index b3418b1152933..0c5b235b4af18 100644 --- a/Zend/zend_ini.c +++ b/Zend/zend_ini.c @@ -277,8 +277,7 @@ ZEND_API void zend_ini_refresh_caches(int stage) /* {{{ */ ZEND_API zend_result zend_alter_ini_entry(zend_string *name, zend_string *new_value, int modify_type, int stage) /* {{{ */ { - - return zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0); + return zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0, 0); } /* }}} */ @@ -288,7 +287,7 @@ ZEND_API zend_result zend_alter_ini_entry_chars(zend_string *name, const char *v zend_string *new_value; new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST)); - ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0); + ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, 0, 0); zend_string_release(new_value); return ret; } @@ -300,13 +299,13 @@ ZEND_API zend_result zend_alter_ini_entry_chars_ex(zend_string *name, const char zend_string *new_value; new_value = zend_string_init(value, value_length, !(stage & ZEND_INI_STAGE_IN_REQUEST)); - ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change); + ret = zend_alter_ini_entry_ex(name, new_value, modify_type, stage, force_change, 0); zend_string_release(new_value); return ret; } /* }}} */ -ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change) /* {{{ */ +ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change, bool skip_on_update) /* {{{ */ { zend_ini_entry *ini_entry; zend_string *duplicate; @@ -344,7 +343,7 @@ ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new duplicate = zend_string_copy(new_value); if (!ini_entry->on_modify - || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) { + || skip_on_update || ini_entry->on_modify(ini_entry, duplicate, ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) { if (modified && ini_entry->orig_value != ini_entry->value) { /* we already changed the value, free the changed value */ zend_string_release(ini_entry->value); } diff --git a/Zend/zend_ini.h b/Zend/zend_ini.h index 590ff09cef0c5..237fdb12f7d78 100644 --- a/Zend/zend_ini.h +++ b/Zend/zend_ini.h @@ -75,7 +75,7 @@ ZEND_API zend_result zend_register_ini_entries(const zend_ini_entry_def *ini_ent ZEND_API void zend_unregister_ini_entries(int module_number); ZEND_API void zend_ini_refresh_caches(int stage); ZEND_API zend_result zend_alter_ini_entry(zend_string *name, zend_string *new_value, int modify_type, int stage); -ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change); +ZEND_API zend_result zend_alter_ini_entry_ex(zend_string *name, zend_string *new_value, int modify_type, int stage, bool force_change, bool skip_on_update); ZEND_API zend_result zend_alter_ini_entry_chars(zend_string *name, const char *value, size_t value_length, int modify_type, int stage); ZEND_API zend_result zend_alter_ini_entry_chars_ex(zend_string *name, const char *value, size_t value_length, int modify_type, int stage, int force_change); ZEND_API zend_result zend_restore_ini_entry(zend_string *name, int stage); diff --git a/ext/session/session.c b/ext/session/session.c index 5eda7eae8e2f0..0fab2206179ed 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -2452,7 +2452,7 @@ static int php_session_start_set_ini(zend_string *varname, zend_string *new_valu smart_str_appendc(&buf, '.'); smart_str_append(&buf, varname); smart_str_0(&buf); - ret = zend_alter_ini_entry_ex(buf.s, new_value, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); + ret = zend_alter_ini_entry_ex(buf.s, new_value, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0, 0); smart_str_free(&buf); return ret; } diff --git a/ext/standard/assert.c b/ext/standard/assert.c index fae6d940ba205..4d852044174e5 100644 --- a/ext/standard/assert.c +++ b/ext/standard/assert.c @@ -228,7 +228,7 @@ PHP_FUNCTION(assert_options) } key = zend_string_init("assert.active", sizeof("assert.active")-1, 0); - zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); + zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0, 0); zend_string_release_ex(key, 0); zend_string_release_ex(value_str, 0); } @@ -244,7 +244,7 @@ PHP_FUNCTION(assert_options) } key = zend_string_init("assert.bail", sizeof("assert.bail")-1, 0); - zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); + zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0, 0); zend_string_release_ex(key, 0); zend_string_release_ex(value_str, 0); } @@ -260,7 +260,7 @@ PHP_FUNCTION(assert_options) } key = zend_string_init("assert.warning", sizeof("assert.warning")-1, 0); - zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); + zend_alter_ini_entry_ex(key, value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0, 0); zend_string_release_ex(key, 0); zend_string_release_ex(value_str, 0); } @@ -295,7 +295,7 @@ PHP_FUNCTION(assert_options) } key = zend_string_init("assert.exception", sizeof("assert.exception")-1, 0); - zend_alter_ini_entry_ex(key, val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0); + zend_alter_ini_entry_ex(key, val, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0, 0); zend_string_release_ex(val, 0); zend_string_release_ex(key, 0); } diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 4084f5d853110..18d0aab756d7c 100755 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -2128,7 +2128,7 @@ PHP_FUNCTION(ini_set) } #undef _CHECK_PATH - if (zend_alter_ini_entry_ex(varname, new_value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0) == FAILURE) { + if (zend_alter_ini_entry_ex(varname, new_value_str, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0, 0) == FAILURE) { zval_ptr_dtor_str(return_value); RETVAL_FALSE; } @@ -2171,7 +2171,7 @@ PHP_FUNCTION(set_include_path) } key = zend_string_init("include_path", sizeof("include_path") - 1, 0); - if (zend_alter_ini_entry_ex(key, new_value, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0) == FAILURE) { + if (zend_alter_ini_entry_ex(key, new_value, PHP_INI_USER, PHP_INI_STAGE_RUNTIME, 0, 0) == FAILURE) { zend_string_release_ex(key, 0); zval_ptr_dtor_str(return_value); RETURN_FALSE; diff --git a/main/main.c b/main/main.c index 97c2bed302b4e..21a66ab47b80b 100644 --- a/main/main.c +++ b/main/main.c @@ -385,17 +385,31 @@ static void php_binary_init(void) } /* }}} */ -/* {{{ PHP_INI_MH */ -static PHP_INI_MH(OnUpdateTimeout) +static void updateTimeout(zend_string *new_value, int stage) { - if (stage==PHP_INI_STAGE_STARTUP) { + if (stage == PHP_INI_STAGE_STARTUP) { /* Don't set a timeout on startup, only per-request */ ZEND_ATOL(EG(timeout_seconds), ZSTR_VAL(new_value)); - return SUCCESS; + return; } zend_unset_timeout(); ZEND_ATOL(EG(timeout_seconds), ZSTR_VAL(new_value)); zend_set_timeout(EG(timeout_seconds), 0); +} + +/* {{{ PHP_INI_MH */ +static PHP_INI_MH(OnUpdateTimeout) +{ + updateTimeout(new_value, stage); + +#if defined(WIN32) || defined(__CYGWIN__) || defined(__PASE__) + if (stage != PHP_INI_STAGE_STARTUP) { + zend_string *alias_name = zend_string_init("max_execution_wall_time", sizeof("max_execution_wall_time") - 1, !(stage & ZEND_INI_STAGE_IN_REQUEST)); + zend_alter_ini_entry_ex(alias_name, new_value, PHP_INI_ALL, stage, 0, 1); + zend_string_release(alias_name); + } +#endif + return SUCCESS; } /* }}} */ @@ -403,7 +417,15 @@ static PHP_INI_MH(OnUpdateTimeout) /* {{{ PHP_INI_MH */ static PHP_INI_MH(OnUpdateWallTimeout) { - if (stage==PHP_INI_STAGE_STARTUP) { +#if defined(WIN32) || defined(__CYGWIN__) || defined(__PASE__) + updateTimeout(new_value, stage); + if (stage != PHP_INI_STAGE_STARTUP) { + zend_string *alias_name = zend_string_init("max_execution_time", sizeof("max_execution_time") - 1, !(stage & ZEND_INI_STAGE_IN_REQUEST)); + zend_alter_ini_entry_ex(alias_name, new_value, PHP_INI_ALL, stage, 0, 1); + zend_string_release(alias_name); + } +#else + if (stage == PHP_INI_STAGE_STARTUP) { /* Don't set a timeout on startup, only per-request */ ZEND_ATOL(EG(wall_timeout_seconds), ZSTR_VAL(new_value)); return SUCCESS; @@ -411,6 +433,7 @@ static PHP_INI_MH(OnUpdateWallTimeout) zend_unset_wall_timeout(); ZEND_ATOL(EG(wall_timeout_seconds), ZSTR_VAL(new_value)); zend_set_wall_timeout(EG(wall_timeout_seconds), 0); +#endif return SUCCESS; } /* }}} */ @@ -1702,7 +1725,9 @@ int php_request_startup(void) } else { zend_set_timeout(PG(max_input_time), 1); } +#if !defined(WIN32) && !defined(__CYGWIN__) && !defined(__PASE__) zend_set_wall_timeout(EG(wall_timeout_seconds), 1); +#endif /* Disable realpath cache if an open_basedir is set */ if (PG(open_basedir) && *PG(open_basedir)) { @@ -1792,7 +1817,9 @@ void php_request_shutdown(void *dummy) /* 4. Reset max_execution_time and max_execution_wall_time (no longer executing php code after response sent) */ zend_try { zend_unset_timeout(); +#if !defined(WIN32) && !defined(__CYGWIN__) && !defined(__PASE__) zend_unset_wall_timeout(); +#endif } zend_end_try(); /* 5. Call all extensions RSHUTDOWN functions */ diff --git a/main/php_ini.c b/main/php_ini.c index 0fa3a3336534e..1a42e1371f64e 100644 --- a/main/php_ini.c +++ b/main/php_ini.c @@ -796,7 +796,7 @@ PHPAPI void php_ini_activate_config(HashTable *source_hash, int modify_type, int /* Walk through config hash and alter matching ini entries using the values found in the hash */ ZEND_HASH_FOREACH_STR_KEY_VAL(source_hash, str, data) { - zend_alter_ini_entry_ex(str, Z_STR_P(data), modify_type, stage, 0); + zend_alter_ini_entry_ex(str, Z_STR_P(data), modify_type, stage, 0, 0); } ZEND_HASH_FOREACH_END(); } /* }}} */ diff --git a/sapi/phpdbg/phpdbg_wait.c b/sapi/phpdbg/phpdbg_wait.c index ec78f18db726f..1f5f28cdbd097 100644 --- a/sapi/phpdbg/phpdbg_wait.c +++ b/sapi/phpdbg/phpdbg_wait.c @@ -318,7 +318,7 @@ void phpdbg_webdata_decompress(char *msg, int len) { ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(zvp), key, ini_entry) { if (key && Z_TYPE_P(ini_entry) == IS_STRING) { - zend_alter_ini_entry_ex(key, Z_STR_P(ini_entry), ZEND_INI_PERDIR, ZEND_INI_STAGE_HTACCESS, 1); + zend_alter_ini_entry_ex(key, Z_STR_P(ini_entry), ZEND_INI_PERDIR, ZEND_INI_STAGE_HTACCESS, 1, 0); } } ZEND_HASH_FOREACH_END(); } diff --git a/tests/basic/wall_timeout_variation_4.phpt b/tests/basic/wall_timeout_variation_4.phpt new file mode 100644 index 0000000000000..23b440b3b69ae --- /dev/null +++ b/tests/basic/wall_timeout_variation_4.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test that max_execution_wall_time is an alias of max_execution_time on Unix-based platforms +--SKIPIF-- + +--INI-- +max_execution_time=0 +max_execution_wall_time=0 +--FILE-- + +--EXPECTF-- +string(1) "1" +string(1) "0" +string(1) "1" +string(1) "2" diff --git a/tests/basic/wall_timeout_variation_4_win.phpt b/tests/basic/wall_timeout_variation_4_win.phpt new file mode 100644 index 0000000000000..1d2b19dd44dc5 --- /dev/null +++ b/tests/basic/wall_timeout_variation_4_win.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test that max_execution_wall_time is an alias of max_execution_time on Windows +--SKIPIF-- + +--INI-- +max_execution_time=0 +max_execution_wall_time=0 +--FILE-- + +--EXPECTF-- +string(1) "1" +string(1) "1" +string(1) "2" +string(1) "2"