From 8e3ab806675d86ba0fb03b350d43dc45f51703e9 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 16 Jun 2026 16:27:53 -0400 Subject: [PATCH 1/4] Remove stray printf() on invalid DSN version in pdo_dblib When the connection string specified an unrecognized TDS version, pdo_dblib_handle_factory() wrote the message to stdout via printf() before raising the proper PDO error, corrupting SAPI output. The pdo_raise_impl_error() call right below already reports the condition; drop the printf(). Closes GH-22345 --- ext/pdo_dblib/dblib_driver.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index 0055dcb03b3c..00ecf2d232cd 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -539,7 +539,6 @@ static int pdo_dblib_handle_factory(pdo_dbh_t *dbh, zval *driver_options) } if (i==nvers) { - printf("Invalid version '%s'\n", vars[5].optval); pdo_raise_impl_error(dbh, NULL, "HY000", "PDO_DBLIB: Invalid version specified in connection string."); goto cleanup; /* unknown version specified */ } From 1a5a81ca9f15d1d46d9b705e2f66aec4e9d41cc0 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 16 Jun 2026 16:28:31 -0400 Subject: [PATCH 2/4] Fix buffer overflow converting @@IDENTITY in pdo_dblib lastInsertId dblib_handle_last_id() converted the @@IDENTITY value into a 32-byte buffer with dbconvert()'s destination length set to -1, which disables FreeTDS's destination bounds check. A numeric(p,0) IDENTITY column with precision over ~30 produces a textual form longer than 32 bytes, overflowing the buffer. Size the buffer for the widest @@IDENTITY (numeric(38,0): 38 digits, sign, NUL) and pass the real destination length so dbconvert() stays in bounds, mirroring the explicit-destlen fix already in pdo_dblib_stmt_stringify_col(). Closes GH-22348 --- ext/pdo_dblib/dblib_driver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/pdo_dblib/dblib_driver.c b/ext/pdo_dblib/dblib_driver.c index 00ecf2d232cd..d1d849168bab 100644 --- a/ext/pdo_dblib/dblib_driver.c +++ b/ext/pdo_dblib/dblib_driver.c @@ -267,8 +267,8 @@ zend_string *dblib_handle_last_id(pdo_dbh_t *dbh, const zend_string *name) return NULL; } - id = emalloc(32); - len = dbconvert(NULL, (dbcoltype(H->link, 1)) , (dbdata(H->link, 1)) , (dbdatlen(H->link, 1)), SQLCHAR, (BYTE *)id, (DBINT)-1); + id = emalloc(40); + len = dbconvert(NULL, (dbcoltype(H->link, 1)) , (dbdata(H->link, 1)) , (dbdatlen(H->link, 1)), SQLCHAR, (BYTE *)id, (DBINT)40); dbcancel(H->link); ret_id = zend_string_init(id, len, 0); From 47f060ca330e16f9ea9b7b2b3edf5c4861219128 Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Tue, 16 Jun 2026 00:15:53 +0200 Subject: [PATCH 3/4] Ignore leading namespace separator in ReflectionParameter::__construct() Strip a leading '\\' before lowercasing, matching ReflectionFunction::__construct(). Fixes GH-22324 Closes GH-22325 --- NEWS | 3 +++ ext/reflection/php_reflection.c | 17 ++++++++++++++--- .../ReflectionParameter_leading_backslash.phpt | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 ext/reflection/tests/ReflectionParameter_leading_backslash.phpt diff --git a/NEWS b/NEWS index 2ea7143e39e6..1a069963bd59 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.24 +- Reflection: + . Fixed bug GH-22324 (Ignore leading namespace separator in + ReflectionParameter::__construct()). (jorgsowa) 02 Jul 2026, PHP 8.4.23 diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 1f5b448f85fe..eba5600c16a2 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -2480,9 +2480,20 @@ ZEND_METHOD(ReflectionParameter, __construct) switch (Z_TYPE_P(reference)) { case IS_STRING: { - zend_string *lcname = zend_string_tolower(Z_STR_P(reference)); - fptr = zend_hash_find_ptr(EG(function_table), lcname); - zend_string_release(lcname); + zend_string *fname = Z_STR_P(reference); + zend_string *lcname; + if (UNEXPECTED(ZSTR_VAL(fname)[0] == '\\')) { + /* Ignore leading "\" */ + ALLOCA_FLAG(use_heap) + ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(fname) - 1, use_heap); + zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(fname) + 1, ZSTR_LEN(fname) - 1); + fptr = zend_fetch_function(lcname); + ZSTR_ALLOCA_FREE(lcname, use_heap); + } else { + lcname = zend_string_tolower(fname); + fptr = zend_fetch_function(lcname); + zend_string_release(lcname); + } if (!fptr) { zend_throw_exception_ex(reflection_exception_ptr, 0, "Function %s() does not exist", Z_STRVAL_P(reference)); diff --git a/ext/reflection/tests/ReflectionParameter_leading_backslash.phpt b/ext/reflection/tests/ReflectionParameter_leading_backslash.phpt new file mode 100644 index 000000000000..b0a7827c2764 --- /dev/null +++ b/ext/reflection/tests/ReflectionParameter_leading_backslash.phpt @@ -0,0 +1,17 @@ +--TEST-- +ReflectionParameter::__construct(): a leading "\" in the function name is ignored +--FILE-- +getName()); + +$p = new ReflectionParameter("demo", 0); +var_dump($p->getName()); + +?> +--EXPECT-- +string(3) "arg" +string(3) "arg" From f3cf2f4527eb6d60a418ab5155c37c0fc568f9f0 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 16 Jun 2026 18:07:31 -0400 Subject: [PATCH 4/4] 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'))