From 1748b8111e97f14f709553c7a5498fcdfcd6c754 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 13 Oct 2020 11:38:30 +0200 Subject: [PATCH] Fix handling of throwing undef var in verify return If we have an undefined variable and null is not accepted by the return type, we want to throw just the undef var error. In this case this lead to an infinite loop, because we overwrite the exception opline in SAVE_OPLINE and it does not get reset when chaining into a previous exception. Add an assertiong to catch this case earlier. --- Zend/tests/undef_var_in_verify_return.phpt | 23 ++++++++++++++++++++ Zend/zend_exceptions.c | 12 ++++++++--- Zend/zend_vm_def.h | 5 ++++- Zend/zend_vm_execute.h | 25 +++++++++++++++++----- 4 files changed, 56 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/undef_var_in_verify_return.phpt diff --git a/Zend/tests/undef_var_in_verify_return.phpt b/Zend/tests/undef_var_in_verify_return.phpt new file mode 100644 index 00000000000..b8c263c424c --- /dev/null +++ b/Zend/tests/undef_var_in_verify_return.phpt @@ -0,0 +1,23 @@ +--TEST-- +Throwing undef var in verify return +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ErrorException: Undefined variable $test in %s:%d +Stack trace: +#0 %s(%d): {closure}(2, 'Undefined varia...', '%s', 8) +#1 %s(%d): test() +#2 {main} + thrown in %s on line %d diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index f037f0399a1..b52b1ab113e 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -144,6 +144,13 @@ void zend_exception_restore(void) /* {{{ */ } /* }}} */ +static zend_always_inline zend_bool is_handle_exception_set() { + zend_execute_data *execute_data = EG(current_execute_data); + return !execute_data->func + || !ZEND_USER_CODE(execute_data->func->common.type) + || execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION; +} + ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* {{{ */ { #ifdef HAVE_DTRACE @@ -161,6 +168,7 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* zend_exception_set_previous(exception, EG(exception)); EG(exception) = exception; if (previous) { + ZEND_ASSERT(is_handle_exception_set() && "HANDLE_EXCEPTION not set?"); return; } } @@ -179,9 +187,7 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* zend_throw_exception_hook(exception); } - if (!EG(current_execute_data)->func || - !ZEND_USER_CODE(EG(current_execute_data)->func->common.type) || - EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) { + if (is_handle_exception_set()) { /* no need to rethrow the exception */ return; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index d647dddeab0..cff1012acae 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4195,8 +4195,11 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV if (OP1_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) { SAVE_OPLINE(); retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) { - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 27d1ee15719..1786cfcc83a 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -9707,8 +9707,11 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) { SAVE_OPLINE(); retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) { - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } } @@ -20051,8 +20054,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) { SAVE_OPLINE(); retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) { - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } } @@ -27656,8 +27662,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) { SAVE_OPLINE(); retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) { - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } } @@ -34853,8 +34862,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) { SAVE_OPLINE(); retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) { - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } } @@ -46545,8 +46557,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) { SAVE_OPLINE(); retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1(); + if (UNEXPECTED(EG(exception))) { + HANDLE_EXCEPTION(); + } if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) { - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); + ZEND_VM_NEXT_OPCODE(); } }