Fix circumvented type check with return by ref + finally

Fixes GH-18736
Closes GH-19172
This commit is contained in:
Ilija Tovilo 2025-07-18 15:28:29 +02:00
parent a96b05e63f
commit d0fad34230
No known key found for this signature in database
GPG key ID: 115CEA7A713E12E9
3 changed files with 38 additions and 0 deletions

2
NEWS
View file

@ -17,6 +17,8 @@ PHP NEWS
delegated Generator). (Arnaud) delegated Generator). (Arnaud)
. Fixed bug GH-19326 (Calling Generator::throw() on a running generator with . Fixed bug GH-19326 (Calling Generator::throw() on a running generator with
a non-Generator delegate crashes). (Arnaud) a non-Generator delegate crashes). (Arnaud)
. Fixed bug GH-18736 (Circumvented type check with return by ref + finally).
(ilutov)
- FTP: - FTP:
. Fix theoretical issues with hrtime() not being available. (nielsdos) . Fix theoretical issues with hrtime() not being available. (nielsdos)

24
Zend/tests/gh18736.phpt Normal file
View file

@ -0,0 +1,24 @@
--TEST--
GH-18736: Circumvented type check with return by ref + finally
--FILE--
<?php
function &test(): int {
$x = 0;
try {
return $x;
} finally {
$x = 'test';
}
}
try {
$x = &test();
var_dump($x);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
test(): Return value must be of type int, string returned

View file

@ -5201,8 +5201,20 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */
expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, 0); expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, 0);
} }
uint32_t opnum_before_finally = get_next_op_number();
zend_handle_loops_and_finally((expr_node.op_type & (IS_TMP_VAR | IS_VAR)) ? &expr_node : NULL); zend_handle_loops_and_finally((expr_node.op_type & (IS_TMP_VAR | IS_VAR)) ? &expr_node : NULL);
/* Content of reference might have changed in finally, repeat type check. */
if (by_ref
/* Check if any opcodes were emitted since the last return type check. */
&& opnum_before_finally != get_next_op_number()
&& !is_generator
&& (CG(active_op_array)->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
zend_emit_return_type_check(
expr_ast ? &expr_node : NULL, CG(active_op_array)->arg_info - 1, 0);
}
opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN, opline = zend_emit_op(NULL, by_ref ? ZEND_RETURN_BY_REF : ZEND_RETURN,
&expr_node, NULL); &expr_node, NULL);