diff --git a/Zend/tests/gh10072-2.phpt b/Zend/tests/gh10072-2.phpt new file mode 100644 index 00000000000..3de75a2b6c0 --- /dev/null +++ b/Zend/tests/gh10072-2.phpt @@ -0,0 +1,20 @@ +--TEST-- +GH-10072 (PHP crashes when execute_ex is overridden and a trampoline is used from internal code during shutdown) +--EXTENSIONS-- +zend_test +--INI-- +zend_test.replace_zend_execute_ex=1 +opcache.jit=disable +--FILE-- + +--EXPECT-- +Trampoline for shutdown diff --git a/Zend/tests/gh10072.phpt b/Zend/tests/gh10072.phpt new file mode 100644 index 00000000000..95a0d434505 --- /dev/null +++ b/Zend/tests/gh10072.phpt @@ -0,0 +1,106 @@ +--TEST-- +GH-10072 (PHP crashes when execute_ex is overridden and a trampoline is used from internal code) +--EXTENSIONS-- +zend_test +--INI-- +zend_test.replace_zend_execute_ex=1 +opcache.jit=disable +--FILE-- +handle; + } + + + public function stream_close(): void + { + } + + public function stream_open(string $path, string $mode, int $options = 0, ?string &$openedPath = null): bool + { + return true; + } + + + public function stream_read(int $count) + { + return 0; + } + + + public function stream_seek(int $offset, int $whence = SEEK_SET): bool + { + return true; + } + + + public function stream_set_option(int $option, int $arg1, ?int $arg2): bool + { + return false; + } + + + public function stream_stat() + { + return []; + } + + + public function stream_tell() + { + return []; + } + + + public function stream_truncate(int $newSize): bool + { + return true; + } + + + public function stream_write(string $data) + { + } + + + public function unlink(string $path): bool + { + return false; + } +} + +class TrampolineTest { + /** @var resource|null */ + public $context; + + /** @var object|null */ + private $wrapper; + + public function __call(string $name, array $arguments) { + if (!$this->wrapper) { + $this->wrapper = new DummyStreamWrapper(); + } + echo 'Trampoline for ', $name, PHP_EOL; + return $this->wrapper->$name(...$arguments); + } + +} + +stream_wrapper_register('custom', TrampolineTest::class); + + +$fp = fopen("custom://myvar", "r+"); +?> +--EXPECT-- +Trampoline for stream_open +Trampoline for stream_close diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9a1d00d6c77..d2fcb03fde6 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8723,7 +8723,9 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER)) SAVE_OPLINE_EX(); ZEND_OBSERVER_FCALL_BEGIN(execute_data); execute_data = EX(prev_execute_data); - LOAD_OPLINE(); + if (execute_data) { + LOAD_OPLINE(); + } ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP); zend_execute_ex(call); } @@ -8775,7 +8777,7 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER)) execute_data = EG(current_execute_data); - if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { + if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { ZEND_VM_RETURN(); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 9303953e4e8..02842be27f6 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3409,7 +3409,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z SAVE_OPLINE_EX(); execute_data = EX(prev_execute_data); - LOAD_OPLINE(); + if (execute_data) { + LOAD_OPLINE(); + } ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP); zend_execute_ex(call); } @@ -3460,7 +3462,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z execute_data = EG(current_execute_data); - if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { + if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { ZEND_VM_RETURN(); } @@ -3547,7 +3549,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ SAVE_OPLINE_EX(); zend_observer_fcall_begin(execute_data); execute_data = EX(prev_execute_data); - LOAD_OPLINE(); + if (execute_data) { + LOAD_OPLINE(); + } ZEND_ADD_CALL_FLAG(call, ZEND_CALL_TOP); zend_execute_ex(call); } @@ -3599,7 +3603,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ execute_data = EG(current_execute_data); - if (!EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { + if (!execute_data || !EX(func) || !ZEND_USER_CODE(EX(func)->type) || (call_info & ZEND_CALL_TOP)) { ZEND_VM_RETURN(); }