Skip to content

Commit 650c1c0

Browse files
committed
Safe execution timeout handling.
1 parent f484801 commit 650c1c0

File tree

5 files changed

+79
-24
lines changed

5 files changed

+79
-24
lines changed

Zend/zend_execute.c

+1-6
Original file line numberDiff line numberDiff line change
@@ -2100,22 +2100,17 @@ void zend_free_compiled_variables(zend_execute_data *execute_data) /* {{{ */
21002100
}
21012101
/* }}} */
21022102

2103-
#ifdef ZEND_WIN32
21042103
static zend_never_inline ZEND_COLD ZEND_NORETURN void ZEND_FASTCALL zend_interrupt(void) /* {{{ */
21052104
{
21062105
zend_timeout(0);
21072106
}
21082107
/* }}} */
21092108

2110-
# define ZEND_VM_INTERRUPT_CHECK() do { \
2109+
#define ZEND_VM_INTERRUPT_CHECK() do { \
21112110
if (UNEXPECTED(EG(timed_out))) { \
21122111
zend_interrupt(); \
21132112
} \
21142113
} while (0)
2115-
#else
2116-
# define ZEND_VM_INTERRUPT_CHECK() do { \
2117-
} while (0)
2118-
#endif
21192114

21202115
/*
21212116
* Stack Frame Layout (the whole stack frame is allocated at once)

Zend/zend_execute_API.c

+74-13
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
#ifdef HAVE_SYS_TIME_H
3939
#include <sys/time.h>
4040
#endif
41+
#ifdef HAVE_UNISTD_H
42+
#include <unistd.h>
43+
#endif
4144

4245
ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data);
4346
ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value);
@@ -169,9 +172,7 @@ void init_executor(void) /* {{{ */
169172
zend_objects_store_init(&EG(objects_store), 1024);
170173

171174
EG(full_tables_cleanup) = 0;
172-
#ifdef ZEND_WIN32
173175
EG(timed_out) = 0;
174-
#endif
175176

176177
EG(exception) = NULL;
177178
EG(prev_exception) = NULL;
@@ -1183,8 +1184,47 @@ ZEND_API int zend_eval_string_ex(char *str, zval *retval_ptr, char *string_name,
11831184
}
11841185
/* }}} */
11851186

1187+
static void zend_set_timeout_ex(zend_long seconds, int reset_signals);
1188+
11861189
ZEND_API void zend_timeout(int dummy) /* {{{ */
11871190
{
1191+
EG(timed_out) = 0;
1192+
zend_set_timeout_ex(0, 1);
1193+
zend_error_noreturn(E_ERROR, "Maximum execution time of %pd second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s");
1194+
}
1195+
/* }}} */
1196+
1197+
#ifndef ZEND_WIN32
1198+
static void zend_timeout_handler(int dummy) /* {{{ */
1199+
{
1200+
#ifndef ZTS
1201+
if (EG(timed_out)) {
1202+
/* Die on hard timeout */
1203+
const char *error_filename = NULL;
1204+
uint error_lineno = 0;
1205+
char *log_buffer = NULL;
1206+
1207+
if (zend_is_compiling()) {
1208+
error_filename = ZSTR_VAL(zend_get_compiled_filename());
1209+
error_lineno = zend_get_compiled_lineno();
1210+
} else if (zend_is_executing()) {
1211+
error_filename = zend_get_executed_filename();
1212+
if (error_filename[0] == '[') { /* [no active file] */
1213+
error_filename = NULL;
1214+
error_lineno = 0;
1215+
} else {
1216+
error_lineno = zend_get_executed_lineno();
1217+
}
1218+
}
1219+
if (!error_filename) {
1220+
error_filename = "Unknown";
1221+
}
1222+
1223+
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);
1224+
write(2, log_buffer, strlen(log_buffer));
1225+
_exit(1);
1226+
}
1227+
#endif
11881228

11891229
if (zend_on_timeout) {
11901230
#ifdef ZEND_SIGNALS
@@ -1199,9 +1239,17 @@ ZEND_API void zend_timeout(int dummy) /* {{{ */
11991239
zend_on_timeout(EG(timeout_seconds));
12001240
}
12011241

1202-
zend_error_noreturn(E_ERROR, "Maximum execution time of %pd second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s");
1242+
EG(timed_out) = 1;
1243+
1244+
#ifndef ZTS
1245+
if (EG(hard_timeout) > 0) {
1246+
/* Set hard timeout */
1247+
zend_set_timeout_ex(EG(hard_timeout), 1);
1248+
}
1249+
#endif
12031250
}
12041251
/* }}} */
1252+
#endif
12051253

12061254
#ifdef ZEND_WIN32
12071255
VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out)
@@ -1224,11 +1272,9 @@ VOID CALLBACK tq_timer_cb(PVOID arg, BOOLEAN timed_out)
12241272
#define SIGPROF 27
12251273
#endif
12261274

