Commit 084e4096942 for php.net
commit 084e409694202af1562d3c5f91caeb88534afe2c
Author: Ilija Tovilo <ilija.tovilo@me.com>
Date: Wed Oct 22 01:03:43 2025 +0200
Remove zend_exception_save() and zend_exception_restore()
These are leftovers from the pre-PHP-7.0 era. This also implicitly solves
GH-20564 by not clearing exceptions before entering the autoloader.
Closes GH-20256
Fixes GH-20564
diff --git a/NEWS b/NEWS
index f42cfc4e5ee..74816c94a29 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,7 @@ PHP NEWS
. Added `clamp()`. (kylekatarnls, thinkverse)
. Fix OSS-Fuzz #429429090 (Failed assertion on unset() with uninitialized
container). (ilutov)
+ . Fixed GH-20564 (Don't call autoloaders with pending exception). (ilutov)
- Date:
. Update timelib to 2022.16. (Derick)
diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS
index 748549e085b..2132006540c 100644
--- a/UPGRADING.INTERNALS
+++ b/UPGRADING.INTERNALS
@@ -54,6 +54,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES
ZEND_ACC_USER_ARG_INFO flag was set.
. Added zend_ast_call_get_args() to fetch the argument node from any call
node.
+ . The zend_exception_save() and zend_exception_restore() functions were
+ removed.
========================
2. Build system changes
diff --git a/Zend/tests/gh20564.phpt b/Zend/tests/gh20564.phpt
new file mode 100644
index 00000000000..53311d952de
--- /dev/null
+++ b/Zend/tests/gh20564.phpt
@@ -0,0 +1,24 @@
+--TEST--
+GH-20564: Don't call autoloaders with pending exception
+--CREDITS--
+Viet Hoang Luu (@vi3tL0u1s)
+--FILE--
+<?php
+
+class A {
+ function __call($method, $args) {
+ eval("<<<ENDOFSTRING\n Test\n ENDOFSTRING;");
+ spl_autoload_register('A::test');
+ array_map('B::test', []);
+ }
+}
+
+try {
+ (new A)->test();
+} catch (Throwable $e) {
+ echo $e->getMessage(), "\n";
+}
+
+?>
+--EXPECT--
+array_map(): Argument #1 ($callback) must be a valid callback or null, class "B" not found
diff --git a/Zend/zend.c b/Zend/zend.c
index 6b0ffb73ad7..d2be69a7576 100644
--- a/Zend/zend.c
+++ b/Zend/zend.c
@@ -1978,7 +1978,6 @@ ZEND_API zend_result zend_execute_script(int type, zval *retval, zend_file_handl
zend_result ret = SUCCESS;
if (op_array) {
zend_execute(op_array, retval);
- zend_exception_restore();
if (UNEXPECTED(EG(exception))) {
if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
zend_user_exception_handler();
diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c
index 191d8f7fe6a..52e3ab00925 100644
--- a/Zend/zend_exceptions.c
+++ b/Zend/zend_exceptions.c
@@ -145,31 +145,6 @@ void zend_exception_set_previous(zend_object *exception, zend_object *add_previo
}
/* }}} */
-void zend_exception_save(void) /* {{{ */
-{
- if (EG(prev_exception)) {
- zend_exception_set_previous(EG(exception), EG(prev_exception));
- }
- if (EG(exception)) {
- EG(prev_exception) = EG(exception);
- }
- EG(exception) = NULL;
-}
-/* }}} */
-
-void zend_exception_restore(void) /* {{{ */
-{
- if (EG(prev_exception)) {
- if (EG(exception)) {
- zend_exception_set_previous(EG(exception), EG(prev_exception));
- } else {
- EG(exception) = EG(prev_exception);
- }
- EG(prev_exception) = NULL;
- }
-}
-/* }}} */
-
static zend_always_inline bool is_handle_exception_set(void) {
zend_execute_data *execute_data = EG(current_execute_data);
return !execute_data
@@ -241,10 +216,6 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /*
ZEND_API void zend_clear_exception(void) /* {{{ */
{
zend_object *exception;
- if (EG(prev_exception)) {
- OBJ_RELEASE(EG(prev_exception));
- EG(prev_exception) = NULL;
- }
if (!EG(exception)) {
return;
}
diff --git a/Zend/zend_exceptions.h b/Zend/zend_exceptions.h
index 24d9f4efd80..e5a6be2f32f 100644
--- a/Zend/zend_exceptions.h
+++ b/Zend/zend_exceptions.h
@@ -41,8 +41,6 @@ extern ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
extern ZEND_API zend_class_entry *zend_ce_request_parse_body_exception;
ZEND_API void zend_exception_set_previous(zend_object *exception, zend_object *add_previous);
-ZEND_API void zend_exception_save(void);
-ZEND_API void zend_exception_restore(void);
ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception);
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index a4460286bf6..e95931276ef 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -1315,6 +1315,7 @@ ZEND_API bool zend_internal_call_should_throw(const zend_function *fbc, zend_exe
if ((fbc->common.fn_flags & ZEND_ACC_HAS_TYPE_HINTS) &&
!zend_verify_internal_arg_types(fbc, call)) {
+ zend_clear_exception();
return 1;
}
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index e134d3d496b..69337e27fd5 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -176,7 +176,6 @@ void init_executor(void) /* {{{ */
ZEND_ATOMIC_BOOL_INIT(&EG(timed_out), false);
EG(exception) = NULL;
- EG(prev_exception) = NULL;
EG(fake_scope) = NULL;
EG(trampoline).common.function_name = NULL;
@@ -1268,9 +1267,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string *
zend_long previous_lineno = EG(lineno_override);
EG(filename_override) = NULL;
EG(lineno_override) = -1;
- zend_exception_save();
ce = zend_autoload(autoload_name, lc_name);
- zend_exception_restore();
EG(filename_override) = previous_filename;
EG(lineno_override) = previous_lineno;
diff --git a/Zend/zend_globals.h b/Zend/zend_globals.h
index ef81ae5faaf..f09b81acb31 100644
--- a/Zend/zend_globals.h
+++ b/Zend/zend_globals.h
@@ -255,7 +255,7 @@ struct _zend_executor_globals {
zend_objects_store objects_store;
zend_lazy_objects_store lazy_objects_store;
- zend_object *exception, *prev_exception;
+ zend_object *exception;
const zend_op *opline_before_exception;
zend_op exception_op[3];
diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l
index 1e26ddbd991..5a8a78cc3bd 100644
--- a/Zend/zend_language_scanner.l
+++ b/Zend/zend_language_scanner.l
@@ -2765,7 +2765,8 @@ skip_escape_conversion:
zend_ptr_stack_reverse_apply(¤t_state.heredoc_label_stack, copy_heredoc_label_stack);
- zend_exception_save();
+ zend_object *prev_exception = EG(exception);
+ EG(exception) = NULL;
while (heredoc_nesting_level) {
zval zv;
int retval;
@@ -2794,7 +2795,7 @@ skip_escape_conversion:
heredoc_nesting_level = 0;
}
}
- zend_exception_restore();
+ EG(exception) = prev_exception;
if (
(first_token == T_VARIABLE
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 86de5992a8f..9840bf28040 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -4797,10 +4797,8 @@ ZEND_VM_COLD_CONST_HANDLER(108, ZEND_THROW, CONST|TMPVAR|CV, ANY)
}
} while (0);
- zend_exception_save();
Z_TRY_ADDREF_P(value);
zend_throw_exception_object(value);
- zend_exception_restore();
FREE_OP1();
HANDLE_EXCEPTION();
}
@@ -4813,7 +4811,6 @@ ZEND_VM_HANDLER(107, ZEND_CATCH, CONST, JMP_ADDR, LAST_CATCH|CACHE_SLOT)
SAVE_OPLINE();
/* Check whether an exception has been thrown, if not, jump over code */
- zend_exception_restore();
if (EG(exception) == NULL) {
ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 0);
}
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 62d645c1510..7aa0296aa42 100644
Binary files a/Zend/zend_vm_execute.h and b/Zend/zend_vm_execute.h differ
diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c
index 7acb14b778f..f4134212bc4 100644
--- a/ext/opcache/ZendAccelerator.c
+++ b/ext/opcache/ZendAccelerator.c
@@ -4663,7 +4663,6 @@ static zend_result accel_preload(const char *config, bool in_child)
zend_destroy_file_handle(&file_handle);
if (op_array) {
zend_execute(op_array, NULL);
- zend_exception_restore();
if (UNEXPECTED(EG(exception))) {
if (Z_TYPE(EG(user_exception_handler)) != IS_UNDEF) {
zend_user_exception_handler();
diff --git a/sapi/fuzzer/fuzzer-execute-common.h b/sapi/fuzzer/fuzzer-execute-common.h
index 338c771e551..ef2ff4ee79b 100644
--- a/sapi/fuzzer/fuzzer-execute-common.h
+++ b/sapi/fuzzer/fuzzer-execute-common.h
@@ -134,7 +134,6 @@ ZEND_ATTRIBUTE_UNUSED static void create_file(void) {
ZEND_ATTRIBUTE_UNUSED static void opcache_invalidate(void) {
steps_left = MAX_STEPS;
- zend_exception_save();
zval retval, args[2];
zend_function *fn = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("opcache_invalidate"));
ZEND_ASSERT(fn != NULL);
@@ -145,5 +144,4 @@ ZEND_ATTRIBUTE_UNUSED static void opcache_invalidate(void) {
ZEND_ASSERT(Z_TYPE(retval) == IS_TRUE);
zval_ptr_dtor(&args[0]);
zval_ptr_dtor(&retval);
- zend_exception_restore();
}
diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c
index 7215888cb25..9566c1abd4e 100644
--- a/sapi/phpdbg/phpdbg_prompt.c
+++ b/sapi/phpdbg/phpdbg_prompt.c
@@ -716,10 +716,6 @@ static inline void phpdbg_handle_exception(void) /* {{{ */
phpdbg_writeln("%s", ZSTR_VAL(msg));
zend_string_release(msg);
- if (EG(prev_exception)) {
- OBJ_RELEASE(EG(prev_exception));
- EG(prev_exception) = 0;
- }
OBJ_RELEASE(ex);
EG(opline_before_exception) = NULL;
@@ -876,7 +872,6 @@ PHPDBG_COMMAND(run) /* {{{ */
} zend_end_try();
if (restore) {
- zend_exception_restore();
zend_try {
zend_try_exception_handler();
PHPDBG_G(in_execution) = 1;