From d568337680657c37a3db4f92fe4c883da3e06273 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 29 Jun 2024 14:16:54 +0200 Subject: [PATCH 1/2] Fix OSS-Fuzz #69765: Yield reference to nullsafe chain You cannot return or yield a reference to a nullsafe chain. This was checked already in zend_compile_return but not yet in zend_compile_yield. Closes GH-14716. --- NEWS | 1 + Zend/tests/oss-fuzz-69765.phpt | 10 ++++++++++ Zend/zend_compile.c | 4 ++++ 3 files changed, 15 insertions(+) create mode 100644 Zend/tests/oss-fuzz-69765.phpt diff --git a/NEWS b/NEWS index 1ae1996bb4a..4182d5d7745 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,7 @@ PHP NEWS . Fixed bug GH-14626 (Fix is_zend_ptr() for huge blocks). (Arnaud) . Fixed bug GH-14590 (Memory leak in FPM test gh13563-conf-bool-env.phpt. (nielsdos) + . Fixed OSS-Fuzz #69765. (nielsdos) - Dom: . Fixed bug GH-14702 (DOMDocument::xinclude() crash). (nielsdos) diff --git a/Zend/tests/oss-fuzz-69765.phpt b/Zend/tests/oss-fuzz-69765.phpt new file mode 100644 index 00000000000..011b1ea5252 --- /dev/null +++ b/Zend/tests/oss-fuzz-69765.phpt @@ -0,0 +1,10 @@ +--TEST-- +OSS-Fuzz #69765: yield reference to nullsafe chain +--FILE-- +y?->y; +} +?> +--EXPECTF-- +Fatal error: Cannot take reference of a nullsafe chain in %s on line %d diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e7f9ed8f000..502a29863a5 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -9329,6 +9329,10 @@ static void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */ if (value_ast) { if (returns_by_ref && zend_is_variable(value_ast)) { + if (zend_ast_is_short_circuited(value_ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot take reference of a nullsafe chain"); + } + zend_compile_var(&value_node, value_ast, BP_VAR_W, 1); } else { zend_compile_expr(&value_node, value_ast); From 8fd095669a4f1f2c4316ee95ccc7c0b5c53b37c3 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 29 Jun 2024 14:20:31 +0200 Subject: [PATCH 2/2] Factor out common check for short-circuited ast --- Zend/zend_compile.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 502a29863a5..8662e188aaa 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2312,6 +2312,13 @@ static bool zend_ast_is_short_circuited(const zend_ast *ast) } } +static void zend_assert_not_short_circuited(const zend_ast *ast) +{ + if (zend_ast_is_short_circuited(ast)) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot take reference of a nullsafe chain"); + } +} + /* Mark nodes that are an inner part of a short-circuiting chain. * We should not perform a "commit" on them, as it will be performed by the outer-most node. * We do this to avoid passing down an argument in various compile functions. */ @@ -3304,9 +3311,8 @@ static void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */ if (!zend_is_variable_or_call(expr_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot assign reference to non referenceable value"); - } else if (zend_ast_is_short_circuited(expr_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot take reference of a nullsafe chain"); + } else { + zend_assert_not_short_circuited(expr_ast); } zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1); @@ -3348,9 +3354,7 @@ static void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */ zend_error_noreturn(E_COMPILE_ERROR, "Cannot re-assign $this"); } zend_ensure_writable_variable(target_ast); - if (zend_ast_is_short_circuited(source_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot take reference of a nullsafe chain"); - } + zend_assert_not_short_circuited(source_ast); if (is_globals_fetch(source_ast)) { zend_error_noreturn(E_COMPILE_ERROR, "Cannot acquire reference to $GLOBALS"); } @@ -5026,10 +5030,7 @@ static void zend_compile_return(zend_ast *ast) /* {{{ */ expr_node.op_type = IS_CONST; ZVAL_NULL(&expr_node.u.constant); } else if (by_ref && zend_is_variable(expr_ast)) { - if (zend_ast_is_short_circuited(expr_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot take reference of a nullsafe chain"); - } - + zend_assert_not_short_circuited(expr_ast); zend_compile_var(&expr_node, expr_ast, BP_VAR_W, 1); } else { zend_compile_expr(&expr_node, expr_ast); @@ -9329,10 +9330,7 @@ static void zend_compile_yield(znode *result, zend_ast *ast) /* {{{ */ if (value_ast) { if (returns_by_ref && zend_is_variable(value_ast)) { - if (zend_ast_is_short_circuited(value_ast)) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot take reference of a nullsafe chain"); - } - + zend_assert_not_short_circuited(value_ast); zend_compile_var(&value_node, value_ast, BP_VAR_W, 1); } else { zend_compile_expr(&value_node, value_ast);