From f3cf2f4527eb6d60a418ab5155c37c0fc568f9f0 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 16 Jun 2026 18:07:31 -0400 Subject: [PATCH 1/3] zend_ast: Escape control bytes in exported string literals The AST pretty-printer single-quoted string literals and appended bytes verbatim, so a NUL in a literal survived into the string assert() passes to zend_throw_exception() as a const char*, truncating the failure message at the first NUL. Export literals containing a control byte double-quoted via zend_ast_export_qstr(), which escapes them as octal; literals without control bytes are unchanged. Fixes GH-22290 Closes GH-22350 --- NEWS | 2 ++ Zend/zend_ast.c | 26 +++++++++++++---- ext/standard/tests/assert/gh22290.phpt | 39 ++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 ext/standard/tests/assert/gh22290.phpt diff --git a/NEWS b/NEWS index 1a069963bd59..98f5bf7e718d 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,8 @@ PHP NEWS - Core: . Fixed bug GH-22280 (Incorrect compile error for goto to label preceding try/finally block). (Pratik Bhujel) + . Fixed bug GH-22290 (AST pretty printing does not correctly handle strings + containing NUL). (iliaal) - BCMath: . Fixed issues with oversized allocations and signed overflow in bcround() diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 9df2320d5666..a39f8d30820c 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1328,6 +1328,23 @@ static ZEND_COLD void zend_ast_export_qstr(smart_str *str, char quote, zend_stri } } +static ZEND_COLD void zend_ast_export_quoted_str(smart_str *str, zend_string *s) +{ + size_t i; + + for (i = 0; i < ZSTR_LEN(s); i++) { + if ((unsigned char) ZSTR_VAL(s)[i] < ' ') { + smart_str_appendc(str, '"'); + zend_ast_export_qstr(str, '"', s); + smart_str_appendc(str, '"'); + return; + } + } + smart_str_appendc(str, '\''); + zend_ast_export_str(str, s); + smart_str_appendc(str, '\''); +} + static ZEND_COLD void zend_ast_export_indent(smart_str *str, int indent) { while (indent > 0) { @@ -1612,9 +1629,7 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, zval *zv, int priorit str, Z_DVAL_P(zv), (int) EG(precision), /* zero_fraction */ true); break; case IS_STRING: - smart_str_appendc(str, '\''); - zend_ast_export_str(str, Z_STR_P(zv)); - smart_str_appendc(str, '\''); + zend_ast_export_quoted_str(str, Z_STR_P(zv)); break; case IS_ARRAY: { zend_long idx; @@ -1629,9 +1644,8 @@ static ZEND_COLD void zend_ast_export_zval(smart_str *str, zval *zv, int priorit smart_str_appends(str, ", "); } if (key) { - smart_str_appendc(str, '\''); - zend_ast_export_str(str, key); - smart_str_appends(str, "' => "); + zend_ast_export_quoted_str(str, key); + smart_str_appends(str, " => "); } else { smart_str_append_long(str, idx); smart_str_appends(str, " => "); diff --git a/ext/standard/tests/assert/gh22290.phpt b/ext/standard/tests/assert/gh22290.phpt new file mode 100644 index 000000000000..e519a60f5572 --- /dev/null +++ b/ext/standard/tests/assert/gh22290.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-22290: AST pretty printing does not correctly handle strings containing NUL +--INI-- +zend.assertions=1 +assert.exception=1 +--FILE-- +getMessage(), PHP_EOL; +} + +try { + assert(["a\x00b" => 1] === []); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert("tab\there" === ""); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + assert(str_contains("plain", "zzz")); +} catch (AssertionError $e) { + echo $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +assert(!str_contains($string, "\000")) +assert(["a\000b" => 1] === []) +assert("tab\there" === '') +assert(str_contains('plain', 'zzz')) From b0c78657949289c87d36eda5385d032294c40118 Mon Sep 17 00:00:00 2001 From: P3p111n0 Date: Wed, 17 Jun 2026 16:54:06 +0000 Subject: [PATCH 2/3] zend: fix missing return in global register detection (GH-22206) (#22320) --- Zend/Zend.m4 | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/Zend.m4 b/Zend/Zend.m4 index 47ea9c831d52..72bc116f7edb 100644 --- a/Zend/Zend.m4 +++ b/Zend/Zend.m4 @@ -317,6 +317,7 @@ int emu(const opcode_handler_t *ip, void *fp) { while ((*ip)()); FP = orig_fp; IP = orig_ip; + return 0; }], [])], [php_cv_have_global_register_vars=yes], [php_cv_have_global_register_vars=no]) From 05e9a57017b9112ab4f29b1beb6ea8e411176818 Mon Sep 17 00:00:00 2001 From: Peter Kokot Date: Wed, 17 Jun 2026 18:56:01 +0200 Subject: [PATCH 3/3] Update NEWS [skip ci] --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 98f5bf7e718d..015fc3ca6c65 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.24 +- Core: + . Fixed bug GH-22206 (missing return in global register detection). + (P3p111n0) + - Reflection: . Fixed bug GH-22324 (Ignore leading namespace separator in ReflectionParameter::__construct()). (jorgsowa)