From 553d79c709abfd910c7051147243de89f06e3729 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 14 Nov 2024 22:01:47 +0100 Subject: [PATCH] Fix GH-16799: Assertion failure at Zend/zend_vm_execute.h:7469 zend_is_callable_ex() can unfortunately emit a deprecation, and then a user error handler can throw an exception. This causes an assert failure at ZEND_VM_NEXT_OPCODE(). We fix this by checking if there's an exception after zend_is_callable_ex(). Closes GH-16803. --- NEWS | 2 ++ Zend/tests/gh16799.phpt | 21 +++++++++++++++++++++ Zend/zend_vm_def.h | 10 ++++++++++ Zend/zend_vm_execute.h | 30 ++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+) create mode 100644 Zend/tests/gh16799.phpt diff --git a/NEWS b/NEWS index 97b4957143e..62c1e3eeeeb 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,8 @@ PHP NEWS . Fail early in *nix configuration build script. (hakre) . Fixed bug GH-16727 (Opcache bad signal 139 crash in ZTS bookworm (frankenphp)). (nielsdos) + . Fixed bug GH-16799 (Assertion failure at Zend/zend_vm_execute.h:7469). + (nielsdos) - FPM: . Fixed GH-16432 (PHP-FPM 8.2 SIGSEGV in fpm_get_status). (Jakub Zelenka) diff --git a/Zend/tests/gh16799.phpt b/Zend/tests/gh16799.phpt new file mode 100644 index 00000000000..9348c38fc30 --- /dev/null +++ b/Zend/tests/gh16799.phpt @@ -0,0 +1,21 @@ +--TEST-- +GH-16799 (Assertion failure at Zend/zend_vm_execute.h) +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Exception: Use of "static" in callables is deprecated in %s:%d +Stack trace: +#0 %s(%d): {closure}(%d, 'Use of "static"...', %s, %d) +#1 %s(%d): Test::test() +#2 {main} + thrown in %s on line %d diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 2a48b9c3713..26cb95c8f94 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -3807,6 +3807,16 @@ ZEND_VM_HANDLER(118, ZEND_INIT_USER_CALL, CONST, CONST|TMPVAR|CV, NUM) function_name = GET_OP2_ZVAL_PTR(BP_VAR_R); if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { ZEND_ASSERT(!error); + + /* Deprecation can be emitted from zend_is_callable_ex(), which can + * invoke a user error handler and throw an exception. + * For the CONST and CV case we reuse the same exception block below + * to make sure we don't increase VM size too much. */ + if (!(OP2_TYPE & (IS_TMP_VAR|IS_VAR)) && UNEXPECTED(EG(exception))) { + FREE_OP2(); + HANDLE_EXCEPTION(); + } + func = fcc.function_handler; object_or_called_scope = fcc.called_scope; if (func->common.fn_flags & ZEND_ACC_CLOSURE) { diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 3bc01a597fa..39bdd2e80b8 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7023,6 +7023,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CONS function_name = RT_CONSTANT(opline, opline->op2); if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { ZEND_ASSERT(!error); + + /* Deprecation can be emitted from zend_is_callable_ex(), which can + * invoke a user error handler and throw an exception. + * For the CONST and CV case we reuse the same exception block below + * to make sure we don't increase VM size too much. */ + if (!(IS_CONST & (IS_TMP_VAR|IS_VAR)) && UNEXPECTED(EG(exception))) { + + HANDLE_EXCEPTION(); + } + func = fcc.function_handler; object_or_called_scope = fcc.called_scope; if (func->common.fn_flags & ZEND_ACC_CLOSURE) { @@ -9368,6 +9378,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_TMPV function_name = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { ZEND_ASSERT(!error); + + /* Deprecation can be emitted from zend_is_callable_ex(), which can + * invoke a user error handler and throw an exception. + * For the CONST and CV case we reuse the same exception block below + * to make sure we don't increase VM size too much. */ + if (!((IS_TMP_VAR|IS_VAR) & (IS_TMP_VAR|IS_VAR)) && UNEXPECTED(EG(exception))) { + zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); + HANDLE_EXCEPTION(); + } + func = fcc.function_handler; object_or_called_scope = fcc.called_scope; if (func->common.fn_flags & ZEND_ACC_CLOSURE) { @@ -11742,6 +11762,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_INIT_USER_CALL_SPEC_CONST_CV_H function_name = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); if (zend_is_callable_ex(function_name, NULL, 0, NULL, &fcc, &error)) { ZEND_ASSERT(!error); + + /* Deprecation can be emitted from zend_is_callable_ex(), which can + * invoke a user error handler and throw an exception. + * For the CONST and CV case we reuse the same exception block below + * to make sure we don't increase VM size too much. */ + if (!(IS_CV & (IS_TMP_VAR|IS_VAR)) && UNEXPECTED(EG(exception))) { + + HANDLE_EXCEPTION(); + } + func = fcc.function_handler; object_or_called_scope = fcc.called_scope; if (func->common.fn_flags & ZEND_ACC_CLOSURE) {