Skip to content

Commit 1748b81

Browse files
committed
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.
1 parent 8c68745 commit 1748b81

File tree

4 files changed

+56
-9
lines changed

4 files changed

+56
-9
lines changed
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--TEST--
2+
Throwing undef var in verify return
3+
--FILE--
4+
<?php
5+
6+
set_error_handler(function(int $severity, string $message, string $filename, int $lineNumber): void {
7+
throw new ErrorException($message, 0, $severity, $filename, $lineNumber);
8+
});
9+
10+
function test(): string {
11+
return $test;
12+
}
13+
14+
test();
15+
16+
?>
17+
--EXPECTF--
18+
Fatal error: Uncaught ErrorException: Undefined variable $test in %s:%d
19+
Stack trace:
20+
#0 %s(%d): {closure}(2, 'Undefined varia...', '%s', 8)
21+
#1 %s(%d): test()
22+
#2 {main}
23+
thrown in %s on line %d

Zend/zend_exceptions.c

+9-3
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@ void zend_exception_restore(void) /* {{{ */
144144
}
145145
/* }}} */
146146

147+
static zend_always_inline zend_bool is_handle_exception_set() {
148+
zend_execute_data *execute_data = EG(current_execute_data);
149+
return !execute_data->func
150+
|| !ZEND_USER_CODE(execute_data->func->common.type)
151+
|| execute_data->opline->opcode == ZEND_HANDLE_EXCEPTION;
152+
}
153+
147154
ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /* {{{ */
148155
{
149156
#ifdef HAVE_DTRACE
@@ -161,6 +168,7 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /*
161168
zend_exception_set_previous(exception, EG(exception));
162169
EG(exception) = exception;
163170
if (previous) {
171+
ZEND_ASSERT(is_handle_exception_set() && "HANDLE_EXCEPTION not set?");
164172
return;
165173
}
166174
}
@@ -179,9 +187,7 @@ ZEND_API ZEND_COLD void zend_throw_exception_internal(zend_object *exception) /*
179187
zend_throw_exception_hook(exception);
180188
}
181189

182-
if (!EG(current_execute_data)->func ||
183-
!ZEND_USER_CODE(EG(current_execute_data)->func->common.type) ||
184-
EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) {
190+
if (is_handle_exception_set()) {
185191
/* no need to rethrow the exception */
186192
return;
187193
}

Zend/zend_vm_def.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -4195,8 +4195,11 @@ ZEND_VM_COLD_CONST_HANDLER(124, ZEND_VERIFY_RETURN_TYPE, CONST|TMP|VAR|UNUSED|CV
41954195
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) {
41964196
SAVE_OPLINE();
41974197
retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1();
4198+
if (UNEXPECTED(EG(exception))) {
4199+
HANDLE_EXCEPTION();
4200+
}
41984201
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) {
4199-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
4202+
ZEND_VM_NEXT_OPCODE();
42004203
}
42014204
}
42024205

Zend/zend_vm_execute.h

+20-5
Original file line numberDiff line numberDiff line change
@@ -9707,8 +9707,11 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYP
97079707
if (IS_CONST == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) {
97089708
SAVE_OPLINE();
97099709
retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1();
9710+
if (UNEXPECTED(EG(exception))) {
9711+
HANDLE_EXCEPTION();
9712+
}
97109713
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) {
9711-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
9714+
ZEND_VM_NEXT_OPCODE();
97129715
}
97139716
}
97149717

@@ -20051,8 +20054,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_TMP_UN
2005120054
if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) {
2005220055
SAVE_OPLINE();
2005320056
retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1();
20057+
if (UNEXPECTED(EG(exception))) {
20058+
HANDLE_EXCEPTION();
20059+
}
2005420060
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) {
20055-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
20061+
ZEND_VM_NEXT_OPCODE();
2005620062
}
2005720063
}
2005820064

@@ -27656,8 +27662,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_VAR_UN
2765627662
if (IS_VAR == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) {
2765727663
SAVE_OPLINE();
2765827664
retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1();
27665+
if (UNEXPECTED(EG(exception))) {
27666+
HANDLE_EXCEPTION();
27667+
}
2765927668
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) {
27660-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
27669+
ZEND_VM_NEXT_OPCODE();
2766127670
}
2766227671
}
2766327672

@@ -34853,8 +34862,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_UNUSED
3485334862
if (IS_UNUSED == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) {
3485434863
SAVE_OPLINE();
3485534864
retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1();
34865+
if (UNEXPECTED(EG(exception))) {
34866+
HANDLE_EXCEPTION();
34867+
}
3485634868
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) {
34857-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
34869+
ZEND_VM_NEXT_OPCODE();
3485834870
}
3485934871
}
3486034872

@@ -46545,8 +46557,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_VERIFY_RETURN_TYPE_SPEC_CV_UNU
4654546557
if (IS_CV == IS_CV && UNEXPECTED(Z_ISUNDEF_P(retval_ptr))) {
4654646558
SAVE_OPLINE();
4654746559
retval_ref = retval_ptr = ZVAL_UNDEFINED_OP1();
46560+
if (UNEXPECTED(EG(exception))) {
46561+
HANDLE_EXCEPTION();
46562+
}
4654846563
if (ZEND_TYPE_FULL_MASK(ret_info->type) & MAY_BE_NULL) {
46549-
ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION();
46564+
ZEND_VM_NEXT_OPCODE();
4655046565
}
4655146566
}
4655246567

0 commit comments

Comments
 (0)