1227-
void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
1275+
static void zend_set_timeout_ex(zend_long seconds, int reset_signals) /* {{{ */
12281276
{
12291277

1230-
EG(timeout_seconds) = seconds;
1231-
12321278
#ifdef ZEND_WIN32
12331279
if(!seconds) {
12341280
return;
@@ -1239,7 +1285,6 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
12391285
delete and recreate. */
12401286
if (NULL != tq_timer) {
12411287
if (!DeleteTimerQueueTimer(NULL, tq_timer, NULL)) {
1242-
EG(timed_out) = 0;
12431288
tq_timer = NULL;
12441289
zend_error_noreturn(E_ERROR, "Could not delete queued timer");
12451290
return;
@@ -1249,12 +1294,10 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
12491294

12501295
/* XXX passing NULL means the default timer queue provided by the system is used */
12511296
if (!CreateTimerQueueTimer(&tq_timer, NULL, (WAITORTIMERCALLBACK)tq_timer_cb, (VOID*)&EG(timed_out), seconds*1000, 0, WT_EXECUTEONLYONCE)) {
1252-
EG(timed_out) = 0;
12531297
tq_timer = NULL;
12541298
zend_error_noreturn(E_ERROR, "Could not queue new timer");
12551299
return;
12561300
}
1257-
EG(timed_out) = 0;
12581301
#else
12591302
# ifdef HAVE_SETITIMER
12601303
{
@@ -1277,22 +1320,39 @@ void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
12771320

12781321
if (reset_signals) {
12791322
# ifdef ZEND_SIGNALS
1280-
zend_signal(signo, zend_timeout);
1323+
zend_signal(signo, zend_timeout_handler);
12811324
# else
12821325
sigset_t sigset;
1283-
1284-
signal(signo, zend_timeout);
1326+
# ifdef HAVE_SIGACTION
1327+
struct sigaction act;
1328+
1329+
act.sa_handler = zend_timeout_handler;
1330+
sigemptyset(&act.sa_mask);
1331+
act.sa_flags = SA_RESETHAND | SA_NODEFER;
1332+
sigaction(signo, &act, NULL);
1333+
# else
1334+
signal(signo, zend_timeout_handler);
1335+
# endif /* HAVE_SIGACTION */
12851336
sigemptyset(&sigset);
12861337
sigaddset(&sigset, signo);
12871338
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
1288-
# endif
1339+
# endif /* ZEND_SIGNALS */
12891340
}
12901341
}
12911342
# endif /* HAVE_SETITIMER */
12921343
#endif
12931344
}
12941345
/* }}} */
12951346

1347+
void zend_set_timeout(zend_long seconds, int reset_signals) /* {{{ */
1348+
{
1349+
1350+
EG(timeout_seconds) = seconds;
1351+
zend_set_timeout_ex(seconds, reset_signals);
1352+
EG(timed_out) = 0;
1353+
}
1354+
/* }}} */
1355+
12961356
void zend_unset_timeout(void) /* {{{ */
12971357
{
12981358
#ifdef ZEND_WIN32
@@ -1320,6 +1380,7 @@ void zend_unset_timeout(void) /* {{{ */
13201380
#endif
13211381
}
13221382
# endif
1383+
EG(timed_out) = 0;
13231384
#endif
13241385
}
13251386
/* }}} */

Zend/zend_globals.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,10 @@ struct _zend_executor_globals {
174174
/* for extended information support */
175175
zend_bool no_extensions;
176176

177-
#ifdef ZEND_WIN32
178177
zend_bool timed_out;
178+
zend_long hard_timeout;
179+
180+
#ifdef ZEND_WIN32
179181
OSVERSIONINFOEX windows_version_info;
180182
#endif
181183

main/main.c

+1-3
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ PHP_INI_BEGIN()
572572

573573
STD_PHP_INI_ENTRY("user_ini.filename", ".user.ini", PHP_INI_SYSTEM, OnUpdateString, user_ini_filename, php_core_globals, core_globals)
574574
STD_PHP_INI_ENTRY("user_ini.cache_ttl", "300", PHP_INI_SYSTEM, OnUpdateLong, user_ini_cache_ttl, php_core_globals, core_globals)
575-
STD_PHP_INI_BOOLEAN("exit_on_timeout", "0", PHP_INI_ALL, OnUpdateBool, exit_on_timeout, php_core_globals, core_globals)
575+
STD_PHP_INI_ENTRY("hard_timeout", "2", PHP_INI_SYSTEM, OnUpdateLong, hard_timeout, zend_executor_globals, executor_globals)
576576
#ifdef PHP_WIN32
577577
STD_PHP_INI_BOOLEAN("windows.show_crt_warning", "0", PHP_INI_ALL, OnUpdateBool, windows_show_crt_warning, php_core_globals, core_globals)
578578
#endif
@@ -1505,8 +1505,6 @@ static ZEND_COLD void php_message_handler_for_zend(zend_long message, const void
15051505
void php_on_timeout(int seconds)
15061506
{
15071507
PG(connection_status) |= PHP_CONNECTION_TIMEOUT;
1508-
zend_set_timeout(EG(timeout_seconds), 1);
1509-
if(PG(exit_on_timeout)) sapi_terminate_process();
15101508
}
15111509

15121510
#if PHP_SIGCHILD

main/php_globals.h

-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ struct _php_core_globals {
147147
char *disable_functions;
148148
char *disable_classes;
149149
zend_bool allow_url_include;
150-
zend_bool exit_on_timeout;
151150
#ifdef PHP_WIN32
152151
zend_bool com_initialized;
153152
#endif

0 commit comments

Comments
 (0)