From 4f705871fc5b510903551f00a8c1806dad1cd2bf Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 22 Oct 2025 21:44:50 +0200 Subject: [PATCH 1/2] Fix OSS-Fuzz #454273637: UAF with printf optimization and const output Note that ZEND_COPY_TMP isn't even valid for CONSTs, and we would need to add a ref even if it were, so just add special handling instead to simplify it. --- Zend/tests/oss_fuzz_454273637.phpt | 8 ++++++++ Zend/zend_compile.c | 13 ++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 Zend/tests/oss_fuzz_454273637.phpt diff --git a/Zend/tests/oss_fuzz_454273637.phpt b/Zend/tests/oss_fuzz_454273637.phpt new file mode 100644 index 0000000000000..fbcdb57ed2a16 --- /dev/null +++ b/Zend/tests/oss_fuzz_454273637.phpt @@ -0,0 +1,8 @@ +--TEST-- +OSS-Fuzz #454273637 (UAF with printf optimization and const output) +--FILE-- + +--EXPECT-- +% diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 97e36d6b2498a..de413a1132db4 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5009,9 +5009,16 @@ static zend_result zend_compile_func_printf(znode *result, zend_ast_list *args) * pass in the Zend Optimizer if the result of the printf() is in fact * unused */ znode copy; - zend_emit_op_tmp(©, ZEND_COPY_TMP, &rope_result, NULL); - zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL); - zend_emit_op_tmp(result, ZEND_STRLEN, ©, NULL); + if (rope_result.op_type != IS_CONST) { + /* Note: ZEND_COPY_TMP is only valid for TMPVAR. */ + zend_emit_op_tmp(©, ZEND_COPY_TMP, &rope_result, NULL); + zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL); + zend_emit_op_tmp(result, ZEND_STRLEN, ©, NULL); + } else { + zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL); + result->op_type = IS_CONST; + ZVAL_LONG(&result->u.constant, Z_STRLEN(rope_result.u.constant)); + } return SUCCESS; } From be0c643d016b82fb2561e7d71565f2a2d255a4fc Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 23 Oct 2025 00:45:14 +0200 Subject: [PATCH 2/2] assert --- Zend/zend_compile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index de413a1132db4..8f10f1577ca72 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5011,6 +5011,7 @@ static zend_result zend_compile_func_printf(znode *result, zend_ast_list *args) znode copy; if (rope_result.op_type != IS_CONST) { /* Note: ZEND_COPY_TMP is only valid for TMPVAR. */ + ZEND_ASSERT(rope_result.op_type == IS_TMP_VAR); zend_emit_op_tmp(©, ZEND_COPY_TMP, &rope_result, NULL); zend_emit_op(NULL, ZEND_ECHO, &rope_result, NULL); zend_emit_op_tmp(result, ZEND_STRLEN, ©, NULL);