Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Zend/zend_execute.h
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_cannot_pass_by_reference_ex(const zen

ZEND_API void zend_set_timeout(zend_long seconds, bool reset_signals);
ZEND_API void zend_unset_timeout(void);
ZEND_NORETURN ZEND_API void ZEND_FASTCALL zend_timeout(void);
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_timeout(void);
ZEND_API zend_class_entry *zend_fetch_class(zend_string *class_name, uint32_t fetch_type);
ZEND_API zend_class_entry *zend_fetch_class_with_scope(zend_string *class_name, uint32_t fetch_type, zend_class_entry *scope);
ZEND_API zend_class_entry *zend_fetch_class_by_name(zend_string *class_name, zend_string *lcname, uint32_t fetch_type);
Expand Down
21 changes: 19 additions & 2 deletions Zend/zend_execute_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -1494,7 +1494,8 @@ 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);

ZEND_NORETURN ZEND_API void ZEND_FASTCALL zend_timeout(void) /* {{{ */
/* zend_timeout() is called by the VM interrupt handler or by zend_call_function() when EG(timed_out) is set */
ZEND_API ZEND_COLD void ZEND_FASTCALL zend_timeout(void) /* {{{ */
{
#if defined(PHP_WIN32)
# ifndef ZTS
Expand All @@ -1514,7 +1515,23 @@ ZEND_NORETURN ZEND_API void ZEND_FASTCALL zend_timeout(void) /* {{{ */
zend_set_timeout_ex(0, true);
#endif

zend_error_noreturn(E_ERROR, "Maximum execution time of " ZEND_LONG_FMT " second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s");
/* Trigger error but do not bail */
zend_error(E_ERROR | E_DONT_BAIL, "Maximum execution time of " ZEND_LONG_FMT " second%s exceeded", EG(timeout_seconds), EG(timeout_seconds) == 1 ? "" : "s");

/* Do not call destructors */
zend_objects_store_mark_destructed(&EG(objects_store));

if (EG(exception)) {
OBJ_RELEASE(EG(exception));
}
EG(exception) = zend_create_unwind_exit();

if (EG(current_execute_data)) {
/* zend_call_function() may call zend_timeout() without EG(current_execute_data)
* when executing shutdown functions */
EG(opline_before_exception) = EG(current_execute_data)->opline;
EG(current_execute_data)->opline = EG(exception_op);
}
}
/* }}} */

Expand Down
1 change: 1 addition & 0 deletions Zend/zend_vm_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -10743,6 +10743,7 @@ ZEND_VM_HELPER(zend_interrupt_helper, ANY, ANY)
#endif
if (atomic_load(&EG(timed_out))) {
zend_timeout();
HANDLE_EXCEPTION();
} else if (ZEND_HAS_OOM()) {
zend_handle_delayed_oom();
HANDLE_EXCEPTION();
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_vm_execute.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 14 additions & 6 deletions ext/opcache/jit/zend_jit_ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -2052,23 +2052,30 @@ static int zend_jit_exception_handler_free_op2_stub(zend_jit_ctx *jit)
static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit)
{
ir_ref if_timeout, if_exception;
ir_ref end_inputs = IR_UNUSED;

// EX(opline) = opline
ir_STORE(jit_EX(opline), jit_IP(jit));

ir_STORE(jit_EG(vm_interrupt), ir_CONST_U8(0));
if_timeout = ir_IF(ir_EQ(ir_LOAD_U8(jit_EG(timed_out)), ir_CONST_U8(0)));

// if (EG(timed_out)) {
if_timeout = ir_IF(ir_LOAD_U8(jit_EG(timed_out)));
ir_IF_TRUE(if_timeout);
ir_CALL(IR_VOID, ir_CONST_FUNC(zend_timeout));
ir_CALL(IR_VOID, ir_CONST_FUNC(zend_jit_exception_in_interrupt_handler_helper));
// LOAD_OPLINE()
jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline)));
ir_END_list(end_inputs);
ir_IF_FALSE(if_timeout);
ir_CALL(IR_VOID, ir_CONST_FUNC(zend_timeout)); // noreturn
ir_MERGE_WITH_EMPTY_TRUE(if_timeout);

// if (ZEND_HAS_OOM()) {
// } else if (ZEND_HAS_OOM()) {
ir_ref if_delayed_oom = ir_IF(ir_LOAD_U8(ir_ADD_OFFSET(jit_EG(delayed_oom), offsetof(zend_oom_info, state))));
ir_IF_TRUE(if_delayed_oom);
ir_CALL(IR_VOID, ir_CONST_FC_FUNC(zend_handle_delayed_oom));
// LOAD_OPLINE()
jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline)));
ir_ref if_delayed_oom_end = ir_END();
ir_END_list(end_inputs);
ir_IF_FALSE(if_delayed_oom);

if (zend_interrupt_function) {
Expand All @@ -2082,7 +2089,8 @@ static int zend_jit_interrupt_handler_stub(zend_jit_ctx *jit)
jit_STORE_IP(jit, ir_LOAD_A(jit_EX(opline)));
}

ir_MERGE_WITH(if_delayed_oom_end);
ir_END_list(end_inputs);
ir_MERGE_list(end_inputs);

if (GCC_GLOBAL_REGS || ZEND_VM_KIND == ZEND_VM_KIND_TAILCALL) {
zend_jit_tailcall_handler(jit, ir_LOAD_A(jit_IP(jit)));
Expand Down
22 changes: 22 additions & 0 deletions tests/basic/timeout_variation_11.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
--TEST--
Timeout within shutdown function, user function variation
--SKIPIF--
<?php
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
?>
--INI--
max_execution_time=1
--FILE--
<?php

register_shutdown_function(function () {
while (true) {}
});
register_shutdown_function(function () {
echo "Not executed\n";
while (true) {}
});

?>
--EXPECTF--
Fatal error: Maximum execution time of 1 second exceeded %s
27 changes: 27 additions & 0 deletions tests/basic/timeout_variation_12.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
Timeout does not call destructors
--SKIPIF--
<?php
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
?>
--INI--
max_execution_time=1
--FILE--
<?php

$obj = new class {
function __destruct() {
echo "destructor called\n";
}
};

register_shutdown_function(function() {
echo "shutdown function called\n";
});

while (true) {}

?>
--EXPECTF--
Fatal error: Maximum execution time of 1 second exceeded %s
shutdown function called
38 changes: 38 additions & 0 deletions tests/basic/timeout_variation_13.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
--TEST--
Timeout does not execute finally blocks
--SKIPIF--
<?php
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
?>
--INI--
max_execution_time=1
--FILE--
<?php

$gen1 = (function () {
try {
yield;
} finally {
echo "finally executed (1)\n";
}
})();
$gen1->current();

$gen2 = (function () {
try {
while (true) {}
} finally {
echo "finally executed (2)\n";
}
yield;
})();

try {
foreach ($gen2 as $value) {}
} finally {
echo "finally executed (3)\n";
}

?>
--EXPECTF--
Fatal error: Maximum execution time of 1 second exceeded %s
Loading