From 9d0c492d304b341bce19f4b552c64eb536e19944 Mon Sep 17 00:00:00 2001 From: Calvin Buckley Date: Tue, 25 Mar 2025 16:38:46 -0300 Subject: [PATCH 01/53] Bump for 8.4.7-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index 0f8bd8f3d656a..f629af3a622a1 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.4.6 +?? ??? ????, PHP 8.4.7 + + +27 Mar 2025, PHP 8.4.6RC1 - BCMath: . Fixed pointer subtraction for scale. (SakiTakamachi) diff --git a/Zend/zend.h b/Zend/zend.h index c787b54940d92..2e5fb5d0ef091 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.6-dev" +#define ZEND_VERSION "4.4.7-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 63a30a9def502..0e12d55b00963 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.4.6-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.7-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index e46868a3abd88..414e0e8f5a68a 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 -#define PHP_RELEASE_VERSION 6 +#define PHP_RELEASE_VERSION 7 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.6-dev" -#define PHP_VERSION_ID 80406 +#define PHP_VERSION "8.4.7-dev" +#define PHP_VERSION_ID 80407 From b57f425cfe20a11003253427424cc0517483550b Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 25 Mar 2025 22:09:16 +0100 Subject: [PATCH 02/53] PHP 8.3 is now for PHP 8.3.21-dev --- NEWS | 5 ++++- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/NEWS b/NEWS index caad04f9d0895..22f3d75974629 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| -?? ??? ????, PHP 8.3.20 +?? ??? ????, PHP 8.3.21 + + +10 Apr 2025, PHP 8.3.20 - Core: . Fixed bug GH-17961 (use-after-free during dl()'ed module class destruction). diff --git a/Zend/zend.h b/Zend/zend.h index 927088ea18706..139e6a70e15dd 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.3.20-dev" +#define ZEND_VERSION "4.3.21-dev" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index a94d4958492fd..d721458a73eb2 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.3.20-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.3.21-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 3dee6de933251..eb91f434188c6 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -2,7 +2,7 @@ /* edit configure.ac to change version number */ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 20 +#define PHP_RELEASE_VERSION 21 #define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.3.20-dev" -#define PHP_VERSION_ID 80320 +#define PHP_VERSION "8.3.21-dev" +#define PHP_VERSION_ID 80321 From 9c6fe6b0ffbd21bf8580ce00114f83b996577ffe Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 26 Mar 2025 12:47:57 +0000 Subject: [PATCH 03/53] Fix GH-18148: pg_copy_from() wrong \n offset check. Close GH-18149 --- NEWS | 4 ++++ ext/pgsql/pgsql.c | 6 ++++-- ext/pgsql/tests/gh18148.phpt | 24 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 ext/pgsql/tests/gh18148.phpt diff --git a/NEWS b/NEWS index f629af3a622a1..d747ba1b07b93 100644 --- a/NEWS +++ b/NEWS @@ -75,6 +75,10 @@ PHP NEWS - PDO: . Fix memory leak when destroying PDORow. (nielsdos) +- PGSQL: + . Fixed bug GH-18148 (pg_copy_from() regression with explicit \n terminator + due to wrong offset check). (David Carlier) + - Standard: . Fix memory leaks in array_any() / array_all(). (nielsdos) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 4b03bfc3c9df4..c0d4a5117aefc 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -3451,11 +3451,13 @@ PHP_FUNCTION(pg_copy_from) if (UNEXPECTED(!tmp)) { return; } - zend_string *zquery = zend_string_alloc(ZSTR_LEN(tmp) + 1, false); + // we give allocation room for a potential command line `\n` terminator addition + zend_string *zquery = zend_string_alloc(ZSTR_LEN(tmp) + 2, false); memcpy(ZSTR_VAL(zquery), ZSTR_VAL(tmp), ZSTR_LEN(tmp) + 1); ZSTR_LEN(zquery) = ZSTR_LEN(tmp); - if (ZSTR_LEN(tmp) > 0 && ZSTR_VAL(zquery)[ZSTR_LEN(tmp)] != '\n') { + if (ZSTR_LEN(tmp) > 0 && ZSTR_VAL(zquery)[ZSTR_LEN(tmp) - 1] != '\n') { ZSTR_VAL(zquery)[ZSTR_LEN(tmp)] = '\n'; + ZSTR_VAL(zquery)[ZSTR_LEN(tmp) + 1] = '\0'; ZSTR_LEN(zquery) ++; } if (PQputCopyData(pgsql, ZSTR_VAL(zquery), ZSTR_LEN(zquery)) != 1) { diff --git a/ext/pgsql/tests/gh18148.phpt b/ext/pgsql/tests/gh18148.phpt new file mode 100644 index 0000000000000..6cc2a2542086f --- /dev/null +++ b/ext/pgsql/tests/gh18148.phpt @@ -0,0 +1,24 @@ +--TEST-- +Fix GH-18148 pg_copy_from() command position offset when giving explicit \n terminator +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) From f994c2f1fa8f3fdca303e8efb1e0d7530d1dcd04 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 26 Mar 2025 19:22:15 +0100 Subject: [PATCH 04/53] Implement benchmark diff commit fallback When the base commit is not benchmarked (yet), iterate first parents of the commit until we find one that is. Fixes GH-18094 Closes GH-18152 --- benchmark/generate_diff.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/benchmark/generate_diff.php b/benchmark/generate_diff.php index b62f55c06d9e4..3a1e0d7b1370f 100644 --- a/benchmark/generate_diff.php +++ b/benchmark/generate_diff.php @@ -10,6 +10,7 @@ function main(?string $headCommitHash, ?string $baseCommitHash) { $repo = __DIR__ . '/repos/data'; cloneRepo($repo, 'git@github.com:php/benchmarking-data.git'); + $baseCommitHash = find_benchmarked_commit_hash($repo, $baseCommitHash); $headSummaryFile = $repo . '/' . substr($headCommitHash, 0, 2) . '/' . $headCommitHash . '/summary.json'; $baseSummaryFile = $repo . '/' . substr($baseCommitHash, 0, 2) . '/' . $baseCommitHash . '/summary.json'; if (!file_exists($headSummaryFile)) { @@ -60,6 +61,24 @@ function formatDiff(?int $baseInstructions, int $headInstructions): string { return sprintf('%.2f%%', $instructionDiff / $baseInstructions * 100); } +function find_benchmarked_commit_hash(string $repo, string $commitHash): ?string { + $repeat = 10; + + while (true) { + if ($repeat-- <= 0) { + fwrite(STDERR, "Count not find benchmarked commit hash\n"); + exit(1); + } + $summaryFile = $repo . '/' . substr($commitHash, 0, 2) . '/' . $commitHash . '/summary.json'; + if (file_exists($summaryFile)) { + break; + } + $commitHash = trim(runCommand(['git', 'rev-parse', $commitHash . '^'], dirname(__DIR__))->stdout); + } + + return $commitHash; +} + $headCommitHash = $argv[1] ?? null; $baseCommitHash = $argv[2] ?? null; $output = main($headCommitHash, $baseCommitHash); From 272f7f75e2d4ff25d4dc00b0102c39fdb4f9c573 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 5 Feb 2025 22:13:25 +0100 Subject: [PATCH 05/53] Fix infinite recursion on deprecated attribute evaluation Fixes GH-17711 Fixes GH-18022 Closes GH-17712 --- NEWS | 3 ++ .../deprecated/class_constants/gh17711.phpt | 28 +++++++++++++++++++ Zend/zend_API.c | 2 +- Zend/zend_compile.c | 4 +++ Zend/zend_constants.c | 4 ++- Zend/zend_constants.h | 11 ++++++++ Zend/zend_vm_def.h | 4 ++- Zend/zend_vm_execute.h | 24 ++++++++++++---- ext/opcache/ZendAccelerator.c | 5 ++++ 9 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 Zend/tests/attributes/deprecated/class_constants/gh17711.phpt diff --git a/NEWS b/NEWS index d747ba1b07b93..1eb60d4b67b5b 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.4.7 +- Core: + . Fixed bug GH-17711 and GH-18022 (Infinite recursion on deprecated attribute + evaluation). (ilutov) 27 Mar 2025, PHP 8.4.6RC1 diff --git a/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt b/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt new file mode 100644 index 0000000000000..abec209343ab3 --- /dev/null +++ b/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-17711: Infinite recursion through deprecated class constants self-referencing through deprecation message +--FILE-- + +--EXPECTF-- +Deprecated: Constant C::C is deprecated, Message in %s on line %d +string(7) "Message" + +Deprecated: Constant D::C is deprecated, test in %s on line %d +string(4) "test" diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5aac3c1f7d77c..6e254561d3745 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1439,7 +1439,7 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_ ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) { if (c->ce == class_type) { - if (Z_TYPE(c->value) == IS_CONSTANT_AST) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST || (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) { new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); memcpy(new_c, c, sizeof(zend_class_constant)); c = new_c; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 832dedc421042..195c65d504374 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8822,6 +8822,10 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as if (deprecated) { ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED; + /* For deprecated constants, we need to flag the zval for recursion + * detection. Make sure the zval is separated out of shm. */ + ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; + ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } } } diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index d453b8bb73717..2145cb1915354 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -353,8 +353,10 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * } if (UNEXPECTED(ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) { - if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { + if ((flags & ZEND_FETCH_CLASS_SILENT) == 0 && !CONST_IS_RECURSIVE(c)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { goto failure; } diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index 6f0710c0ce63e..bd759c2891500 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -27,6 +27,17 @@ #define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */ #define CONST_DEPRECATED (1<<2) /* Deprecated */ #define CONST_OWNED (1<<3) /* constant should be destroyed together with class */ +#define CONST_RECURSIVE (1<<4) /* Recursion protection for constant evaluation */ + +#define CONST_IS_RECURSIVE(c) (Z_CONSTANT_FLAGS((c)->value) & CONST_RECURSIVE) +#define CONST_PROTECT_RECURSION(c) \ + do { \ + Z_CONSTANT_FLAGS((c)->value) |= CONST_RECURSIVE; \ + } while (0) +#define CONST_UNPROTECT_RECURSION(c) \ + do { \ + Z_CONSTANT_FLAGS((c)->value) &= ~CONST_RECURSIVE; \ + } while (0) #define PHP_USER_CONSTANT 0x7fffff /* a constant defined in user space */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index a4fa8063a5bd4..37e0e0fb43d95 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6094,8 +6094,10 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 2f6f0af2d872c..5270fc841cd8d 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7615,8 +7615,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -8775,8 +8777,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -25874,8 +25878,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -26443,8 +26449,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35282,8 +35290,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35641,8 +35651,10 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated)) { + if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { + CONST_PROTECT_RECURSION(c); zend_deprecated_class_constant(c, constant_name); + CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 459449d85f23c..f71f43b330ffa 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3800,6 +3800,11 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + /* For deprecated constants, we need to flag the zval for recursion + * detection. Make sure the zval is separated out of shm. */ + if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) { + ok = false; + } if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) { was_changed = changed = true; } else { From 2197a490f77d018be1517913a54d24029745682c Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Wed, 26 Mar 2025 22:35:21 +0100 Subject: [PATCH 06/53] Fix GH-18145: basic_globals_ctor initialization This resets all basic globals during ctor and just modifies the ones with a special value. It also switches to using basic_globals_p which what should be used in this context. Closes GH-18156 --- NEWS | 4 ++++ ext/standard/basic_functions.c | 29 +++++++---------------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/NEWS b/NEWS index 22f3d75974629..4b981f7bdd953 100644 --- a/NEWS +++ b/NEWS @@ -55,6 +55,10 @@ PHP NEWS . Fixed bug GH-18018 (RC1 data returned from offsetGet causes UAF in ArrayObject). (nielsdos) +- Standard: + . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). + (Jakub Zelenka) + - Treewide: . Fixed bug GH-17736 (Assertion failure zend_reference_destroy()). (nielsdos) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 36cf727b82a24..99d92af167f0e 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -215,31 +215,16 @@ static void php_putenv_destructor(zval *zv) /* {{{ */ static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */ { - BG(umask) = -1; - BG(user_tick_functions) = NULL; - BG(user_filter_map) = NULL; - BG(serialize_lock) = 0; - - memset(&BG(serialize), 0, sizeof(BG(serialize))); - memset(&BG(unserialize), 0, sizeof(BG(unserialize))); - - memset(&BG(url_adapt_session_ex), 0, sizeof(BG(url_adapt_session_ex))); - memset(&BG(url_adapt_output_ex), 0, sizeof(BG(url_adapt_output_ex))); + memset(basic_globals_p, 0, sizeof(php_basic_globals)); - BG(url_adapt_session_ex).type = 1; - BG(url_adapt_output_ex).type = 0; + basic_globals_p->umask = -1; + basic_globals_p->url_adapt_session_ex.type = 1; - zend_hash_init(&BG(url_adapt_session_hosts_ht), 0, NULL, NULL, 1); - zend_hash_init(&BG(url_adapt_output_hosts_ht), 0, NULL, NULL, 1); - -#if defined(_REENTRANT) - memset(&BG(mblen_state), 0, sizeof(BG(mblen_state))); -#endif - - BG(page_uid) = -1; - BG(page_gid) = -1; + zend_hash_init(&basic_globals_p->url_adapt_session_hosts_ht, 0, NULL, NULL, 1); + zend_hash_init(&basic_globals_p->url_adapt_output_hosts_ht, 0, NULL, NULL, 1); - BG(syslog_device) = NULL; + basic_globals_p->page_uid = -1; + basic_globals_p->page_gid = -1; } /* }}} */ From fe4930612a2f116f229f743a4723bbc735637663 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Thu, 27 Mar 2025 11:53:24 +0100 Subject: [PATCH 07/53] [ci skip] Fix NEWS --- NEWS | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 4b981f7bdd953..bee2ce0e09276 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,9 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.21 +- Standard: + . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). + (Jakub Zelenka) 10 Apr 2025, PHP 8.3.20 @@ -55,10 +58,6 @@ PHP NEWS . Fixed bug GH-18018 (RC1 data returned from offsetGet causes UAF in ArrayObject). (nielsdos) -- Standard: - . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). - (Jakub Zelenka) - - Treewide: . Fixed bug GH-17736 (Assertion failure zend_reference_destroy()). (nielsdos) From 26f5009e91eee02fd46d757df86f02b3ebcdca7f Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Thu, 13 Mar 2025 12:50:01 +0100 Subject: [PATCH 08/53] Fix lazy proxy calling magic methods twice Fixes GH-18038 Closes GH-18039 --- NEWS | 1 + Zend/tests/lazy_objects/gh18038-001.phpt | 29 +++++++ Zend/tests/lazy_objects/gh18038-002.phpt | 38 +++++++++ Zend/tests/lazy_objects/gh18038-003.phpt | 30 ++++++++ Zend/tests/lazy_objects/gh18038-004.phpt | 45 +++++++++++ Zend/tests/lazy_objects/gh18038-005.phpt | 28 +++++++ Zend/tests/lazy_objects/gh18038-006.phpt | 37 +++++++++ Zend/tests/lazy_objects/gh18038-007.phpt | 41 ++++++++++ Zend/tests/lazy_objects/gh18038-008.phpt | 28 +++++++ Zend/tests/lazy_objects/gh18038-009.phpt | 41 ++++++++++ Zend/tests/lazy_objects/gh18038-010.phpt | 35 +++++++++ Zend/tests/lazy_objects/gh18038-011.phpt | 45 +++++++++++ Zend/tests/lazy_objects/gh18038-012.phpt | 28 +++++++ Zend/zend_object_handlers.c | 98 +++++++++++++++++++----- 14 files changed, 503 insertions(+), 21 deletions(-) create mode 100644 Zend/tests/lazy_objects/gh18038-001.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-002.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-003.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-004.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-005.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-006.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-007.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-008.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-009.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-010.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-011.phpt create mode 100644 Zend/tests/lazy_objects/gh18038-012.phpt diff --git a/NEWS b/NEWS index b2cea7cd533a3..e0916d904edeb 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ PHP NEWS - Core: . Fixed bug GH-17711 and GH-18022 (Infinite recursion on deprecated attribute evaluation). (ilutov) + . Fixed bug GH-18038 (Lazy proxy calls magic methods twice). (Arnaud) - Standard: . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). diff --git a/Zend/tests/lazy_objects/gh18038-001.phpt b/Zend/tests/lazy_objects/gh18038-001.phpt new file mode 100644 index 0000000000000..88e453c4a251f --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-001.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-18038 001: Lazy proxy calls magic methods twice +--FILE-- +$name = $value * 2; + } +} + +$rc = new ReflectionClass(C::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new C; +}); + +$obj->prop = 1; +var_dump($obj->prop); + +?> +--EXPECT-- +string(8) "C::__set" +init +int(2) diff --git a/Zend/tests/lazy_objects/gh18038-002.phpt b/Zend/tests/lazy_objects/gh18038-002.phpt new file mode 100644 index 0000000000000..4c12f21de8115 --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-002.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-18038 002: Lazy proxy calls magic methods twice +--FILE-- +$name = $value * 2; + unset($this->$name); + $this->$name = $value * 2; + } +} + +#[AllowDynamicProperties] +class Proxy extends RealInstance { +} + +$rc = new ReflectionClass(Proxy::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new RealInstance; +}); + +$real = $rc->initializeLazyObject($obj); +$real->prop = 1; +var_dump($obj->prop); + +?> +--EXPECT-- +init +string(19) "RealInstance::__set" +string(12) "Proxy::__set" +int(2) diff --git a/Zend/tests/lazy_objects/gh18038-003.phpt b/Zend/tests/lazy_objects/gh18038-003.phpt new file mode 100644 index 0000000000000..1db0588a70cfa --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-003.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-18038 003: Lazy proxy calls magic methods twice +--FILE-- +$name; + } +} + +$rc = new ReflectionClass(C::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new C; +}); + +var_dump($obj->prop); + +?> +--EXPECTF-- +string(8) "C::__get" +init + +Warning: Undefined property: C::$prop in %s on line %d +NULL diff --git a/Zend/tests/lazy_objects/gh18038-004.phpt b/Zend/tests/lazy_objects/gh18038-004.phpt new file mode 100644 index 0000000000000..8810efb6bec2e --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-004.phpt @@ -0,0 +1,45 @@ +--TEST-- +GH-18038 004: Lazy proxy calls magic methods twice +--FILE-- +$name); + return $this->$name; + } +} + +#[AllowDynamicProperties] +class Proxy extends RealInstance { + public function __get($name) { + var_dump(get_class($this)."::".__FUNCTION__); + return $this->$name; + } +} + +$rc = new ReflectionClass(Proxy::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new RealInstance; +}); + +$real = $rc->initializeLazyObject($obj); +var_dump($real->prop); + +?> +--EXPECTF-- +init +string(19) "RealInstance::__get" +string(12) "Proxy::__get" + +Warning: Undefined property: RealInstance::$prop in %s on line %d +NULL + +Warning: Undefined property: RealInstance::$prop in %s on line %d +NULL diff --git a/Zend/tests/lazy_objects/gh18038-005.phpt b/Zend/tests/lazy_objects/gh18038-005.phpt new file mode 100644 index 0000000000000..b728d45253daf --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-005.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-18038 005: Lazy proxy calls magic methods twice +--FILE-- +$name['']); + } +} + +$rc = new ReflectionClass(C::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new C; +}); + +var_dump(isset($obj->prop[''])); + +?> +--EXPECT-- +string(10) "C::__isset" +init +bool(false) diff --git a/Zend/tests/lazy_objects/gh18038-006.phpt b/Zend/tests/lazy_objects/gh18038-006.phpt new file mode 100644 index 0000000000000..256f0be8e202b --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-006.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-18038 006: Lazy proxy calls magic methods twice +--FILE-- +$name['']); + } + public function __get($name) { + var_dump(__METHOD__); + return $this->$name['']; + } +} + +$rc = new ReflectionClass(C::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new C; +}); + +var_dump(isset($obj->prop[''])); + +?> +--EXPECTF-- +string(10) "C::__isset" +string(8) "C::__get" +init + +Warning: Undefined property: C::$prop in %s on line %d + +Warning: Trying to access array offset on null in %s on line %d +bool(false) diff --git a/Zend/tests/lazy_objects/gh18038-007.phpt b/Zend/tests/lazy_objects/gh18038-007.phpt new file mode 100644 index 0000000000000..9925190a19801 --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-007.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-18038 007: Lazy proxy calls magic methods twice +--FILE-- +$name[''])); + return isset($this->$name['']); + } +} + +#[AllowDynamicProperties] +class Proxy extends RealInstance { + public function __isset($name) { + var_dump(get_class($this)."::".__FUNCTION__); + return isset($this->$name['']); + } +} + +$rc = new ReflectionClass(Proxy::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new RealInstance; +}); + +$real = $rc->initializeLazyObject($obj); +var_dump(isset($real->prop[''])); + +?> +--EXPECT-- +init +string(21) "RealInstance::__isset" +string(14) "Proxy::__isset" +bool(false) +bool(false) diff --git a/Zend/tests/lazy_objects/gh18038-008.phpt b/Zend/tests/lazy_objects/gh18038-008.phpt new file mode 100644 index 0000000000000..34d6e766163de --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-008.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-18038 008: Lazy proxy calls magic methods twice +--FILE-- +$name); + } +} + +$rc = new ReflectionClass(C::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new C; +}); + +var_dump(isset($obj->prop)); + +?> +--EXPECT-- +string(10) "C::__isset" +init +bool(false) diff --git a/Zend/tests/lazy_objects/gh18038-009.phpt b/Zend/tests/lazy_objects/gh18038-009.phpt new file mode 100644 index 0000000000000..3c165a71ccffe --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-009.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-18038 009: Lazy proxy calls magic methods twice +--FILE-- +$name)); + return isset($this->$name); + } +} + +#[AllowDynamicProperties] +class Proxy extends RealInstance { + public function __isset($name) { + var_dump(get_class($this)."::".__FUNCTION__); + return isset($this->$name); + } +} + +$rc = new ReflectionClass(Proxy::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new RealInstance; +}); + +$real = $rc->initializeLazyObject($obj); +var_dump(isset($real->prop)); + +?> +--EXPECT-- +init +string(21) "RealInstance::__isset" +string(14) "Proxy::__isset" +bool(false) +bool(false) diff --git a/Zend/tests/lazy_objects/gh18038-010.phpt b/Zend/tests/lazy_objects/gh18038-010.phpt new file mode 100644 index 0000000000000..584c18580a210 --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-010.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-18038 010: Lazy proxy calls magic methods twice +--FILE-- +$name); + } +} + +$rc = new ReflectionClass(C::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new C; +}); + +unset($obj->prop); +var_dump($obj); + +?> +--EXPECTF-- +string(10) "C::__unset" +init +lazy proxy object(C)#%d (1) { + ["instance"]=> + object(C)#%d (1) { + ["_"]=> + NULL + } +} diff --git a/Zend/tests/lazy_objects/gh18038-011.phpt b/Zend/tests/lazy_objects/gh18038-011.phpt new file mode 100644 index 0000000000000..b356f9e4f384d --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-011.phpt @@ -0,0 +1,45 @@ +--TEST-- +GH-18038 011: Lazy proxy calls magic methods twice +--FILE-- +$name); + } +} + +#[AllowDynamicProperties] +class Proxy extends RealInstance { + public function __isset($name) { + var_dump(get_class($this)."::".__FUNCTION__); + unset($this->$name); + } +} + +$rc = new ReflectionClass(Proxy::class); + +$obj = $rc->newLazyProxy(function () { + echo "init\n"; + return new RealInstance; +}); + +$real = $rc->initializeLazyObject($obj); +unset($real->prop); +var_dump($obj); + +?> +--EXPECTF-- +init +string(21) "RealInstance::__unset" +lazy proxy object(Proxy)#%d (1) { + ["instance"]=> + object(RealInstance)#%d (1) { + ["_"]=> + NULL + } +} diff --git a/Zend/tests/lazy_objects/gh18038-012.phpt b/Zend/tests/lazy_objects/gh18038-012.phpt new file mode 100644 index 0000000000000..5bc83333d81c7 --- /dev/null +++ b/Zend/tests/lazy_objects/gh18038-012.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-18038 012: Lazy proxy calls magic methods twice +--FILE-- +$name = $value * 2; + } +} + +$rc = new ReflectionClass(C::class); + +$obj = $rc->newLazyGhost(function () { + echo "init\n"; +}); + +$obj->prop = 1; +var_dump($obj->prop); + +?> +--EXPECT-- +string(8) "C::__set" +init +int(2) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index a4dba771e3ef5..4aea3501cf269 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -946,6 +946,18 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int goto exit; } + if (UNEXPECTED(guard)) { + uint32_t guard_type = (type == BP_VAR_IS) && zobj->ce->__isset + ? IN_ISSET : IN_GET; + guard = zend_get_property_guard(zobj, name); + if (!((*guard) & guard_type)) { + (*guard) |= guard_type; + retval = zend_std_read_property(zobj, name, type, cache_slot, rv); + (*guard) &= ~guard_type; + return retval; + } + } + return zend_std_read_property(zobj, name, type, cache_slot, rv); } } @@ -970,6 +982,43 @@ static zend_always_inline bool property_uses_strict_types(void) { && ZEND_CALL_USES_STRICT_TYPES(EG(current_execute_data)); } +static zval *forward_write_to_lazy_object(zend_object *zobj, + zend_string *name, zval *value, void **cache_slot, bool guarded) +{ + zval *variable_ptr; + + /* backup value as it may change during initialization */ + zval backup; + ZVAL_COPY(&backup, value); + + zend_object *instance = zend_lazy_object_init(zobj); + if (UNEXPECTED(!instance)) { + zval_ptr_dtor(&backup); + return &EG(error_zval); + } + + if (UNEXPECTED(guarded)) { + uint32_t *guard = zend_get_property_guard(instance, name); + if (!((*guard) & IN_SET)) { + (*guard) |= IN_SET; + variable_ptr = zend_std_write_property(instance, name, &backup, cache_slot); + (*guard) &= ~IN_SET; + goto exit; + } + } + + variable_ptr = zend_std_write_property(instance, name, &backup, cache_slot); + +exit: + zval_ptr_dtor(&backup); + + if (variable_ptr == &backup) { + variable_ptr = value; + } + + return variable_ptr; +} + ZEND_API zval *zend_std_write_property(zend_object *zobj, zend_string *name, zval *value, void **cache_slot) /* {{{ */ { zval *variable_ptr, tmp; @@ -1151,7 +1200,8 @@ found:; variable_ptr = value; } else if (EXPECTED(!IS_WRONG_PROPERTY_OFFSET(property_offset))) { if (UNEXPECTED(zend_lazy_object_must_init(zobj))) { - goto lazy_init; + return forward_write_to_lazy_object(zobj, name, value, + cache_slot, /* guarded */ true); } goto write_std_property; @@ -1198,26 +1248,9 @@ found:; exit: return variable_ptr; -lazy_init:; - /* backup value as it may change during initialization */ - zval backup; - ZVAL_COPY(&backup, value); - - zobj = zend_lazy_object_init(zobj); - if (UNEXPECTED(!zobj)) { - variable_ptr = &EG(error_zval); - zval_ptr_dtor(&backup); - goto exit; - } - - variable_ptr = zend_std_write_property(zobj, name, &backup, cache_slot); - zval_ptr_dtor(&backup); - - if (variable_ptr == &backup) { - variable_ptr = value; - } - - return variable_ptr; +lazy_init: + return forward_write_to_lazy_object(zobj, name, value, cache_slot, + /* guarded */ false); } /* }}} */ @@ -1538,6 +1571,17 @@ ZEND_API void zend_std_unset_property(zend_object *zobj, zend_string *name, void if (!zobj) { return; } + + if (UNEXPECTED(guard)) { + guard = zend_get_property_guard(zobj, name); + if (!((*guard) & IN_UNSET)) { + (*guard) |= IN_UNSET; + zend_std_unset_property(zobj, name, cache_slot); + (*guard) &= ~IN_UNSET; + return; + } + } + zend_std_unset_property(zobj, name, cache_slot); return; } @@ -2323,6 +2367,8 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has } (*guard) &= ~IN_ISSET; OBJ_RELEASE(zobj); + } else { + goto lazy_init; } } @@ -2338,6 +2384,16 @@ ZEND_API int zend_std_has_property(zend_object *zobj, zend_string *name, int has goto exit; } + if (UNEXPECTED(zobj->ce->__isset)) { + uint32_t *guard = zend_get_property_guard(zobj, name); + if (!((*guard) & IN_ISSET)) { + (*guard) |= IN_ISSET; + result = zend_std_has_property(zobj, name, has_set_exists, cache_slot); + (*guard) &= ~IN_ISSET; + return result; + } + } + return zend_std_has_property(zobj, name, has_set_exists, cache_slot); } } From 9b96ea1a99cc0eaed43ee44c8a8f68b2f6a7786a Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 25 Mar 2025 11:51:49 +0100 Subject: [PATCH 09/53] GDB: Import gdb.printing gdb.printing is not imported by default since version 16, for some reason --- main/debug_gdb_scripts.c | 1 + scripts/gdb/php_gdb.py | 1 + 2 files changed, 2 insertions(+) diff --git a/main/debug_gdb_scripts.c b/main/debug_gdb_scripts.c index de7d0c5c92df8..384a1c8b46ee8 100644 --- a/main/debug_gdb_scripts.c +++ b/main/debug_gdb_scripts.c @@ -698,6 +698,7 @@ asm( ".ascii \"\\\"\\\"\\\"\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"import gdb\\n\"\n" + ".ascii \"import gdb.printing\\n\"\n" ".ascii \"import re\\n\"\n" ".ascii \"\\n\"\n" ".ascii \"pp_set = gdb.printing.RegexpCollectionPrettyPrinter(\\\"php\\\")\\n\"\n" diff --git a/scripts/gdb/php_gdb.py b/scripts/gdb/php_gdb.py index 7fef2ad1f49c8..3e40ef8a3b411 100644 --- a/scripts/gdb/php_gdb.py +++ b/scripts/gdb/php_gdb.py @@ -28,6 +28,7 @@ """ import gdb +import gdb.printing import re pp_set = gdb.printing.RegexpCollectionPrettyPrinter("php") From 2b9840894da4a178585c74ad3b2bfb119890d902 Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Thu, 27 Mar 2025 22:24:46 +0300 Subject: [PATCH 10/53] Update IR IR commit: dd228777b67334d8ed51de44f427d66d4ac99c08 --- ext/opcache/jit/ir/ir_fold.h | 35 +++++++++++++++- ext/opcache/jit/ir/ir_sccp.c | 57 +++++++++++++------------ ext/opcache/jit/ir/ir_x86.dasc | 76 ++++++++++++++++++++++++++++++---- 3 files changed, 130 insertions(+), 38 deletions(-) diff --git a/ext/opcache/jit/ir/ir_fold.h b/ext/opcache/jit/ir/ir_fold.h index c7745a8c68723..78f3ca0c01e5f 100644 --- a/ext/opcache/jit/ir/ir_fold.h +++ b/ext/opcache/jit/ir/ir_fold.h @@ -1859,8 +1859,39 @@ IR_FOLD(SUB(ADD, ADD)) } // IR_FOLD(SUB(NEG, CONST)) TODO: -a - b => -b - a -// IR_FOLD(MUL(NEG, CONST)) TODO: -a * b => a * -b -// IR_FOLD(DIV(NEG, CONST)) TODO: -a / b => a / -b + +IR_FOLD(MUL(NEG, C_I8)) +IR_FOLD(MUL(NEG, C_I16)) +IR_FOLD(MUL(NEG, C_I32)) +IR_FOLD(MUL(NEG, C_I64)) +IR_FOLD(DIV(NEG, C_I8)) +IR_FOLD(DIV(NEG, C_I16)) +IR_FOLD(DIV(NEG, C_I32)) +IR_FOLD(DIV(NEG, C_I64)) +{ + op1 = op1_insn->op1; + val.i64 = -op2_insn->val.i64; + op2 = ir_const(ctx, val, op2_insn->type); + IR_FOLD_RESTART; +} + +IR_FOLD(MUL(NEG, C_FLOAT)) +IR_FOLD(DIV(NEG, C_FLOAT)) +{ + op1 = op1_insn->op1; + val.f = -op2_insn->val.f; + op2 = ir_const(ctx, val, op2_insn->type); + IR_FOLD_RESTART; +} + +IR_FOLD(MUL(NEG, C_DOUBLE)) +IR_FOLD(DIV(NEG, C_DOUBLE)) +{ + op1 = op1_insn->op1; + val.d = -op2_insn->val.d; + op2 = ir_const(ctx, val, op2_insn->type); + IR_FOLD_RESTART; +} IR_FOLD(MUL(_, C_U8)) IR_FOLD(MUL(_, C_U16)) diff --git a/ext/opcache/jit/ir/ir_sccp.c b/ext/opcache/jit/ir/ir_sccp.c index af039aaef829b..8480861f91fe7 100644 --- a/ext/opcache/jit/ir/ir_sccp.c +++ b/ext/opcache/jit/ir/ir_sccp.c @@ -1517,7 +1517,7 @@ static bool ir_may_promote_f2d(ir_ctx *ctx, ir_ref ref) return 0; } -static ir_ref ir_promote_d2f(ir_ctx *ctx, ir_ref ref, ir_ref use) +static ir_ref ir_promote_d2f(ir_ctx *ctx, ir_ref ref, ir_ref use, ir_bitqueue *worklist) { ir_insn *insn = &ctx->ir_base[ref]; uint32_t count; @@ -1526,6 +1526,7 @@ static ir_ref ir_promote_d2f(ir_ctx *ctx, ir_ref ref, ir_ref use) if (IR_IS_CONST_REF(ref)) { return ir_const_float(ctx, (float)insn->val.d); } else { + ir_bitqueue_add(worklist, ref); switch (insn->op) { case IR_FP2FP: count = ctx->use_lists[ref].count; @@ -1555,7 +1556,7 @@ static ir_ref ir_promote_d2f(ir_ctx *ctx, ir_ref ref, ir_ref use) // return ref; case IR_NEG: case IR_ABS: - insn->op1 = ir_promote_d2f(ctx, insn->op1, ref); + insn->op1 = ir_promote_d2f(ctx, insn->op1, ref, worklist); insn->type = IR_FLOAT; return ref; case IR_ADD: @@ -1565,10 +1566,10 @@ static ir_ref ir_promote_d2f(ir_ctx *ctx, ir_ref ref, ir_ref use) case IR_MIN: case IR_MAX: if (insn->op1 == insn->op2) { - insn->op2 = insn->op1 = ir_promote_d2f(ctx, insn->op1, ref); + insn->op2 = insn->op1 = ir_promote_d2f(ctx, insn->op1, ref, worklist); } else { - insn->op1 = ir_promote_d2f(ctx, insn->op1, ref); - insn->op2 = ir_promote_d2f(ctx, insn->op2, ref); + insn->op1 = ir_promote_d2f(ctx, insn->op1, ref, worklist); + insn->op2 = ir_promote_d2f(ctx, insn->op2, ref, worklist); } insn->type = IR_FLOAT; return ref; @@ -1580,7 +1581,7 @@ static ir_ref ir_promote_d2f(ir_ctx *ctx, ir_ref ref, ir_ref use) return ref; } -static ir_ref ir_promote_f2d(ir_ctx *ctx, ir_ref ref, ir_ref use) +static ir_ref ir_promote_f2d(ir_ctx *ctx, ir_ref ref, ir_ref use, ir_bitqueue *worklist) { ir_insn *insn = &ctx->ir_base[ref]; uint32_t count; @@ -1590,6 +1591,7 @@ static ir_ref ir_promote_f2d(ir_ctx *ctx, ir_ref ref, ir_ref use) if (IR_IS_CONST_REF(ref)) { return ir_const_double(ctx, (double)insn->val.f); } else { + ir_bitqueue_add(worklist, ref); switch (insn->op) { case IR_FP2FP: count = ctx->use_lists[ref].count; @@ -1628,7 +1630,7 @@ static ir_ref ir_promote_f2d(ir_ctx *ctx, ir_ref ref, ir_ref use) return ref; case IR_NEG: case IR_ABS: - insn->op1 = ir_promote_f2d(ctx, insn->op1, ref); + insn->op1 = ir_promote_f2d(ctx, insn->op1, ref, worklist); insn->type = IR_DOUBLE; return ref; case IR_ADD: @@ -1638,10 +1640,10 @@ static ir_ref ir_promote_f2d(ir_ctx *ctx, ir_ref ref, ir_ref use) case IR_MIN: case IR_MAX: if (insn->op1 == insn->op2) { - insn->op2 = insn->op1 = ir_promote_f2d(ctx, insn->op1, ref); + insn->op2 = insn->op1 = ir_promote_f2d(ctx, insn->op1, ref, worklist); } else { - insn->op1 = ir_promote_f2d(ctx, insn->op1, ref); - insn->op2 = ir_promote_f2d(ctx, insn->op2, ref); + insn->op1 = ir_promote_f2d(ctx, insn->op1, ref, worklist); + insn->op2 = ir_promote_f2d(ctx, insn->op2, ref, worklist); } insn->type = IR_DOUBLE; return ref; @@ -1707,7 +1709,7 @@ static bool ir_may_promote_trunc(ir_ctx *ctx, ir_type type, ir_ref ref) } } } - for (p = insn->ops + 1, n = insn->inputs_count - 1; n > 0; p++, n--) { + for (p = insn->ops + 2, n = insn->inputs_count - 1; n > 0; p++, n--) { input = *p; if (input != ref) { if (!ir_may_promote_trunc(ctx, type, input)) { @@ -1723,7 +1725,7 @@ static bool ir_may_promote_trunc(ir_ctx *ctx, ir_type type, ir_ref ref) return 0; } -static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use) +static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use, ir_bitqueue *worklist) { ir_insn *insn = &ctx->ir_base[ref]; uint32_t count; @@ -1732,6 +1734,7 @@ static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use) if (IR_IS_CONST_REF(ref)) { return ir_const(ctx, insn->val, type); } else { + ir_bitqueue_add(worklist, ref); switch (insn->op) { case IR_ZEXT: case IR_SEXT: @@ -1776,7 +1779,7 @@ static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use) case IR_NEG: case IR_ABS: case IR_NOT: - insn->op1 = ir_promote_i2i(ctx, type, insn->op1, ref); + insn->op1 = ir_promote_i2i(ctx, type, insn->op1, ref, worklist); insn->type = type; return ref; case IR_ADD: @@ -1789,10 +1792,10 @@ static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use) case IR_XOR: case IR_SHL: if (insn->op1 == insn->op2) { - insn->op2 = insn->op1 = ir_promote_i2i(ctx, type, insn->op1, ref); + insn->op2 = insn->op1 = ir_promote_i2i(ctx, type, insn->op1, ref, worklist); } else { - insn->op1 = ir_promote_i2i(ctx, type, insn->op1, ref); - insn->op2 = ir_promote_i2i(ctx, type, insn->op2, ref); + insn->op1 = ir_promote_i2i(ctx, type, insn->op1, ref, worklist); + insn->op2 = ir_promote_i2i(ctx, type, insn->op2, ref, worklist); } insn->type = type; return ref; @@ -1804,18 +1807,18 @@ static ir_ref ir_promote_i2i(ir_ctx *ctx, ir_type type, ir_ref ref, ir_ref use) // TODO: ??? case IR_COND: if (insn->op2 == insn->op3) { - insn->op3 = insn->op2 = ir_promote_i2i(ctx, type, insn->op2, ref); + insn->op3 = insn->op2 = ir_promote_i2i(ctx, type, insn->op2, ref, worklist); } else { - insn->op2 = ir_promote_i2i(ctx, type, insn->op2, ref); - insn->op3 = ir_promote_i2i(ctx, type, insn->op3, ref); + insn->op2 = ir_promote_i2i(ctx, type, insn->op2, ref, worklist); + insn->op3 = ir_promote_i2i(ctx, type, insn->op3, ref, worklist); } insn->type = type; return ref; case IR_PHI: - for (p = insn->ops + 1, n = insn->inputs_count - 1; n > 0; p++, n--) { + for (p = insn->ops + 2, n = insn->inputs_count - 1; n > 0; p++, n--) { input = *p; if (input != ref) { - *p = ir_promote_i2i(ctx, type, input, ref); + *p = ir_promote_i2i(ctx, type, input, ref, worklist); } } insn->type = type; @@ -1906,7 +1909,7 @@ static uint32_t _ir_estimated_control(ir_ctx *ctx, ir_ref val) } IR_ASSERT(ir_op_flags[insn->op] & IR_OP_FLAG_DATA); - if (IR_OPND_KIND(ir_op_flags[insn->op], 1) & IR_OPND_CONTROL_DEP) { + if (IR_OPND_KIND(ir_op_flags[insn->op], 1) == IR_OPND_CONTROL_DEP) { return insn->op1; } @@ -3479,14 +3482,14 @@ void ir_iter_opt(ir_ctx *ctx, ir_bitqueue *worklist) case IR_FP2FP: if (insn->type == IR_FLOAT) { if (ir_may_promote_d2f(ctx, insn->op1)) { - ir_ref ref = ir_promote_d2f(ctx, insn->op1, i); + ir_ref ref = ir_promote_d2f(ctx, insn->op1, i, worklist); insn->op1 = ref; ir_iter_replace_insn(ctx, i, ref, worklist); break; } } else { if (ir_may_promote_f2d(ctx, insn->op1)) { - ir_ref ref = ir_promote_f2d(ctx, insn->op1, i); + ir_ref ref = ir_promote_f2d(ctx, insn->op1, i, worklist); insn->op1 = ref; ir_iter_replace_insn(ctx, i, ref, worklist); break; @@ -3496,17 +3499,17 @@ void ir_iter_opt(ir_ctx *ctx, ir_bitqueue *worklist) case IR_FP2INT: if (ctx->ir_base[insn->op1].type == IR_DOUBLE) { if (ir_may_promote_d2f(ctx, insn->op1)) { - insn->op1 = ir_promote_d2f(ctx, insn->op1, i); + insn->op1 = ir_promote_d2f(ctx, insn->op1, i, worklist); } } else { if (ir_may_promote_f2d(ctx, insn->op1)) { - insn->op1 = ir_promote_f2d(ctx, insn->op1, i); + insn->op1 = ir_promote_f2d(ctx, insn->op1, i, worklist); } } goto folding; case IR_TRUNC: if (ir_may_promote_trunc(ctx, insn->type, insn->op1)) { - ir_ref ref = ir_promote_i2i(ctx, insn->type, insn->op1, i); + ir_ref ref = ir_promote_i2i(ctx, insn->type, insn->op1, i, worklist); insn->op1 = ref; ir_iter_replace_insn(ctx, i, ref, worklist); break; diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index 3b6cf156ad909..ad785d3b891eb 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -1003,6 +1003,8 @@ const char *ir_reg_name(int8_t reg, ir_type type) _(LEA_SI_B) \ _(LEA_B_SI_O) \ _(LEA_SI_B_O) \ + _(LEA_SYM_O) \ + _(LEA_O_SYM) \ _(INC) \ _(DEC) \ _(MUL_PWR2) \ @@ -1065,6 +1067,9 @@ const char *ir_reg_name(int8_t reg, ir_type type) _(SSE_TRUNC) \ _(SSE_NEARBYINT) \ +#define IR_LEA_FIRST IR_LEA_OB +#define IR_LEA_LAST IR_LEA_O_SYM + #define IR_RULE_ENUM(name) IR_ ## name, #define IR_STATIC_ALLOCA (IR_SKIPPED | IR_FUSED | IR_SIMPLE | IR_ALLOCA) @@ -1584,7 +1589,7 @@ static void ir_match_fuse_addr(ir_ctx *ctx, ir_ref addr_ref) if (!rule) { ctx->rules[addr_ref] = rule = ir_match_insn(ctx, addr_ref); } - if (rule >= IR_LEA_OB && rule <= IR_LEA_SI_B_O) { + if (rule >= IR_LEA_FIRST && rule <= IR_LEA_LAST) { ir_use_list *use_list; ir_ref j; @@ -1972,6 +1977,19 @@ static uint32_t ir_match_insn(ir_ctx *ctx, ir_ref ref) if ((ctx->flags & IR_OPT_CODEGEN) && IR_IS_CONST_REF(insn->op2)) { op2_insn = &ctx->ir_base[insn->op2]; if (IR_IS_CONST_REF(insn->op1)) { + ir_insn *op1_insn = &ctx->ir_base[insn->op1]; + + if (insn->op == IR_ADD + && IR_IS_SYM_CONST(op1_insn->op) + && !IR_IS_SYM_CONST(op2_insn->op) + && IR_IS_SIGNED_32BIT((intptr_t)ir_sym_val(ctx, op1_insn) + (intptr_t)op2_insn->val.i64)) { + return IR_LEA_SYM_O; + } else if (insn->op == IR_ADD + && IR_IS_SYM_CONST(op2_insn->op) + && !IR_IS_SYM_CONST(op1_insn->op) + && IR_IS_SIGNED_32BIT((intptr_t)ir_sym_val(ctx, op2_insn) + (intptr_t)op1_insn->val.i64)) { + return IR_LEA_O_SYM; + } // const // TODO: add support for sym+offset ??? } else if (IR_IS_SYM_CONST(op2_insn->op)) { @@ -3264,7 +3282,11 @@ static void ir_emit_mov_ext(ir_ctx *ctx, ir_type type, ir_reg dst, ir_reg src) | ASM_REG_REG_OP mov, type, dst, src } else if (ir_type_size[type] == 2) { if (IR_IS_TYPE_SIGNED(type)) { - | movsx Rd(dst), Rw(src) + if (dst == IR_REG_RAX && src == IR_REG_RAX) { + | cwde + } else { + | movsx Rd(dst), Rw(src) + } } else { | movzx Rd(dst), Rw(src) } @@ -3311,8 +3333,8 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) ir_reg base_reg = IR_REG_NONE, index_reg; int32_t offset = 0, scale; - IR_ASSERT(((rule & IR_RULE_MASK) >= IR_LEA_OB && - (rule & IR_RULE_MASK) <= IR_LEA_SI_B_O) || + IR_ASSERT(((rule & IR_RULE_MASK) >= IR_LEA_FIRST && + (rule & IR_RULE_MASK) <= IR_LEA_LAST) || rule == IR_STATIC_ALLOCA); switch (rule & IR_RULE_MASK) { default: @@ -3498,6 +3520,22 @@ static ir_mem ir_fuse_addr(ir_ctx *ctx, ir_ref root, ir_ref ref) op1_insn = &ctx->ir_base[op1_insn->op1]; scale = ctx->ir_base[op1_insn->op2].val.i32; break; + case IR_LEA_SYM_O: + op1_insn = &ctx->ir_base[insn->op1]; + op2_insn = &ctx->ir_base[insn->op2]; + offset = (intptr_t)ir_sym_val(ctx, op1_insn) + (intptr_t)op2_insn->val.i64; + base_reg_ref = index_reg_ref = IR_UNUSED; + scale = 1; + offset_insn = NULL; + break; + case IR_LEA_O_SYM: + op1_insn = &ctx->ir_base[insn->op1]; + op2_insn = &ctx->ir_base[insn->op2]; + offset = (intptr_t)ir_sym_val(ctx, op2_insn) + (intptr_t)op1_insn->val.i64; + base_reg_ref = index_reg_ref = IR_UNUSED; + scale = 1; + offset_insn = NULL; + break; case IR_ALLOCA: offset = IR_SPILL_POS_TO_OFFSET(insn->op3); base_reg = (ctx->flags & IR_USE_FRAME_POINTER) ? IR_REG_FRAME_POINTER : IR_REG_STACK_POINTER; @@ -5186,7 +5224,7 @@ static void ir_emit_mul_div_mod(ir_ctx *ctx, ir_ref def, ir_insn *insn) } else if (ir_type_size[type] == 2) { | cwd } else { - | movsx ax, al + | cbw } if (op2_reg != IR_REG_NONE) { | ASM_REG_OP idiv, type, op2_reg @@ -6813,7 +6851,11 @@ static void ir_emit_sext(ir_ctx *ctx, ir_ref def, ir_insn *insn) } if (ir_type_size[src_type] == 1) { if (ir_type_size[dst_type] == 2) { - | movsx Rw(def_reg), Rb(op1_reg) + if (def_reg == IR_REG_RAX && op1_reg == IR_REG_RAX) { + | cbw + } else { + | movsx Rw(def_reg), Rb(op1_reg) + } } else if (ir_type_size[dst_type] == 4) { | movsx Rd(def_reg), Rb(op1_reg) } else { @@ -6825,7 +6867,11 @@ static void ir_emit_sext(ir_ctx *ctx, ir_ref def, ir_insn *insn) } } else if (ir_type_size[src_type] == 2) { if (ir_type_size[dst_type] == 4) { - | movsx Rd(def_reg), Rw(op1_reg) + if (def_reg == IR_REG_RAX && op1_reg == IR_REG_RAX) { + | cwde + } else { + | movsx Rd(def_reg), Rw(op1_reg) + } } else { IR_ASSERT(ir_type_size[dst_type] == 8); IR_ASSERT(sizeof(void*) == 8); @@ -6838,7 +6884,11 @@ static void ir_emit_sext(ir_ctx *ctx, ir_ref def, ir_insn *insn) IR_ASSERT(ir_type_size[dst_type] == 8); IR_ASSERT(sizeof(void*) == 8); |.if X64 - | movsxd Rq(def_reg), Rd(op1_reg) + if (def_reg == IR_REG_RAX && op1_reg == IR_REG_RAX) { + | cdqe + } else { + | movsxd Rq(def_reg), Rd(op1_reg) + } |.endif } } else if (IR_IS_CONST_REF(insn->op1)) { @@ -7203,6 +7253,8 @@ static void ir_emit_int2fp(ir_ctx *ctx, ir_ref def, ir_insn *insn) |.else || if (ir_type_size[src_type] == 1) { | movsx Rd(op1_reg), Rb(op1_reg) +|| } else if (op1_reg == IR_REG_RAX) { + | cwde || } else { | movsx Rd(op1_reg), Rw(op1_reg) || } @@ -8502,7 +8554,11 @@ static void ir_emit_switch(ir_ctx *ctx, uint32_t b, ir_ref def, ir_insn *insn) case 4: |.if X64 if (IR_IS_TYPE_SIGNED(type)) { - | movsxd Ra(op2_reg), Rd(op2_reg) + if (op2_reg == IR_REG_RAX) { + | cdqe + } else { + | movsxd Ra(op2_reg), Rd(op2_reg) + } } else { | mov Rd(op2_reg), Rd(op2_reg) } @@ -10603,6 +10659,8 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) case IR_LEA_SI_B: case IR_LEA_B_SI_O: case IR_LEA_SI_B_O: + case IR_LEA_SYM_O: + case IR_LEA_O_SYM: ir_emit_lea(ctx, i, insn->type); break; case IR_MUL_PWR2: From a1620048fb4714ea503560406bc782e7eacfb890 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 9 Mar 2025 14:04:01 +0000 Subject: [PATCH 11/53] ext/gd: imagecrop rect argument overflow fixes. ``` ext/gd/libgd/gd.c:2275:14: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int' #0 0x5d6a2103e1db in php_gd_gdImageCopy /home/dcarlier/Contribs/php-src/ext/gd/libgd/gd.c:2275 #1 0x5d6a210a2b63 in gdImageCrop /home/dcarlier/Contribs/php-src/ext/gd/libgd/gd_crop.c:57 #2 0x5d6a21018ca4 in zif_imagecrop /home/dcarlier/Contribs/php-src/ext/gd/gd.c:3575 #3 0x5d6a21e46e7a in ZEND_DO_ICALL_SPEC_RETVAL_USED_HANDLER /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:1337 #4 0x5d6a221188da in execute_ex /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:57246 #5 0x5d6a221366bd in zend_execute /home/dcarlier/Contribs/php-src/Zend/zend_vm_execute.h:61634 #6 0x5d6a21d107a6 in zend_execute_scripts /home/dcarlier/Contribs/php-src/Zend/zend.c:1895 #7 0x5d6a21a63409 in php_execute_script /home/dcarlier/Contribs/php-src/main/main.c:2529 #8 0x5d6a22516d5e in do_cli /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:966 #9 0x5d6a2251981d in main /home/dcarlier/Contribs/php-src/sapi/cli/php_cli.c:1341 #10 0x7f10d002a3b7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58 #11 0x7f10d002a47a in __libc_start_main_impl ../csu/libc-start.c:360 #12 0x5d6a20a06da4 in _start (/home/dcarlier/Contribs/php-src/sapi/cli/php+0x2806da4) (BuildId: d9a79c7e0e4872311439d7313cb3a81fe04190a2) ``` close GH-18006 --- NEWS | 4 +++ ext/gd/gd.c | 20 +++++++++++++ ext/gd/tests/imagecrop_overflow.phpt | 45 ++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 ext/gd/tests/imagecrop_overflow.phpt diff --git a/NEWS b/NEWS index bee2ce0e09276..c08d732697cb3 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.21 +- GD: + . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage + in gdImageCrop(). (David Carlier) + - Standard: . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). (Jakub Zelenka) diff --git a/ext/gd/gd.c b/ext/gd/gd.c index 2585923edcc22..ae03b602cdc5b 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -3560,6 +3560,26 @@ PHP_FUNCTION(imagecrop) RETURN_THROWS(); } + if ((rect.width > 0 && rect.x > INT_MAX - rect.width)) { + zend_argument_value_error(2, "overflow with \"x\" and \"width\" keys"); + RETURN_THROWS(); + } + + if ((rect.width < 0 && rect.x < INT_MIN - rect.width)) { + zend_argument_value_error(2, "underflow with \"x\" and \"width\" keys"); + RETURN_THROWS(); + } + + if ((rect.height > 0 && rect.y > INT_MAX - rect.height)) { + zend_argument_value_error(2, "overflow with \"y\" and \"height\" keys"); + RETURN_THROWS(); + } + + if ((rect.height < 0 && rect.y < INT_MIN - rect.height)) { + zend_argument_value_error(2, "underflow with \"y\" and \"height\" keys"); + RETURN_THROWS(); + } + im_crop = gdImageCrop(im, &rect); if (im_crop == NULL) { diff --git a/ext/gd/tests/imagecrop_overflow.phpt b/ext/gd/tests/imagecrop_overflow.phpt new file mode 100644 index 0000000000000..3331a6267168a --- /dev/null +++ b/ext/gd/tests/imagecrop_overflow.phpt @@ -0,0 +1,45 @@ +--TEST-- +imagecrop() overflows when the combo x/width or y/height is over INT_MAX or under INT_MIN. +--EXTENSIONS-- +gd +--FILE-- + 2147483647, "y" => 2147483647, "width" => 10, "height" => 10]; + +try { + imagecrop($img, $arr); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + +$arr = ["x" => -2147483648, "y" => 0, "width" => -10, "height" => 10]; + +try { + imagecrop($img, $arr); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + +$arr = ["x" => 1, "y" => 2147483647, "width" => 10, "height" => 10]; + +try { + imagecrop($img, $arr); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + +$arr = ["x" => 1, "y" => -2147483648, "width" => 10, "height" => -10]; + +try { + imagecrop($img, $arr); +} catch (\ValueError $e) { + echo $e->getMessage(); +} +?> +--EXPECT-- +imagecrop(): Argument #2 ($rectangle) overflow with "x" and "width" keys +imagecrop(): Argument #2 ($rectangle) underflow with "x" and "width" keys +imagecrop(): Argument #2 ($rectangle) overflow with "y" and "height" keys +imagecrop(): Argument #2 ($rectangle) underflow with "y" and "height" keys From c0b441f8fde516e714586d85603d8a25aea1d4e8 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sun, 30 Mar 2025 00:42:59 +0100 Subject: [PATCH 12/53] [skip ci] Fix valgrind benchmark diff output Don't print command when searching benchmarked commit, as this breaks the markdown summary. --- benchmark/generate_diff.php | 6 +++++- benchmark/shared.php | 6 ++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/benchmark/generate_diff.php b/benchmark/generate_diff.php index 3a1e0d7b1370f..94c020df4b998 100644 --- a/benchmark/generate_diff.php +++ b/benchmark/generate_diff.php @@ -73,7 +73,11 @@ function find_benchmarked_commit_hash(string $repo, string $commitHash): ?string if (file_exists($summaryFile)) { break; } - $commitHash = trim(runCommand(['git', 'rev-parse', $commitHash . '^'], dirname(__DIR__))->stdout); + $commitHash = trim(runCommand( + ['git', 'rev-parse', $commitHash . '^'], + dirname(__DIR__), + printCommand: false, + )->stdout); } return $commitHash; diff --git a/benchmark/shared.php b/benchmark/shared.php index 450101770b28b..0f58a2a1bf870 100644 --- a/benchmark/shared.php +++ b/benchmark/shared.php @@ -5,12 +5,14 @@ class ProcessResult { public $stderr; } -function runCommand(array $args, ?string $cwd = null): ProcessResult { +function runCommand(array $args, ?string $cwd = null, bool $printCommand = true): ProcessResult { $cmd = implode(' ', array_map('escapeshellarg', $args)); $pipes = null; $result = new ProcessResult(); $descriptorSpec = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']]; - fwrite(STDOUT, "> $cmd\n"); + if ($printCommand) { + fwrite(STDOUT, "> $cmd\n"); + } $processHandle = proc_open($cmd, $descriptorSpec, $pipes, $cwd ?? getcwd(), null); $stdin = $pipes[0]; From aa7c8a9de0ce54bd0d8479b3d64e805843022237 Mon Sep 17 00:00:00 2001 From: William Varmus <0@willvar.tw> Date: Sat, 29 Mar 2025 18:46:08 +0800 Subject: [PATCH 13/53] Address deprecated PHP 8.4 session options to prevent test failures Closes GH-18179. --- NEWS | 4 ++++ build/Makefile.global | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 980cf89ce3bd6..6b344f641681e 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,10 @@ PHP NEWS . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). (Jakub Zelenka) +- Tests: + . Address deprecated PHP 8.4 session options to prevent test failures. + (willvar) + 10 Apr 2025, PHP 8.4.6 - BCMath: diff --git a/build/Makefile.global b/build/Makefile.global index d5170ebcae4ac..52a629c048a22 100644 --- a/build/Makefile.global +++ b/build/Makefile.global @@ -89,7 +89,7 @@ PHP_TEST_SHARED_EXTENSIONS = ` \ . $$i; $(top_srcdir)/build/shtool echo -n -- " -d zend_extension=$(top_builddir)/modules/$$dlname"; \ done; \ fi` -PHP_DEPRECATED_DIRECTIVES_REGEX = '^(magic_quotes_(gpc|runtime|sybase)?|(zend_)?extension(_debug)?(_ts)?)[\t\ ]*=' +PHP_DEPRECATED_DIRECTIVES_REGEX = '^(magic_quotes_(gpc|runtime|sybase)?|(zend_)?extension(_debug)?(_ts)?|session\.sid_(length|bits_per_character))[\t\ ]*=' test: all @if test ! -z "$(PHP_EXECUTABLE)" && test -x "$(PHP_EXECUTABLE)"; then \ From 13d51f895b44c866858ee31f200f2619f99c731d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 31 Mar 2025 23:05:07 +0200 Subject: [PATCH 14/53] Add missing EXTENSIONS section to intl test [ci skip] --- ext/intl/tests/dateformat_format_references.phpt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/intl/tests/dateformat_format_references.phpt b/ext/intl/tests/dateformat_format_references.phpt index da1a52955f121..576d901edeb82 100644 --- a/ext/intl/tests/dateformat_format_references.phpt +++ b/ext/intl/tests/dateformat_format_references.phpt @@ -1,5 +1,7 @@ --TEST-- Fix dateformat_format() with array argument with values as references. +--EXTENSIONS-- +intl --SKIPIF-- Date: Mon, 31 Mar 2025 22:06:17 +0200 Subject: [PATCH 15/53] Use-after-free in extract() with EXTR_REFS Fixes GH-18209 Closes GH-18211 --- NEWS | 1 + ext/standard/array.c | 4 +++- ext/standard/tests/gh18209.phpt | 23 +++++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/gh18209.phpt diff --git a/NEWS b/NEWS index c08d732697cb3..37a320d27be07 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,7 @@ PHP NEWS - Standard: . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). (Jakub Zelenka) + . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov) 10 Apr 2025, PHP 8.3.20 diff --git a/ext/standard/array.c b/ext/standard/array.c index 7382e1e9f8bd9..b89deb241f046 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -1863,8 +1863,10 @@ static zend_long php_extract_ref_overwrite(zend_array *arr, zend_array *symbol_t } else { ZVAL_MAKE_REF_EX(entry, 2); } - zval_ptr_dtor(orig_var); + zval garbage; + ZVAL_COPY_VALUE(&garbage, orig_var); ZVAL_REF(orig_var, Z_REF_P(entry)); + zval_ptr_dtor(&garbage); } else { if (Z_ISREF_P(entry)) { Z_ADDREF_P(entry); diff --git a/ext/standard/tests/gh18209.phpt b/ext/standard/tests/gh18209.phpt new file mode 100644 index 0000000000000..6a759639f7dcb --- /dev/null +++ b/ext/standard/tests/gh18209.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-18209: Use-after-free in extract() with EXTR_REFS +--CREDITS-- +Noam Rathaus (nrathaus) +--FILE-- + 42]; +extract($array, EXTR_REFS); +var_dump($b); + +?> +--EXPECT-- +int(42) +int(43) From 2e47442a6b1e9a47d449008df2a853e6e99fada3 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 1 Apr 2025 18:58:31 +0100 Subject: [PATCH 16/53] Fix GH-18212: fseek with SEEK_CUR and negative offset crash on debug Triggers the assertion as with SEEK_CUR the stream position is set to a negative value so we force the failure without affecting its position instead. close GH-18224 --- NEWS | 2 ++ ext/standard/tests/file/gh18212.phpt | 13 +++++++++++++ ext/standard/tests/file/stream_rfc2397_007.phpt | 2 +- main/streams/streams.c | 4 ++++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/file/gh18212.phpt diff --git a/NEWS b/NEWS index 37a320d27be07..335903eee23c0 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,8 @@ PHP NEWS . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). (Jakub Zelenka) . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov) + . Fixed bug GH-18212 (fseek with SEEK_CUR whence value and negative offset + leads to negative stream position). (David Carlier) 10 Apr 2025, PHP 8.3.20 diff --git a/ext/standard/tests/file/gh18212.phpt b/ext/standard/tests/file/gh18212.phpt new file mode 100644 index 0000000000000..6e4d8ad9bd328 --- /dev/null +++ b/ext/standard/tests/file/gh18212.phpt @@ -0,0 +1,13 @@ +--TEST-- +GH-18212: fseek with SEEK_CUR and negative offset leads to negative file stream position. +--FILE-- + +--EXPECT-- +int(-1) +int(-1) + diff --git a/ext/standard/tests/file/stream_rfc2397_007.phpt b/ext/standard/tests/file/stream_rfc2397_007.phpt index dcbe5beeb3dc9..49a037e855fbd 100644 --- a/ext/standard/tests/file/stream_rfc2397_007.phpt +++ b/ext/standard/tests/file/stream_rfc2397_007.phpt @@ -118,7 +118,7 @@ int(2) bool(false) ===S:-10,C=== int(-1) -bool(false) +int(2) bool(false) ===S:3,S=== int(0) diff --git a/main/streams/streams.c b/main/streams/streams.c index 7a5dc2a58aaf1..4e0aaa53b443e 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1390,6 +1390,10 @@ PHPAPI int _php_stream_seek(php_stream *stream, zend_off_t offset, int whence) } whence = SEEK_SET; break; + case SEEK_SET: + if (offset < 0) { + return -1; + } } ret = stream->ops->seek(stream, offset, whence, &stream->position); From 79dc7a2d269c2c3ff38e51c1aad12f6fc6d96a1b Mon Sep 17 00:00:00 2001 From: Dmitry Stogov Date: Wed, 2 Apr 2025 16:20:09 +0300 Subject: [PATCH 17/53] Update IR IR commit: 8d17022fb61ebfed9f6be81a8182ea31202697ed --- ext/opcache/jit/ir/ir_x86.dasc | 70 ++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/ext/opcache/jit/ir/ir_x86.dasc b/ext/opcache/jit/ir/ir_x86.dasc index ad785d3b891eb..d01a8c41359aa 100644 --- a/ext/opcache/jit/ir/ir_x86.dasc +++ b/ext/opcache/jit/ir/ir_x86.dasc @@ -650,6 +650,26 @@ IR_ALWAYS_INLINE ir_mem IR_MEM(ir_reg base, int32_t offset, ir_reg index, int32_ || } |.endmacro +/* Like ASM_REG_IMM_OP, but op1 accepts r16,r32,r64 (not r8) */ +|.macro ASM_REG16_IMM_OP, op, type, op1, op2 +|| switch (ir_type_size[type]) { +|| default: +|| IR_ASSERT(0); +|| case 1: +|| case 2: +| op Rw(op1), (op2 & 0xffff) +|| break; +|| case 4: +| op Rd(op1), op2 +|| break; +|.if X64 +|| case 8: +| op Rq(op1), op2 +|| break; +|.endif +|| } +|.endmacro + |.macro ASM_MEM_REG_OP, op, type, op1, op2 | ASM_EXPAND_OP1_MEM ASM_EXPAND_TYPE_MEM_REG, op, type, op1, op2 |.endmacro @@ -1066,6 +1086,7 @@ const char *ir_reg_name(int8_t reg, ir_type type) _(SSE_CEIL) \ _(SSE_TRUNC) \ _(SSE_NEARBYINT) \ + _(BIT_OP) \ #define IR_LEA_FIRST IR_LEA_OB #define IR_LEA_LAST IR_LEA_O_SYM @@ -1400,6 +1421,7 @@ op2_const: case IR_DIV_PWR2: case IR_OP_INT: case IR_OP_FP: + case IR_BIT_OP: flags = IR_DEF_REUSES_OP1_REG | IR_USE_MUST_BE_IN_REG | IR_OP1_SHOULD_BE_IN_REG; break; case IR_MOD_PWR2: @@ -2280,6 +2302,9 @@ binop_fp: // return IR_COPY_INT; } else if (op2_insn->val.i64 == -1) { // -1 + } else if (IR_IS_POWER_OF_TWO(op2_insn->val.u64) && !IR_IS_SIGNED_32BIT(op2_insn->val.i64)) { + /* OR(X, PWR2) => BTS */ + return IR_BIT_OP; } } goto binop_int; @@ -2294,6 +2319,9 @@ binop_fp: // 0 } else if (op2_insn->val.i64 == -1) { // return IR_COPY_INT; + } else if (IR_IS_POWER_OF_TWO(~op2_insn->val.u64) && !IR_IS_SIGNED_32BIT(op2_insn->val.i64)) { + /* AND(X, ~PWR2) => BTR */ + return IR_BIT_OP; } } goto binop_int; @@ -4341,6 +4369,45 @@ static void ir_emit_mul_div_mod_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn) } } +static void ir_emit_bit_op(ir_ctx *ctx, ir_ref def, ir_insn *insn) +{ + ir_backend_data *data = ctx->data; + dasm_State **Dst = &data->dasm_state; + ir_type type = insn->type; + ir_ref op1 = insn->op1; + ir_reg def_reg = IR_REG_NUM(ctx->regs[def][0]); + ir_reg op1_reg = ctx->regs[def][1]; + + IR_ASSERT(IR_IS_CONST_REF(insn->op2)); + IR_ASSERT(!IR_IS_SYM_CONST(ctx->ir_base[insn->op2].op)); + IR_ASSERT(def_reg != IR_REG_NONE); + + if (op1_reg != IR_REG_NONE && IR_REG_SPILLED(op1_reg)) { + op1_reg = IR_REG_NUM(op1_reg); + ir_emit_load(ctx, type, op1_reg, op1); + } + if (def_reg != op1_reg) { + if (op1_reg != IR_REG_NONE) { + ir_emit_mov(ctx, type, def_reg, op1_reg); + } else { + ir_emit_load(ctx, type, def_reg, op1); + } + } + if (insn->op == IR_OR) { + uint32_t bit = IR_LOG2(ctx->ir_base[insn->op2].val.u64); + + | ASM_REG16_IMM_OP, bts, type, def_reg, bit + } else { + IR_ASSERT(insn->op == IR_AND); + uint32_t bit = IR_LOG2(~ctx->ir_base[insn->op2].val.u64); + + | ASM_REG16_IMM_OP, btr, type, def_reg, bit + } + if (IR_REG_SPILLED(ctx->regs[def][0])) { + ir_emit_store(ctx, type, def, def_reg); + } +} + static void ir_emit_sdiv_pwr2(ir_ctx *ctx, ir_ref def, ir_insn *insn) { ir_backend_data *data = ctx->data; @@ -10668,6 +10735,9 @@ void *ir_emit_code(ir_ctx *ctx, size_t *size_ptr) case IR_MOD_PWR2: ir_emit_mul_div_mod_pwr2(ctx, i, insn); break; + case IR_BIT_OP: + ir_emit_bit_op(ctx, i, insn); + break; case IR_SDIV_PWR2: ir_emit_sdiv_pwr2(ctx, i, insn); break; From 74720a22f332cb09435405d50c312413c86af45e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 29 Mar 2025 23:36:57 +0100 Subject: [PATCH 18/53] Fix memory leak in openssl_sign() when passing invalid algorithm Closes GH-18185. --- NEWS | 4 ++++ ext/openssl/openssl.c | 1 + .../tests/openssl_sign_invalid_algorithm.phpt | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 ext/openssl/tests/openssl_sign_invalid_algorithm.phpt diff --git a/NEWS b/NEWS index 335903eee23c0..188c5a877968c 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage in gdImageCrop(). (David Carlier) +- OpenSSL: + . Fix memory leak in openssl_sign() when passing invalid algorithm. + (nielsdos) + - Standard: . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). (Jakub Zelenka) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index c5720ee97e38c..bd386dbe8ae7a 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -6959,6 +6959,7 @@ PHP_FUNCTION(openssl_sign) mdtype = php_openssl_get_evp_md_from_algo(method_long); } if (!mdtype) { + EVP_PKEY_free(pkey); php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; } diff --git a/ext/openssl/tests/openssl_sign_invalid_algorithm.phpt b/ext/openssl/tests/openssl_sign_invalid_algorithm.phpt new file mode 100644 index 0000000000000..c669a373a1079 --- /dev/null +++ b/ext/openssl/tests/openssl_sign_invalid_algorithm.phpt @@ -0,0 +1,18 @@ +--TEST-- +openssl_sign: invalid algorithm +--EXTENSIONS-- +openssl +--FILE-- + +--EXPECTF-- +Warning: openssl_sign(): Unknown digest algorithm in %s on line %d From 0dc600c69aaf90361775c2d9c83094e324fae3d5 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 30 Mar 2025 23:51:58 +0200 Subject: [PATCH 19/53] Fix openssl_random_pseudo_bytes() always setting strong_result to true This regressed in 62c7432f, prior to that commit the value was set to false in case random number generation failed, but now even if an exception is thrown it is set to true. This likely does not _really_ matter as the user will handle the exception, still the value in $strong_result is observable. --- ext/openssl/openssl.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index bd386dbe8ae7a..9eb052335b8d5 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -7961,17 +7961,15 @@ PHP_FUNCTION(openssl_random_pseudo_bytes) RETURN_THROWS(); } - if (zstrong_result_returned) { - ZEND_TRY_ASSIGN_REF_FALSE(zstrong_result_returned); - } - if ((buffer = php_openssl_random_pseudo_bytes(buffer_length))) { ZSTR_VAL(buffer)[buffer_length] = 0; RETVAL_NEW_STR(buffer); - } - if (zstrong_result_returned) { - ZEND_TRY_ASSIGN_REF_TRUE(zstrong_result_returned); + if (zstrong_result_returned) { + ZEND_TRY_ASSIGN_REF_TRUE(zstrong_result_returned); + } + } else if (zstrong_result_returned) { + ZEND_TRY_ASSIGN_REF_FALSE(zstrong_result_returned); } } /* }}} */ From 5e68671f88ef92f0612bfa3e2819e5107e624418 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 30 Mar 2025 23:54:56 +0200 Subject: [PATCH 20/53] Fix inverted call to php_openssl_store_errors() This calls php_openssl_store_errors() in the success path right now, change it to call php_openssl_store_errors() in the error path. --- ext/openssl/openssl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 9eb052335b8d5..bfbf51ff6442c 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -7940,11 +7940,10 @@ PHP_OPENSSL_API zend_string* php_openssl_random_pseudo_bytes(zend_long buffer_le PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(buffer_length, length); PHP_OPENSSL_RAND_ADD_TIME(); if (RAND_bytes((unsigned char*)ZSTR_VAL(buffer), (int)buffer_length) <= 0) { + php_openssl_store_errors(); zend_string_release_ex(buffer, 0); zend_throw_exception(zend_ce_exception, "Error reading from source device", 0); return NULL; - } else { - php_openssl_store_errors(); } return buffer; From 8a1f6711bf75da789fbc23a63629d16a1925e252 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 1 Apr 2025 22:54:17 +0200 Subject: [PATCH 21/53] Fix resource leak in iptcembed() on error Closes GH-18225. --- NEWS | 1 + ext/standard/iptc.c | 1 + 2 files changed, 2 insertions(+) diff --git a/NEWS b/NEWS index 188c5a877968c..92b199270cf4d 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,7 @@ PHP NEWS . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov) . Fixed bug GH-18212 (fseek with SEEK_CUR whence value and negative offset leads to negative stream position). (David Carlier) + . Fix resource leak in iptcembed() on error. (nielsdos) 10 Apr 2025, PHP 8.3.20 diff --git a/ext/standard/iptc.c b/ext/standard/iptc.c index e4dd38637570a..44dd33bab10ac 100644 --- a/ext/standard/iptc.c +++ b/ext/standard/iptc.c @@ -204,6 +204,7 @@ PHP_FUNCTION(iptcembed) if (spool < 2) { if (zend_fstat(fileno(fp), &sb) != 0) { + fclose(fp); RETURN_FALSE; } From 7a3383b482af333c03aca7d52a2012857840e746 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 3 Apr 2025 13:01:59 +0200 Subject: [PATCH 22/53] [skip ci] Restrict on-push freebsd build to main repo The same applies to all other push jobs, it was just forgotten here. --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index ddc90935018ba..214798af62b5c 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -171,6 +171,7 @@ jobs: - name: Test run: .github/scripts/windows/test.bat FREEBSD: + if: github.repository == 'php/php-src' || github.event_name == 'pull_request' name: FREEBSD runs-on: ubuntu-latest steps: From fe8dffef5df949a3bf574023c2ee442fcc0adb3e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 4 Apr 2025 06:55:59 +0100 Subject: [PATCH 23/53] Fixed GH-18243: imagettftext underflow/overflow on size argument. close GH-18245 --- NEWS | 2 ++ ext/gd/gd.c | 11 ++++++++++ ext/gd/tests/gh18243.phpt | 42 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) create mode 100644 ext/gd/tests/gh18243.phpt diff --git a/NEWS b/NEWS index 92b199270cf4d..774058d253783 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,8 @@ PHP NEWS - GD: . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage in gdImageCrop(). (David Carlier) + . Fixed GH-18243 imagettftext() overflow/underflow on font size value. + (David Carlier) - OpenSSL: . Fix memory leak in openssl_sign() when passing invalid algorithm. diff --git a/ext/gd/gd.c b/ext/gd/gd.c index ae03b602cdc5b..6b727a211189a 100644 --- a/ext/gd/gd.c +++ b/ext/gd/gd.c @@ -3082,6 +3082,17 @@ static void php_imagettftext_common(INTERNAL_FUNCTION_PARAMETERS, int mode) im = php_gd_libgdimageptr_from_zval_p(IM); } + // FT_F26Dot6 is a signed long alias + if (ptsize < (double)LONG_MIN / 64 || ptsize > (double)LONG_MAX / 64) { + zend_argument_value_error(2, "must be between " ZEND_LONG_FMT " and " ZEND_LONG_FMT, (zend_long)((double)LONG_MIN / 64), (zend_long)((double)LONG_MAX / 64)); + RETURN_THROWS(); + } + + if (UNEXPECTED(!zend_finite(ptsize))) { + zend_argument_value_error(2, "must be finite"); + RETURN_THROWS(); + } + /* convert angle to radians */ angle = angle * (M_PI/180); diff --git a/ext/gd/tests/gh18243.phpt b/ext/gd/tests/gh18243.phpt new file mode 100644 index 0000000000000..3235098a3dcc2 --- /dev/null +++ b/ext/gd/tests/gh18243.phpt @@ -0,0 +1,42 @@ +--TEST-- +GH-18243: imagefttext underflow/overflow on $size +--EXTENSIONS-- +gd +--SKIPIF-- + +--FILE-- +getMessage(), PHP_EOL; +} + +try { + imagettftext($im, PHP_INT_MIN, 0, 15, 60, 0, $font, ""); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + imagettftext($im, NAN, 0, 15, 60, 0, $font, ""); +} catch (\ValueError $e) { + echo $e->getMessage(), PHP_EOL; +} + +try { + imagettftext($im, INF, 0, 15, 60, 0, $font, ""); +} catch (\ValueError $e) { + echo $e->getMessage(); +} +?> +--EXPECTF-- +imagettftext(): Argument #2 ($size) must be between %i and %d +imagettftext(): Argument #2 ($size) must be between %i and %d +imagettftext(): Argument #2 ($size) must be finite +imagettftext(): Argument #2 ($size) must be between %i and %d From fed948dbd48f247de8181a127835adf1ff725794 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 4 Apr 2025 20:48:57 +0100 Subject: [PATCH 24/53] Fixed GH-18247: dba_popen() memory leak on invalid path. and a handful more error code paths. close GH-18250 --- NEWS | 3 +++ ext/dba/dba.c | 25 +++++++++++++------------ ext/dba/tests/gh18247.phpt | 12 ++++++++++++ 3 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 ext/dba/tests/gh18247.phpt diff --git a/NEWS b/NEWS index d04c454e35b92..f3a314498bbbc 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,9 @@ PHP NEWS . Fixed bug GH-18038 (Lazy proxy calls magic methods twice). (Arnaud) . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov) +- DBA: + . FIxed bug GH-18247 dba_popen() memory leak on invalid path. (David Carlier) + - GD: . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage in gdImageCrop(). (David Carlier) diff --git a/ext/dba/dba.c b/ext/dba/dba.c index 2205b13dfe050..7982e4255b07c 100644 --- a/ext/dba/dba.c +++ b/ext/dba/dba.c @@ -864,9 +864,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent) } if (!connection->info->lock.fp) { /* stream operation already wrote an error message */ - efree(resource_key); - zval_ptr_dtor(return_value); - RETURN_FALSE; + goto fail; } if (!error && !php_stream_supports_lock(connection->info->lock.fp)) { error = "Stream does not support locking"; @@ -885,9 +883,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent) } if (!connection->info->fp) { /* stream operation already wrote an error message */ - efree(resource_key); - zval_ptr_dtor(return_value); - RETURN_FALSE; + goto fail; } if (hptr->flags & (DBA_NO_APPEND|DBA_CAST_AS_FD)) { /* Needed because some systems do not allow to write to the original @@ -895,9 +891,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent) */ if (SUCCESS != php_stream_cast(connection->info->fp, PHP_STREAM_AS_FD, (void*)&connection->info->fd, 1)) { php_error_docref(NULL, E_WARNING, "Could not cast stream"); - efree(resource_key); - zval_ptr_dtor(return_value); - RETURN_FALSE; + goto fail; #ifdef F_SETFL } else if (modenr == DBA_CREAT) { int flags = fcntl(connection->info->fd, F_GETFL); @@ -931,9 +925,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent) php_error_docref(NULL, E_WARNING, "Driver initialization failed for handler: %s", hptr->name); } } - efree(resource_key); - zval_ptr_dtor(return_value); - RETURN_FALSE; + goto fail; } connection->info->hnd = hptr; @@ -942,6 +934,7 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent) if (zend_register_persistent_resource(resource_key, resource_key_len, connection->info, le_pdb) == NULL) { php_error_docref(NULL, E_WARNING, "Could not register persistent resource"); efree(resource_key); + dba_close_connection(connection); zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -949,6 +942,14 @@ static void php_dba_open(INTERNAL_FUNCTION_PARAMETERS, bool persistent) zend_hash_add_new(&DBA_G(connections), connection->hash, return_value); efree(resource_key); + return; +fail: + efree(resource_key); + zend_string_release_ex(connection->hash, persistent); + dba_close_info(connection->info); + connection->info = NULL; + zval_ptr_dtor(return_value); + RETURN_FALSE; } /* }}} */ diff --git a/ext/dba/tests/gh18247.phpt b/ext/dba/tests/gh18247.phpt new file mode 100644 index 0000000000000..bb757452321d9 --- /dev/null +++ b/ext/dba/tests/gh18247.phpt @@ -0,0 +1,12 @@ +--TEST-- +GH-18247: dba_open() memory leak on invalid path +--EXTENSIONS-- +dba +--FILE-- + +--EXPECTF-- + +Warning: dba_popen(/inexistent): Failed to open stream: No such file or directory in %s on line %d +bool(false) From 61f704f26990326a86d925e1374093e9b2e2bed2 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 17 Mar 2025 14:12:11 +0000 Subject: [PATCH 25/53] ext/libxml: Fixed custom external entity loader returning an invalid resource leading to a confusing TypeError message Closes GH-18096 --- NEWS | 4 ++++ ext/libxml/libxml.c | 19 ++++++++++++------- ...nal_entity_loader_error_callback_name.phpt | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 774058d253783..71430bc20b0ff 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ PHP NEWS . Fixed GH-18243 imagettftext() overflow/underflow on font size value. (David Carlier) +- libxml: + . Fixed custom external entity loader returning an invalid resource leading + to a confusing TypeError message. (Girgias) + - OpenSSL: . Fix memory leak in openssl_sign() when passing invalid algorithm. (nielsdos) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 18ca51e36a052..5c903d2c9a228 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -793,13 +793,18 @@ static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL, is_string: resource = Z_STRVAL(retval); } else if (Z_TYPE(retval) == IS_RESOURCE) { - php_stream *stream; - php_stream_from_zval_no_verify(stream, &retval); - if (stream == NULL) { - php_libxml_ctx_error(context, - "The user entity loader callback '%s' has returned a " - "resource, but it is not a stream", - ZSTR_VAL(LIBXML(entity_loader_callback).function_handler->common.function_name)); + php_stream *stream = (php_stream*)zend_fetch_resource2_ex(&retval, NULL, php_file_le_stream(), php_file_le_pstream()); + if (UNEXPECTED(stream == NULL)) { + zval callable; + zend_get_callable_zval_from_fcc(&LIBXML(entity_loader_callback), &callable); + zend_string *callable_name = zend_get_callable_name(&callable); + zend_string *func_name = get_active_function_or_method_name(); + zend_type_error( + "%s(): The user entity loader callback \"%s\" has returned a resource, but it is not a stream", + ZSTR_VAL(func_name), ZSTR_VAL(callable_name)); + zend_string_release(func_name); + zend_string_release(callable_name); + zval_ptr_dtor(&callable); } else { /* TODO: allow storing the encoding in the stream context? */ xmlCharEncoding enc = XML_CHAR_ENCODING_NONE; diff --git a/ext/libxml/tests/libxml_get_external_entity_loader_error_callback_name.phpt b/ext/libxml/tests/libxml_get_external_entity_loader_error_callback_name.phpt index 1bdbbfb5b817e..2122785ef5b9b 100644 --- a/ext/libxml/tests/libxml_get_external_entity_loader_error_callback_name.phpt +++ b/ext/libxml/tests/libxml_get_external_entity_loader_error_callback_name.phpt @@ -40,4 +40,4 @@ $file = __DIR__ . '/db.dba'; unlink($file); ?> --EXPECT-- -string(73) "DOMDocument::validate(): supplied resource is not a valid stream resource" +string(122) "DOMDocument::validate(): The user entity loader callback "Handler::handle" has returned a resource, but it is not a stream" From 6d458caefedf34f9086120178aae4e9bcb440270 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 7 Apr 2025 17:33:47 +0200 Subject: [PATCH 26/53] Fix prop info fetching from prop slot with added hooks Fixes GH-18268 Closes GH-18271 --- NEWS | 2 ++ Zend/tests/property_hooks/gh18268.phpt | 23 +++++++++++++++++++++++ Zend/zend_compile.h | 2 ++ Zend/zend_objects_API.c | 2 +- 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/property_hooks/gh18268.phpt diff --git a/NEWS b/NEWS index 1aeb22dfb0671..af8017fe151b0 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,8 @@ PHP NEWS evaluation). (ilutov) . Fixed bug GH-18038 (Lazy proxy calls magic methods twice). (Arnaud) . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov) + . Fixed bug GH-18268 (Segfault in array_walk() on object with added property + hooks). (ilutov) - DBA: . FIxed bug GH-18247 dba_popen() memory leak on invalid path. (David Carlier) diff --git a/Zend/tests/property_hooks/gh18268.phpt b/Zend/tests/property_hooks/gh18268.phpt new file mode 100644 index 0000000000000..9836bb6d96270 --- /dev/null +++ b/Zend/tests/property_hooks/gh18268.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-18268: array_walk() on object with added property hooks +--FILE-- + +--EXPECT-- +int(42) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 7f425852d01b4..b2e7de4c343ae 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -464,6 +464,8 @@ typedef struct _zend_property_info { ((uint32_t)(XtOffsetOf(zend_object, properties_table) + sizeof(zval) * (num))) #define OBJ_PROP_TO_NUM(offset) \ (((offset) - OBJ_PROP_TO_OFFSET(0)) / sizeof(zval)) +#define OBJ_PROP_SLOT_TO_OFFSET(obj, slot) \ + ((uintptr_t)(slot) - (uintptr_t)(obj)) typedef struct _zend_class_constant { zval value; /* flags are stored in u2 */ diff --git a/Zend/zend_objects_API.c b/Zend/zend_objects_API.c index 1ba250bec6439..9b1b6dd4fcbb4 100644 --- a/Zend/zend_objects_API.c +++ b/Zend/zend_objects_API.c @@ -203,7 +203,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_store_del(zend_object *object) /* {{{ * ZEND_API ZEND_COLD zend_property_info *zend_get_property_info_for_slot_slow(zend_object *obj, zval *slot) { - uintptr_t offset = (uintptr_t)slot - (uintptr_t)obj->properties_table; + uintptr_t offset = OBJ_PROP_SLOT_TO_OFFSET(obj, slot); zend_property_info *prop_info; ZEND_HASH_MAP_FOREACH_PTR(&obj->ce->properties_info, prop_info) { if (prop_info->offset == offset) { From 389de7c6bf59e14145e932f4d3c0171803a3ba97 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 26 Feb 2025 17:26:08 +0100 Subject: [PATCH 27/53] Fix #17776 LDAP_OPT_X_TLS_REQUIRE_CERT can't be overridden --- ext/ldap/ldap.c | 89 ++++++++++++++++++------ ext/ldap/php_ldap.h | 1 + ext/ldap/tests/ldap_start_tls_basic.phpt | 21 +++++- ext/ldap/tests/ldaps_basic.phpt | 55 +++++++++++++++ 4 files changed, 141 insertions(+), 25 deletions(-) create mode 100644 ext/ldap/tests/ldaps_basic.phpt diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index db4c5ab231042..1a878bfc58483 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -835,6 +835,21 @@ static PHP_GINIT_FUNCTION(ldap) } /* }}} */ +/* {{{ PHP_RINIT_FUNCTION */ +static PHP_RINIT_FUNCTION(ldap) +{ +#if defined(COMPILE_DL_LDAP) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + + /* needed before first connect and after TLS option changes */ + LDAPG(tls_newctx) = true; + + return SUCCESS; +} +/* }}} */ + + /* {{{ PHP_MINIT_FUNCTION */ PHP_MINIT_FUNCTION(ldap) { @@ -987,6 +1002,20 @@ PHP_FUNCTION(ldap_connect) snprintf( url, urllen, "ldap://%s:" ZEND_LONG_FMT, host, port ); } +#ifdef LDAP_OPT_X_TLS_NEWCTX + if (LDAPG(tls_newctx) && url && !strncmp(url, "ldaps:", 6)) { + int val = 0; + + /* ensure all pending TLS options are applied in a new context */ + if (ldap_set_option(NULL, LDAP_OPT_X_TLS_NEWCTX, &val) != LDAP_OPT_SUCCESS) { + zval_ptr_dtor(return_value); + php_error_docref(NULL, E_WARNING, "Could not create new security context"); + RETURN_FALSE; + } + LDAPG(tls_newctx) = false; + } +#endif + #ifdef LDAP_API_FEATURE_X_OPENLDAP /* ldap_init() is deprecated, use ldap_initialize() instead. */ @@ -3172,15 +3201,7 @@ PHP_FUNCTION(ldap_set_option) } switch (option) { - /* options with int value */ - case LDAP_OPT_DEREF: - case LDAP_OPT_SIZELIMIT: - case LDAP_OPT_TIMELIMIT: - case LDAP_OPT_PROTOCOL_VERSION: - case LDAP_OPT_ERROR_NUMBER: -#ifdef LDAP_OPT_DEBUG_LEVEL - case LDAP_OPT_DEBUG_LEVEL: -#endif + /* TLS options with int value */ #ifdef LDAP_OPT_X_TLS_REQUIRE_CERT case LDAP_OPT_X_TLS_REQUIRE_CERT: #endif @@ -3189,6 +3210,18 @@ PHP_FUNCTION(ldap_set_option) #endif #ifdef LDAP_OPT_X_TLS_PROTOCOL_MIN case LDAP_OPT_X_TLS_PROTOCOL_MIN: +#endif + /* TLS option change requires resetting TLS context */ + LDAPG(tls_newctx) = true; + ZEND_FALLTHROUGH; + /* other options with int value */ + case LDAP_OPT_DEREF: + case LDAP_OPT_SIZELIMIT: + case LDAP_OPT_TIMELIMIT: + case LDAP_OPT_PROTOCOL_VERSION: + case LDAP_OPT_ERROR_NUMBER: +#ifdef LDAP_OPT_DEBUG_LEVEL + case LDAP_OPT_DEBUG_LEVEL: #endif #ifdef LDAP_OPT_X_KEEPALIVE_IDLE case LDAP_OPT_X_KEEPALIVE_IDLE: @@ -3245,17 +3278,7 @@ PHP_FUNCTION(ldap_set_option) } } break; #endif - /* options with string value */ - case LDAP_OPT_ERROR_STRING: -#ifdef LDAP_OPT_HOST_NAME - case LDAP_OPT_HOST_NAME: -#endif -#ifdef HAVE_LDAP_SASL - case LDAP_OPT_X_SASL_MECH: - case LDAP_OPT_X_SASL_REALM: - case LDAP_OPT_X_SASL_AUTHCID: - case LDAP_OPT_X_SASL_AUTHZID: -#endif + /* TLS options with string value */ #if (LDAP_API_VERSION > 2000) case LDAP_OPT_X_TLS_CACERTDIR: case LDAP_OPT_X_TLS_CACERTFILE: @@ -3269,6 +3292,20 @@ PHP_FUNCTION(ldap_set_option) #endif #ifdef LDAP_OPT_X_TLS_DHFILE case LDAP_OPT_X_TLS_DHFILE: +#endif + /* TLS option change requires resetting TLS context */ + LDAPG(tls_newctx) = true; + ZEND_FALLTHROUGH; + /* other options with string value */ + case LDAP_OPT_ERROR_STRING: +#ifdef LDAP_OPT_HOST_NAME + case LDAP_OPT_HOST_NAME: +#endif +#ifdef HAVE_LDAP_SASL + case LDAP_OPT_X_SASL_MECH: + case LDAP_OPT_X_SASL_REALM: + case LDAP_OPT_X_SASL_AUTHCID: + case LDAP_OPT_X_SASL_AUTHZID: #endif #ifdef LDAP_OPT_MATCHED_DN case LDAP_OPT_MATCHED_DN: @@ -3688,6 +3725,9 @@ PHP_FUNCTION(ldap_start_tls) zval *link; ldap_linkdata *ld; int rc, protocol = LDAP_VERSION3; +#ifdef LDAP_OPT_X_TLS_NEWCTX + int val = 0; +#endif if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &link, ldap_link_ce) != SUCCESS) { RETURN_THROWS(); @@ -3697,13 +3737,16 @@ PHP_FUNCTION(ldap_start_tls) VERIFY_LDAP_LINK_CONNECTED(ld); if (((rc = ldap_set_option(ld->link, LDAP_OPT_PROTOCOL_VERSION, &protocol)) != LDAP_SUCCESS) || +#ifdef LDAP_OPT_X_TLS_NEWCTX + (LDAPG(tls_newctx) && (rc = ldap_set_option(ld->link, LDAP_OPT_X_TLS_NEWCTX, &val)) != LDAP_OPT_SUCCESS) || +#endif ((rc = ldap_start_tls_s(ld->link, NULL, NULL)) != LDAP_SUCCESS) ) { php_error_docref(NULL, E_WARNING,"Unable to start TLS: %s", ldap_err2string(rc)); RETURN_FALSE; - } else { - RETURN_TRUE; } + LDAPG(tls_newctx) = false; + RETURN_TRUE; } /* }}} */ #endif @@ -4218,7 +4261,7 @@ zend_module_entry ldap_module_entry = { /* {{{ */ ext_functions, PHP_MINIT(ldap), PHP_MSHUTDOWN(ldap), - NULL, + PHP_RINIT(ldap), NULL, PHP_MINFO(ldap), PHP_LDAP_VERSION, diff --git a/ext/ldap/php_ldap.h b/ext/ldap/php_ldap.h index 5581f5b7671a8..a8c4a77af8010 100644 --- a/ext/ldap/php_ldap.h +++ b/ext/ldap/php_ldap.h @@ -39,6 +39,7 @@ PHP_MINFO_FUNCTION(ldap); ZEND_BEGIN_MODULE_GLOBALS(ldap) zend_long num_links; zend_long max_links; + bool tls_newctx; /* create new TLS context before connect */ ZEND_END_MODULE_GLOBALS(ldap) #if defined(ZTS) && defined(COMPILE_DL_LDAP) diff --git a/ext/ldap/tests/ldap_start_tls_basic.phpt b/ext/ldap/tests/ldap_start_tls_basic.phpt index 4dbdce034369f..b8816de9ac4f5 100644 --- a/ext/ldap/tests/ldap_start_tls_basic.phpt +++ b/ext/ldap/tests/ldap_start_tls_basic.phpt @@ -9,11 +9,28 @@ ldap --FILE-- --EXPECT-- +bool(false) bool(true) +bool(false) diff --git a/ext/ldap/tests/ldaps_basic.phpt b/ext/ldap/tests/ldaps_basic.phpt new file mode 100644 index 0000000000000..7a1a1383436d7 --- /dev/null +++ b/ext/ldap/tests/ldaps_basic.phpt @@ -0,0 +1,55 @@ +--TEST-- +ldap_connect() - Basic ldaps test +--EXTENSIONS-- +ldap +--XFAIL-- +Passes locally but fails on CI - need investigation (configuration ?) +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) From 98fb27a5775a1d73029822280c7c67128008aa54 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 10 Apr 2025 17:09:03 +0200 Subject: [PATCH 28/53] NEWS for GH-17940 --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 71430bc20b0ff..416d97aeee93f 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,9 @@ PHP NEWS . Fixed GH-18243 imagettftext() overflow/underflow on font size value. (David Carlier) +- LDAP: + . Fixed bug GH-17940 (LDAP_OPT_X_TLS_REQUIRE_CERT can't be overridden). (Remi) + - libxml: . Fixed custom external entity loader returning an invalid resource leading to a confusing TypeError message. (Girgias) From b3a43ca7a46a4a77c2497d60660473ab2fed9ca4 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 10 Apr 2025 17:14:03 +0200 Subject: [PATCH 29/53] NEWS for GH-17940 --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index af8017fe151b0..8f165880aa8fa 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,9 @@ PHP NEWS . Fixed GH-18243 imagettftext() overflow/underflow on font size value. (David Carlier) +- LDAP: + . Fixed bug GH-17940 (LDAP_OPT_X_TLS_REQUIRE_CERT can't be overridden). (Remi) + - libxml: . Fixed custom external entity loader returning an invalid resource leading to a confusing TypeError message. (Girgias) From 90f582b1880e7ff09f083dfd326e82a8730ee9fd Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 10 Apr 2025 17:21:15 +0200 Subject: [PATCH 30/53] [ci skip] fix news --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 416d97aeee93f..6e91f46dc6b18 100644 --- a/NEWS +++ b/NEWS @@ -9,7 +9,7 @@ PHP NEWS (David Carlier) - LDAP: - . Fixed bug GH-17940 (LDAP_OPT_X_TLS_REQUIRE_CERT can't be overridden). (Remi) + . Fixed bug GH-17776 (LDAP_OPT_X_TLS_* options can't be overridden). (Remi) - libxml: . Fixed custom external entity loader returning an invalid resource leading From 94681850a12812a8e2ba31333d918bf9ee89482b Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 10 Apr 2025 17:21:50 +0200 Subject: [PATCH 31/53] [ci skip] fix news --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8f165880aa8fa..50ea67eb7f519 100644 --- a/NEWS +++ b/NEWS @@ -20,7 +20,7 @@ PHP NEWS (David Carlier) - LDAP: - . Fixed bug GH-17940 (LDAP_OPT_X_TLS_REQUIRE_CERT can't be overridden). (Remi) + . Fixed bug GH-17776 (LDAP_OPT_X_TLS_* options can't be overridden). (Remi) - libxml: . Fixed custom external entity loader returning an invalid resource leading From 4a12a9f3e9866ce34bc29348614d6cc5e907b4e6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 10 Apr 2025 20:11:02 +0200 Subject: [PATCH 32/53] Fix GH-18294: assertion failure zend_jit_ir.c The JIT helper `zend_jit_assign_op_to_typed_ref` expects a `zval*` as an argument, so we have to store to the stack if OP1_DATA(=op3) is in a register. Closes GH-18299. --- NEWS | 3 +++ ext/opcache/jit/zend_jit_ir.c | 7 ++++++ ext/opcache/tests/jit/gh18294.phpt | 37 ++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 ext/opcache/tests/jit/gh18294.phpt diff --git a/NEWS b/NEWS index 50ea67eb7f519..b110830629160 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,9 @@ PHP NEWS . Fixed custom external entity loader returning an invalid resource leading to a confusing TypeError message. (Girgias) +- Opcache: + . Fixed bug GH-18294 (assertion failure zend_jit_ir.c). (nielsdos) + - OpenSSL: . Fix memory leak in openssl_sign() when passing invalid algorithm. (nielsdos) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 7ff6522ba2c4a..e9b1a9f01e184 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -13482,6 +13482,13 @@ static int zend_jit_assign_dim_op(zend_jit_ctx *jit, ref_path = ir_END(); ir_IF_TRUE_cold(if_typed); + if (Z_MODE(op3_addr) == IS_REG) { + zend_jit_addr real_addr = ZEND_ADDR_MEM_ZVAL(ZREG_FP, (opline+1)->op1.var); + if (!zend_jit_spill_store_inv(jit, op3_addr, real_addr, op1_data_info)) { + return 0; + } + op3_addr = real_addr; + } arg2 = jit_ZVAL_ADDR(jit, op3_addr); ir_CALL_3(IR_VOID, ir_CONST_FC_FUNC(zend_jit_assign_op_to_typed_ref), reference, arg2, ir_CONST_FC_FUNC(binary_op)); diff --git a/ext/opcache/tests/jit/gh18294.phpt b/ext/opcache/tests/jit/gh18294.phpt new file mode 100644 index 0000000000000..a3e99e8e7bf0c --- /dev/null +++ b/ext/opcache/tests/jit/gh18294.phpt @@ -0,0 +1,37 @@ +--TEST-- +GH-18294 (assertion failure zend_jit_ir.c) +--EXTENSIONS-- +opcache +--INI-- +opcache.jit=1152 +opcache.jit_hot_func=1 +opcache.jit_hot_side_exit=1 +--FILE-- +>= $overflow; + } + } + return $fusion; +} +?> +--EXPECT-- +Array +( + [1] => 0 + [2] => 0 + [3] => 0 + [4] => 0 + [5] => 0 + [6] => 0 + [7] => 0 + [8] => 0 +) From 14853ea2f2b102bd71b633bd63fec0aa870dfe27 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 10 Apr 2025 20:11:56 +0200 Subject: [PATCH 33/53] Fix reproducibility of test GH-17190 The test failure did not trigger for me when playing with the JIT code. From the original issue report some INI settings were not set properly. --- ext/opcache/tests/jit/gh17190.phpt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/opcache/tests/jit/gh17190.phpt b/ext/opcache/tests/jit/gh17190.phpt index d4bb4372b9215..9027050ed6897 100644 --- a/ext/opcache/tests/jit/gh17190.phpt +++ b/ext/opcache/tests/jit/gh17190.phpt @@ -7,7 +7,9 @@ opcache.enable=1 opcache.enable_cli=1 opcache.file_update_protection=0 opcache.jit_buffer_size=32M -opcache.jit=function +opcache.jit=1254 +opcache.jit_hot_func=1 +opcache.jit_hot_side_exit=1 --FILE-- Date: Sat, 29 Mar 2025 23:42:45 +0100 Subject: [PATCH 34/53] Fix potential leaks when writing to BIO fails When the BIO is created but writing fails, these can leak. Closes GH-18186. --- NEWS | 1 + ext/openssl/openssl.c | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 6e91f46dc6b18..045d9c8d9f431 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,7 @@ PHP NEWS - OpenSSL: . Fix memory leak in openssl_sign() when passing invalid algorithm. (nielsdos) + . Fix potential leaks when writing to BIO fails. (nielsdos) - Standard: . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index bfbf51ff6442c..c978859b7ec00 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -5742,8 +5742,8 @@ PHP_FUNCTION(openssl_pkcs7_read) BIO_get_mem_ptr(bio_out, &bio_buf); ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length); add_index_zval(zout, i, &zcert); - BIO_free(bio_out); } + BIO_free(bio_out); } } @@ -5757,8 +5757,8 @@ PHP_FUNCTION(openssl_pkcs7_read) BIO_get_mem_ptr(bio_out, &bio_buf); ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length); add_index_zval(zout, i, &zcert); - BIO_free(bio_out); } + BIO_free(bio_out); } } @@ -6383,8 +6383,8 @@ PHP_FUNCTION(openssl_cms_read) BIO_get_mem_ptr(bio_out, &bio_buf); ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length); add_index_zval(zout, i, &zcert); - BIO_free(bio_out); } + BIO_free(bio_out); } } @@ -6398,8 +6398,8 @@ PHP_FUNCTION(openssl_cms_read) BIO_get_mem_ptr(bio_out, &bio_buf); ZVAL_STRINGL(&zcert, bio_buf->data, bio_buf->length); add_index_zval(zout, i, &zcert); - BIO_free(bio_out); } + BIO_free(bio_out); } } From ba0853888d8735b1d46fbe26ca7254327b1423aa Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 11 Apr 2025 18:18:14 +0200 Subject: [PATCH 35/53] Fix GH-18304: Changing the properties of a DateInterval through dynamic properties triggers a SegFault For dynamic fetches the cache_slot will be NULL, so we have to check for that when resetting the cache. For zip and xmlreader this couldn't easily be tested because of a lack of writable properties. Closes GH-18307. --- NEWS | 4 ++++ ext/date/php_date.c | 4 +++- ext/date/tests/gh18304.phpt | 35 ++++++++++++++++++++++++++++++++ ext/dom/php_dom.c | 4 +++- ext/dom/tests/gh18304.phpt | 15 ++++++++++++++ ext/pdo/pdo_stmt.c | 5 +++-- ext/simplexml/simplexml.c | 4 +++- ext/simplexml/tests/gh18304.phpt | 18 ++++++++++++++++ ext/snmp/snmp.c | 4 +++- ext/snmp/tests/gh18304.phpt | 15 ++++++++++++++ ext/spl/spl_array.c | 4 +++- ext/spl/tests/gh18304.phpt | 14 +++++++++++++ ext/xmlreader/php_xmlreader.c | 4 ++-- ext/zip/php_zip.c | 4 ++-- 14 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 ext/date/tests/gh18304.phpt create mode 100644 ext/dom/tests/gh18304.phpt create mode 100644 ext/simplexml/tests/gh18304.phpt create mode 100644 ext/snmp/tests/gh18304.phpt create mode 100644 ext/spl/tests/gh18304.phpt diff --git a/NEWS b/NEWS index 045d9c8d9f431..64911472be3b3 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,10 @@ PHP NEWS ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| ?? ??? ????, PHP 8.3.21 +- Core: + . Fixed bug GH-18304 (Changing the properties of a DateInterval through + dynamic properties triggers a SegFault). (nielsdos) + - GD: . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage in gdImageCrop(). (David Carlier) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 6f7796eec1250..c1faa842f8a12 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -4399,7 +4399,9 @@ static zval *date_interval_get_property_ptr_ptr(zend_object *object, zend_string zend_string_equals_literal(name, "days") || zend_string_equals_literal(name, "invert") ) { /* Fallback to read_property. */ - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } ret = NULL; } else { ret = zend_std_get_property_ptr_ptr(object, name, type, cache_slot); diff --git a/ext/date/tests/gh18304.phpt b/ext/date/tests/gh18304.phpt new file mode 100644 index 0000000000000..4bab058a7ec4a --- /dev/null +++ b/ext/date/tests/gh18304.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--FILE-- +$field += $i; +var_dump($di); +?> +--EXPECT-- +object(DateInterval)#1 (10) { + ["y"]=> + int(0) + ["m"]=> + int(0) + ["d"]=> + int(1) + ["h"]=> + int(0) + ["i"]=> + int(0) + ["s"]=> + int(0) + ["f"]=> + float(0) + ["invert"]=> + int(0) + ["days"]=> + bool(false) + ["from_string"]=> + bool(false) +} diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index 1da53bae64b51..841dcd66186f8 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -303,7 +303,9 @@ static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, in return zend_std_get_property_ptr_ptr(object, name, type, cache_slot); } - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } return NULL; } diff --git a/ext/dom/tests/gh18304.phpt b/ext/dom/tests/gh18304.phpt new file mode 100644 index 0000000000000..ead44306ff801 --- /dev/null +++ b/ext/dom/tests/gh18304.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--EXTENSIONS-- +dom +--FILE-- +$field .= 'hello'; +var_dump($text->$field); +?> +--EXPECT-- +string(5) "hello" diff --git a/ext/pdo/pdo_stmt.c b/ext/pdo/pdo_stmt.c index efbf519e5411e..8454b4875374e 100644 --- a/ext/pdo/pdo_stmt.c +++ b/ext/pdo/pdo_stmt.c @@ -2493,9 +2493,10 @@ static zval *pdo_row_get_property_ptr_ptr(zend_object *object, zend_string *name ZEND_IGNORE_VALUE(object); ZEND_IGNORE_VALUE(name); ZEND_IGNORE_VALUE(type); - ZEND_IGNORE_VALUE(cache_slot); - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } return NULL; } diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 46ec3a2a788ab..11f497a6673ea 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -635,7 +635,9 @@ static zval *sxe_property_get_adr(zend_object *object, zend_string *zname, int f SXE_ITER type; zval member; - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } sxe = php_sxe_fetch_object(object); GET_NODE(sxe, node); diff --git a/ext/simplexml/tests/gh18304.phpt b/ext/simplexml/tests/gh18304.phpt new file mode 100644 index 0000000000000..b92690856e231 --- /dev/null +++ b/ext/simplexml/tests/gh18304.phpt @@ -0,0 +1,18 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--EXTENSIONS-- +simplexml +--FILE-- +'); +$field = 'abc'; +$sxe->$field .= 'hello'; +var_dump($sxe->$field); +?> +--EXPECT-- +object(SimpleXMLElement)#3 (1) { + [0]=> + string(5) "hello" +} diff --git a/ext/snmp/snmp.c b/ext/snmp/snmp.c index 02348a5fbca00..0ba39ed87c241 100644 --- a/ext/snmp/snmp.c +++ b/ext/snmp/snmp.c @@ -1861,7 +1861,9 @@ static zval *php_snmp_get_property_ptr_ptr(zend_object *object, zend_string *nam return zend_std_get_property_ptr_ptr(object, name, type, cache_slot); } - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } return NULL; } diff --git a/ext/snmp/tests/gh18304.phpt b/ext/snmp/tests/gh18304.phpt new file mode 100644 index 0000000000000..2faf503ac1a58 --- /dev/null +++ b/ext/snmp/tests/gh18304.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--EXTENSIONS-- +snmp +--FILE-- +$field++; +var_dump($snmp->$field); +?> +--EXPECT-- +int(1) diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 8d4541797a1c5..0abaf743ac9d1 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -846,7 +846,9 @@ static zval *spl_array_get_property_ptr_ptr(zend_object *object, zend_string *na if ((intern->ar_flags & SPL_ARRAY_ARRAY_AS_PROPS) != 0 && !zend_std_has_property(object, name, ZEND_PROPERTY_EXISTS, NULL)) { - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; + } /* If object has offsetGet() overridden, then fallback to read_property, * which will call offsetGet(). */ diff --git a/ext/spl/tests/gh18304.phpt b/ext/spl/tests/gh18304.phpt new file mode 100644 index 0000000000000..d93ee3534d0d4 --- /dev/null +++ b/ext/spl/tests/gh18304.phpt @@ -0,0 +1,14 @@ +--TEST-- +GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault) +--CREDITS-- +orose-assetgo +--FILE-- + 1]); +$ao->setFlags(ArrayObject::ARRAY_AS_PROPS); +$field = 'abc'; +$ao->$field++; +var_dump($ao->$field); +?> +--EXPECT-- +int(2) diff --git a/ext/xmlreader/php_xmlreader.c b/ext/xmlreader/php_xmlreader.c index 569058e91a454..dca1898f1c044 100644 --- a/ext/xmlreader/php_xmlreader.c +++ b/ext/xmlreader/php_xmlreader.c @@ -121,8 +121,6 @@ zval *xmlreader_get_property_ptr_ptr(zend_object *object, zend_string *name, int zval *retval = NULL; xmlreader_prop_handler *hnd = NULL; - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; - obj = php_xmlreader_fetch_object(object); if (obj->prop_handler != NULL) { @@ -131,6 +129,8 @@ zval *xmlreader_get_property_ptr_ptr(zend_object *object, zend_string *name, int if (hnd == NULL) { retval = zend_std_get_property_ptr_ptr(object, name, type, cache_slot); + } else if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; } return retval; diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index e28cf688dcff8..172057685105b 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -890,8 +890,6 @@ static zval *php_zip_get_property_ptr_ptr(zend_object *object, zend_string *name zval *retval = NULL; zip_prop_handler *hnd = NULL; - cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; - obj = php_zip_fetch_object(object); if (obj->prop_handler != NULL) { @@ -900,6 +898,8 @@ static zval *php_zip_get_property_ptr_ptr(zend_object *object, zend_string *name if (hnd == NULL) { retval = zend_std_get_property_ptr_ptr(object, name, type, cache_slot); + } else if (cache_slot) { + cache_slot[0] = cache_slot[1] = cache_slot[2] = NULL; } return retval; From 8849a5336ea6feda910567acca34407901d3fc57 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 11 Apr 2025 20:59:34 +0200 Subject: [PATCH 36/53] Fix GH-18309: ipv6 filter integer overflow The intermediate computation can cause a signed integer overflow, but the input is correctly rejected later on by the check on variable `n`. Solve this by using an unsigned number. Closes GH-18312. --- NEWS | 3 +++ ext/filter/logical_filters.c | 3 ++- ext/filter/tests/gh18309.phpt | 10 ++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 ext/filter/tests/gh18309.phpt diff --git a/NEWS b/NEWS index 64911472be3b3..c652b3b83744a 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,9 @@ PHP NEWS . Fixed bug GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault). (nielsdos) +- Filter: + . Fixed bug GH-18309 (ipv6 filter integer overflow). (nielsdos) + - GD: . Fixed imagecrop() overflow with rect argument with x/width y/heigh usage in gdImageCrop(). (David Carlier) diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 1bd9bad5afbe1..76656a218d281 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -762,7 +762,8 @@ static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) { int compressed_pos = -1; int blocks = 0; - int num, n, i; + unsigned int num, n; + int i; char *ipv4; const char *end; int ip4elm[4]; diff --git a/ext/filter/tests/gh18309.phpt b/ext/filter/tests/gh18309.phpt new file mode 100644 index 0000000000000..b541f10883fe6 --- /dev/null +++ b/ext/filter/tests/gh18309.phpt @@ -0,0 +1,10 @@ +--TEST-- +GH-18309 (ipv6 filter integer overflow) +--EXTENSIONS-- +filter +--FILE-- + +--EXPECT-- +bool(false) From 7415dc4649ea057cc2e800aec1dde535b12cd550 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 11 Apr 2025 23:56:57 +0200 Subject: [PATCH 37/53] Fix sxe test --- ext/simplexml/tests/gh18304.phpt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/simplexml/tests/gh18304.phpt b/ext/simplexml/tests/gh18304.phpt index b92690856e231..c6b0f76bd04e2 100644 --- a/ext/simplexml/tests/gh18304.phpt +++ b/ext/simplexml/tests/gh18304.phpt @@ -11,8 +11,8 @@ $field = 'abc'; $sxe->$field .= 'hello'; var_dump($sxe->$field); ?> ---EXPECT-- -object(SimpleXMLElement)#3 (1) { +--EXPECTF-- +object(SimpleXMLElement)#%d (1) { [0]=> string(5) "hello" } From 67503870ca2957aca9270bad324b32ab349559aa Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 14 Apr 2025 11:29:08 +0200 Subject: [PATCH 38/53] Fix GH-18322: SplObjectStorage debug handler mismanages memory This hack was once necessary before there was a proper get_gc handler, but now it breaks the engine constraints. Closes GH-18323. --- NEWS | 4 ++++ ext/spl/spl_observer.c | 6 ++---- ext/spl/tests/gh18322.phpt | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 ext/spl/tests/gh18322.phpt diff --git a/NEWS b/NEWS index c652b3b83744a..6e5a4bc4cfc62 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,10 @@ PHP NEWS (nielsdos) . Fix potential leaks when writing to BIO fails. (nielsdos) +- SPL: + . Fixed bug GH-18322 (SplObjectStorage debug handler mismanages memory). + (nielsdos) + - Standard: . Fixed bug GH-18145 (php8ts crashes in php_clear_stat_cache()). (Jakub Zelenka) diff --git a/ext/spl/spl_observer.c b/ext/spl/spl_observer.c index b3a27cb10292c..83889c430b7a2 100644 --- a/ext/spl/spl_observer.c +++ b/ext/spl/spl_observer.c @@ -340,12 +340,10 @@ static inline HashTable* spl_object_storage_debug_info(zend_object *obj) /* {{{ ZEND_HASH_FOREACH_PTR(&intern->storage, element) { array_init(&tmp); - /* Incrementing the refcount of obj and inf would confuse the garbage collector. - * Prefer to null the destructor */ - Z_ARRVAL_P(&tmp)->pDestructor = NULL; zval obj; - ZVAL_OBJ(&obj, element->obj); + ZVAL_OBJ_COPY(&obj, element->obj); add_assoc_zval_ex(&tmp, "obj", sizeof("obj") - 1, &obj); + Z_TRY_ADDREF(element->inf); add_assoc_zval_ex(&tmp, "inf", sizeof("inf") - 1, &element->inf); zend_hash_next_index_insert(Z_ARRVAL(storage), &tmp); } ZEND_HASH_FOREACH_END(); diff --git a/ext/spl/tests/gh18322.phpt b/ext/spl/tests/gh18322.phpt new file mode 100644 index 0000000000000..e70cbd0d37dd5 --- /dev/null +++ b/ext/spl/tests/gh18322.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-18322 (SplObjectStorage debug handler mismanages memory) +--FILE-- +__debugInfo(); +$tmp2 = $tmp[array_key_first($tmp)]; +unset($tmp); // Drop $tmp2 RC to 1 +$tmp2[0]['obj'] = new stdClass; +var_dump($tmp2); + +?> +--EXPECT-- +array(1) { + [0]=> + array(2) { + ["obj"]=> + object(stdClass)#3 (0) { + } + ["inf"]=> + int(1) + } +} From 701f3a1af67029a1d953ca054f33385be32c7f4a Mon Sep 17 00:00:00 2001 From: haszi Date: Tue, 9 Jan 2024 14:43:44 +0100 Subject: [PATCH 39/53] Mark ob_start callback parameter nullable --- ext/standard/basic_functions.stub.php | 2 +- ext/standard/basic_functions_arginfo.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index be9b024258ee8..3bdc3827b4d36 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1513,7 +1513,7 @@ function header_register_callback(callable $callback): bool {} /* main/output.c */ -/** @param callable $callback */ +/** @param callable|null $callback */ function ob_start($callback = null, int $chunk_size = 0, int $flags = PHP_OUTPUT_HANDLER_STDFLAGS): bool {} function ob_flush(): bool {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 99e08feddf590..1361cf6062797 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7389d094a842a2289cd32cb37386e5e40ea7e031 */ + * Stub hash: 2a3d8da0b92134dcca74f2ac70454bd27768f20e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) From 9d4f8b5379f2dfb071f4311ed5d0c421e014dbf7 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 8 Apr 2025 23:32:36 +0900 Subject: [PATCH 40/53] Fixed GH-18276 - persistent connection - "zend_mm_heap corrupted" with setAttribute() (#18280) Closes #18280 Fixes #18276 --- NEWS | 4 ++++ ext/pdo_firebird/firebird_driver.c | 21 +++++++++-------- ext/pdo_firebird/tests/gh18276.phpt | 35 +++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 ext/pdo_firebird/tests/gh18276.phpt diff --git a/NEWS b/NEWS index 6e5a4bc4cfc62..7d660e26cef75 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,10 @@ PHP NEWS (nielsdos) . Fix potential leaks when writing to BIO fails. (nielsdos) +- PDO Firebird: + . Fixed GH-18276 - persistent connection - "zend_mm_heap corrupted" + with setAttribute() (SakiTakamachi). + - SPL: . Fixed bug GH-18322 (SplObjectStorage debug handler mismanages memory). (nielsdos) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 4f8cd83d7b8ab..504e739a974d9 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -492,13 +492,13 @@ static void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */ } if (H->date_format) { - efree(H->date_format); + pefree(H->date_format, dbh->is_persistent); } if (H->time_format) { - efree(H->time_format); + pefree(H->time_format, dbh->is_persistent); } if (H->timestamp_format) { - efree(H->timestamp_format); + pefree(H->timestamp_format, dbh->is_persistent); } pefree(H, dbh->is_persistent); @@ -881,9 +881,10 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval * return false; } if (H->date_format) { - efree(H->date_format); + pefree(H->date_format, dbh->is_persistent); + H->date_format = NULL; } - spprintf(&H->date_format, 0, "%s", ZSTR_VAL(str)); + H->date_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent); zend_string_release_ex(str, 0); } return true; @@ -895,9 +896,10 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval * return false; } if (H->time_format) { - efree(H->time_format); + pefree(H->time_format, dbh->is_persistent); + H->time_format = NULL; } - spprintf(&H->time_format, 0, "%s", ZSTR_VAL(str)); + H->time_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent); zend_string_release_ex(str, 0); } return true; @@ -909,9 +911,10 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval * return false; } if (H->timestamp_format) { - efree(H->timestamp_format); + pefree(H->timestamp_format, dbh->is_persistent); + H->timestamp_format = NULL; } - spprintf(&H->timestamp_format, 0, "%s", ZSTR_VAL(str)); + H->timestamp_format = pestrndup(ZSTR_VAL(str), ZSTR_LEN(str),dbh->is_persistent); zend_string_release_ex(str, 0); } return true; diff --git a/ext/pdo_firebird/tests/gh18276.phpt b/ext/pdo_firebird/tests/gh18276.phpt new file mode 100644 index 0000000000000..610876166ccf7 --- /dev/null +++ b/ext/pdo_firebird/tests/gh18276.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-18276 (persistent connection - setAttribute(Pdo\Firebird::ATTR_DATE_FORMAT, ..) results in "zend_mm_heap corrupted") +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--XLEAK-- +A bug in firebird causes a memory leak when calling `isc_attach_database()`. +See https://github.com/FirebirdSQL/firebird/issues/7849 +--FILE-- + true, + ], + ); + // Avoid interned + $dbh->setAttribute(PDO::FB_ATTR_DATE_FORMAT, str_repeat('Y----m----d', random_int(1, 1))); + $dbh->setAttribute(PDO::FB_ATTR_TIME_FORMAT, str_repeat('H::::i::::s', random_int(1, 1))); + $dbh->setAttribute(PDO::FB_ATTR_TIMESTAMP_FORMAT, str_repeat('Y----m----d....H::::i::::s', random_int(1, 1))); + unset($dbh); +} + +echo 'done!'; +?> +--EXPECT-- +done! From 061b46e09d9485f134427b55d3efb2e257bd6d97 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Wed, 9 Apr 2025 16:31:48 +0200 Subject: [PATCH 41/53] Save opline in zend_jit_hot_func() Closes GH-18289 --- NEWS | 1 + ext/opcache/jit/zend_jit.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/NEWS b/NEWS index 5912cde4fa66a..582efbc598c6a 100644 --- a/NEWS +++ b/NEWS @@ -33,6 +33,7 @@ PHP NEWS - Opcache: . Fixed bug GH-18294 (assertion failure zend_jit_ir.c). (nielsdos) + . Fixed bug GH-18289 (Fix segfault in JIT). (Florian Engelhardt) - OpenSSL: . Fix memory leak in openssl_sign() when passing invalid algorithm. diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 2d4ef0bf4fc38..e477cbdb5628b 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -3033,6 +3033,10 @@ void ZEND_FASTCALL zend_jit_hot_func(zend_execute_data *execute_data, const zend op_array->opcodes[i].handler = jit_extension->orig_handlers[i]; } +#ifdef HAVE_GCC_GLOBAL_REGS + EX(opline) = opline; +#endif + /* perform real JIT for this function */ zend_real_jit_func(op_array, NULL, opline, ZEND_JIT_ON_HOT_COUNTERS); } zend_catch { From 0a6326c6ac93c53b8804d23d1d42bf91475a260d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 15 Apr 2025 23:58:33 +0200 Subject: [PATCH 42/53] Fix uouv when handling empty options in ZipArchive::addGlob() Reported by OpenAI AARDVARK. php_zip_parse_option is only called when options are passed to the function. Prior to this patch, php_zip_parse_option was responsible for zeroing the opts variable. So in the case when php_zip_parse_option is not called, opts remains uninitialized yet it is being used anyway. By just always zeroing opts at declaration time, we avoid this issue and we are unlikely to reintroduce this in the future. Closes GH-18329. --- NEWS | 3 +++ ext/zip/php_zip.c | 4 ++-- ext/zip/tests/addGlob_empty_options.phpt | 22 ++++++++++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 ext/zip/tests/addGlob_empty_options.phpt diff --git a/NEWS b/NEWS index 7d660e26cef75..a3f2990962d42 100644 --- a/NEWS +++ b/NEWS @@ -43,6 +43,9 @@ PHP NEWS leads to negative stream position). (David Carlier) . Fix resource leak in iptcembed() on error. (nielsdos) +- Zip: + . Fix uouv when handling empty options in ZipArchive::addGlob(). (nielsdos) + 10 Apr 2025, PHP 8.3.20 - Core: diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 172057685105b..be096ad56e29d 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -354,13 +354,13 @@ typedef struct { #endif } zip_options; +/* Expects opts to be zero-initialized. */ static int php_zip_parse_options(HashTable *options, zip_options *opts) /* {{{ */ { zval *option; /* default values */ - memset(opts, 0, sizeof(zip_options)); opts->flags = ZIP_FL_OVERWRITE; opts->comp_method = -1; /* -1 to not change default */ #ifdef HAVE_ENCRYPTION @@ -1732,7 +1732,7 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* size_t path_len = 1; zend_long glob_flags = 0; HashTable *options = NULL; - zip_options opts; + zip_options opts = {0}; int found; zend_string *pattern; diff --git a/ext/zip/tests/addGlob_empty_options.phpt b/ext/zip/tests/addGlob_empty_options.phpt new file mode 100644 index 0000000000000..f4a4126059a7b --- /dev/null +++ b/ext/zip/tests/addGlob_empty_options.phpt @@ -0,0 +1,22 @@ +--TEST-- +addGlob with empty options +--EXTENSIONS-- +zip +--FILE-- +open($file, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->addGlob(__FILE__, 0, []); +var_dump($zip->statIndex(0)['name'] === __FILE__); +$zip->close(); + +?> +--CLEAN-- + +--EXPECT-- +bool(true) From 91c6c727d516716855d6fa8bd75908cd3249d17e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 15 Apr 2025 23:59:25 +0200 Subject: [PATCH 43/53] Fix memory leak when handling a too long path in ZipArchive::addGlob() Closes GH-18330. --- NEWS | 2 ++ ext/zip/php_zip.c | 3 +++ .../addGlob_too_long_add_path_option.phpt | 21 +++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 ext/zip/tests/addGlob_too_long_add_path_option.phpt diff --git a/NEWS b/NEWS index a3f2990962d42..22ce2f45ecca0 100644 --- a/NEWS +++ b/NEWS @@ -45,6 +45,8 @@ PHP NEWS - Zip: . Fix uouv when handling empty options in ZipArchive::addGlob(). (nielsdos) + . Fix memory leak when handling a too long path in ZipArchive::addGlob(). + (nielsdos) 10 Apr 2025, PHP 8.3.20 diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index be096ad56e29d..45b83aeae63bc 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -1796,6 +1796,9 @@ static void php_zip_add_from_pattern(INTERNAL_FUNCTION_PARAMETERS, int type) /* if (opts.add_path) { if ((opts.add_path_len + file_stripped_len) > MAXPATHLEN) { + if (basename) { + zend_string_release_ex(basename, 0); + } php_error_docref(NULL, E_WARNING, "Entry name too long (max: %d, %zd given)", MAXPATHLEN - 1, (opts.add_path_len + file_stripped_len)); zend_array_destroy(Z_ARR_P(return_value)); diff --git a/ext/zip/tests/addGlob_too_long_add_path_option.phpt b/ext/zip/tests/addGlob_too_long_add_path_option.phpt new file mode 100644 index 0000000000000..9598eeca40a89 --- /dev/null +++ b/ext/zip/tests/addGlob_too_long_add_path_option.phpt @@ -0,0 +1,21 @@ +--TEST-- +addGlob with too long add_path option +--EXTENSIONS-- +zip +--FILE-- +open($file, ZipArchive::CREATE | ZipArchive::OVERWRITE); +$zip->addGlob(__FILE__, 0, ['add_path' => str_repeat('A', PHP_MAXPATHLEN - 2)]); +$zip->close(); + +?> +--CLEAN-- + +--EXPECTF-- +Warning: ZipArchive::addGlob(): Entry name too long (max: %d, %d given) in %s on line %d From c905d5910645b6024faee6ce791ab884e739c767 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 16 Apr 2025 00:09:35 +0200 Subject: [PATCH 44/53] Fix NULL deref on high modification key We should re-index in the loop. Closes GH-18331. --- NEWS | 1 + ext/ldap/ldap.c | 8 +++++--- ext/ldap/tests/ldap_modify_batch_error.phpt | 13 +++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 22ce2f45ecca0..ac31131ea9d22 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,7 @@ PHP NEWS - LDAP: . Fixed bug GH-17776 (LDAP_OPT_X_TLS_* options can't be overridden). (Remi) + . Fix NULL deref on high modification key. (nielsdos) - libxml: . Fixed custom external entity loader returning an invalid resource leading diff --git a/ext/ldap/ldap.c b/ext/ldap/ldap.c index 1a878bfc58483..6c005337346b5 100644 --- a/ext/ldap/ldap.c +++ b/ext/ldap/ldap.c @@ -2785,12 +2785,12 @@ PHP_FUNCTION(ldap_modify_batch) ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0); /* for each modification */ - for (i = 0; i < num_mods; i++) { + i = 0; + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(mods), fetched) { /* allocate the modification struct */ ldap_mods[i] = safe_emalloc(1, sizeof(LDAPMod), 0); /* fetch the relevant data */ - fetched = zend_hash_index_find(Z_ARRVAL_P(mods), i); mod = fetched; _ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_ATTRIB, &attrib); @@ -2855,7 +2855,9 @@ PHP_FUNCTION(ldap_modify_batch) /* NULL-terminate values */ ldap_mods[i]->mod_bvalues[num_modvals] = NULL; } - } + + i++; + } ZEND_HASH_FOREACH_END(); /* NULL-terminate modifications */ ldap_mods[num_mods] = NULL; diff --git a/ext/ldap/tests/ldap_modify_batch_error.phpt b/ext/ldap/tests/ldap_modify_batch_error.phpt index bce62cafb2791..0ac093b4a0341 100644 --- a/ext/ldap/tests/ldap_modify_batch_error.phpt +++ b/ext/ldap/tests/ldap_modify_batch_error.phpt @@ -59,6 +59,16 @@ $mods = array( ) ); +var_dump(ldap_modify_batch($link, "dc=my-domain,$base", $mods)); + +// high key with invalid attribute type +$mods = [ + 99999 => [ + "attrib" => "weirdAttribute", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => ["value1"], + ], +]; var_dump(ldap_modify_batch($link, "dc=my-domain,$base", $mods)); ?> --CLEAN-- @@ -81,3 +91,6 @@ bool(false) Warning: ldap_modify_batch(): Batch Modify: Undefined attribute type in %s on line %d bool(false) + +Warning: ldap_modify_batch(): Batch Modify: Undefined attribute type in %s on line %d +bool(false) From ce7304f909396d623c4428c30fb1ad25c42d084d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 16 Apr 2025 00:23:48 +0200 Subject: [PATCH 45/53] Fix memory leak on error return of collation callback in pdo_sqlite We should destroy it when it's not IS_LONG, not when it's IS_LONG. Closes GH-18332. --- NEWS | 3 +++ ext/pdo_sqlite/pdo_sqlite.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 08a1bb902a33b..cd9bb021b9706 100644 --- a/NEWS +++ b/NEWS @@ -45,6 +45,9 @@ PHP NEWS . Fixed GH-18276 - persistent connection - "zend_mm_heap corrupted" with setAttribute() (SakiTakamachi). +- PDO Sqlite: + . Fix memory leak on error return of collation callback. (nielsdos) + - SPL: . Fixed bug GH-18322 (SplObjectStorage debug handler mismanages memory). (nielsdos) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index bc47c15a1eb5e..ff56d04049424 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -352,6 +352,7 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v zend_type_error("%s(): Return value of the callback must be of type int, %s returned", ZSTR_VAL(func_name), zend_zval_value_name(&retval)); zend_string_release(func_name); + zval_ptr_dtor(&retval); return FAILURE; } if (Z_LVAL(retval) > 0) { @@ -359,7 +360,6 @@ static int php_sqlite_collation_callback(void *context, int string1_len, const v } else if (Z_LVAL(retval) < 0) { ret = -1; } - zval_ptr_dtor(&retval); } zval_ptr_dtor(&zargs[0]); From 685baf77df39083c033addf09106716989a1e69e Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Sat, 19 Apr 2025 14:11:20 +0900 Subject: [PATCH 46/53] Fixed GH-17383 - pdo_firebird: PDOException has wrong code and message since PHP 8.4 (#18072) Closes #18072 Fixes #17383 --- .github/scripts/windows/test_task.bat | 5 +++- NEWS | 6 +++-- ext/pdo_firebird/firebird_driver.c | 3 ++- ext/pdo_firebird/tests/gh17383.phpt | 38 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 ext/pdo_firebird/tests/gh17383.phpt diff --git a/.github/scripts/windows/test_task.bat b/.github/scripts/windows/test_task.bat index 124b9b019ca4b..223d654300357 100644 --- a/.github/scripts/windows/test_task.bat +++ b/.github/scripts/windows/test_task.bat @@ -58,12 +58,15 @@ if "%PLATFORM%" == "x64" ( curl -sLo Firebird.zip %PHP_FIREBIRD_DOWNLOAD_URL% 7z x -oC:\Firebird Firebird.zip set PDO_FIREBIRD_TEST_DATABASE=C:\test.fdb -set PDO_FIREBIRD_TEST_DSN=firebird:dbname=%PDO_FIREBIRD_TEST_DATABASE% +set PDO_FIREBIRD_TEST_DSN=firebird:dbname=127.0.0.1:%PDO_FIREBIRD_TEST_DATABASE% set PDO_FIREBIRD_TEST_USER=SYSDBA set PDO_FIREBIRD_TEST_PASS=phpfi +echo create user %PDO_FIREBIRD_TEST_USER% password '%PDO_FIREBIRD_TEST_PASS%';> C:\Firebird\create_user.sql +echo commit;>> C:\Firebird\create_user.sql echo create database '%PDO_FIREBIRD_TEST_DATABASE%' user '%PDO_FIREBIRD_TEST_USER%' password '%PDO_FIREBIRD_TEST_PASS%';> C:\Firebird\setup.sql C:\Firebird\instsvc.exe install -n TestInstance C:\Firebird\isql -q -i C:\Firebird\setup.sql +C:\Firebird\isql -q -i C:\Firebird\create_user.sql -user sysdba %PDO_FIREBIRD_TEST_DATABASE% C:\Firebird\instsvc.exe start -n TestInstance if %errorlevel% neq 0 exit /b 3 path C:\Firebird;%PATH% diff --git a/NEWS b/NEWS index cd9bb021b9706..c3a3a69fd0867 100644 --- a/NEWS +++ b/NEWS @@ -42,8 +42,10 @@ PHP NEWS . Fix potential leaks when writing to BIO fails. (nielsdos) - PDO Firebird: - . Fixed GH-18276 - persistent connection - "zend_mm_heap corrupted" - with setAttribute() (SakiTakamachi). + . Fixed bug GH-18276 (persistent connection - "zend_mm_heap corrupted" + with setAttribute()) (SakiTakamachi). + . Fixed bug GH-17383 (PDOException has wrong code and message since PHP 8.4) + (SakiTakamachi). - PDO Sqlite: . Fix memory leak on error return of collation callback. (nielsdos) diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c index 367d6a746b3ae..259ed53af98a8 100644 --- a/ext/pdo_firebird/firebird_driver.c +++ b/ext/pdo_firebird/firebird_driver.c @@ -594,7 +594,8 @@ static void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */ } H->in_manually_txn = 0; - if (isc_detach_database(H->isc_status, &H->db)) { + /* isc_detach_database returns 0 on success, 1 on failure. */ + if (H->db && isc_detach_database(H->isc_status, &H->db)) { php_firebird_error(dbh); } diff --git a/ext/pdo_firebird/tests/gh17383.phpt b/ext/pdo_firebird/tests/gh17383.phpt new file mode 100644 index 0000000000000..6adad311938c6 --- /dev/null +++ b/ext/pdo_firebird/tests/gh17383.phpt @@ -0,0 +1,38 @@ +--TEST-- +GH-17383 (PDOException has wrong code and message since PHP 8.4) +--EXTENSIONS-- +pdo_firebird +--SKIPIF-- + +--XLEAK-- +A bug in firebird causes a memory leak when calling `isc_attach_database()`. +See https://github.com/FirebirdSQL/firebird/issues/7849 +--FILE-- +getCode() . "\n"; + echo 'PDOException message: ' . $e->getMessage() . "\n"; + echo "\n"; + } +} +?> +--EXPECT-- +PDOException code: 335544721 +PDOException message: SQLSTATE[HY000] [335544721] Unable to complete network request to host "invalid_host". + +PDOException code: 335544472 +PDOException message: SQLSTATE[HY000] [335544472] Your user name and password are not defined. Ask your database administrator to set up a Firebird login. + +PDOException code: 335544472 +PDOException message: SQLSTATE[HY000] [335544472] Your user name and password are not defined. Ask your database administrator to set up a Firebird login. From bf4b470098122953e47aed6b56ea8a96570e1680 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 20 Apr 2025 12:25:45 +0200 Subject: [PATCH 47/53] Fix reference support for intltz_get_offset() It should use the helper macros such that types are properly respected. Closes GH-18364. --- NEWS | 3 +++ .../tests/intltz_get_offset_references.phpt | 26 +++++++++++++++++++ ext/intl/timezone/timezone_methods.cpp | 8 +++--- 3 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 ext/intl/tests/intltz_get_offset_references.phpt diff --git a/NEWS b/NEWS index ac31131ea9d22..55a58a4d8e78f 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,9 @@ PHP NEWS . Fixed GH-18243 imagettftext() overflow/underflow on font size value. (David Carlier) +- Intl: + . Fix reference support for intltz_get_offset(). (nielsdos) + - LDAP: . Fixed bug GH-17776 (LDAP_OPT_X_TLS_* options can't be overridden). (Remi) . Fix NULL deref on high modification key. (nielsdos) diff --git a/ext/intl/tests/intltz_get_offset_references.phpt b/ext/intl/tests/intltz_get_offset_references.phpt new file mode 100644 index 0000000000000..6f104863d2212 --- /dev/null +++ b/ext/intl/tests/intltz_get_offset_references.phpt @@ -0,0 +1,26 @@ +--TEST-- +intltz_get_offset references +--EXTENSIONS-- +intl +--FILE-- +a = $test->b = "hello"; + +$rawOffset =& $test->a; +$dstOffset =& $test->b; +intltz_get_offset($tz, 0.0, true, $rawOffset, $dstOffset); +var_dump($test); +?> +--EXPECT-- +object(Test)#2 (2) { + ["a"]=> + &string(7) "3600000" + ["b"]=> + &string(1) "0" +} diff --git a/ext/intl/timezone/timezone_methods.cpp b/ext/intl/timezone/timezone_methods.cpp index cae64026fa5db..580a721e79ec1 100644 --- a/ext/intl/timezone/timezone_methods.cpp +++ b/ext/intl/timezone/timezone_methods.cpp @@ -419,7 +419,7 @@ U_CFUNC PHP_FUNCTION(intltz_get_offset) TIMEZONE_METHOD_INIT_VARS; if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), - "Odbz/z/", &object, TimeZone_ce_ptr, &date, &local, &rawOffsetArg, + "Odbzz", &object, TimeZone_ce_ptr, &date, &local, &rawOffsetArg, &dstOffsetArg) == FAILURE) { RETURN_THROWS(); } @@ -431,10 +431,8 @@ U_CFUNC PHP_FUNCTION(intltz_get_offset) INTL_METHOD_CHECK_STATUS(to, "intltz_get_offset: error obtaining offset"); - zval_ptr_dtor(rawOffsetArg); - ZVAL_LONG(rawOffsetArg, rawOffset); - zval_ptr_dtor(dstOffsetArg); - ZVAL_LONG(dstOffsetArg, dstOffset); + ZEND_TRY_ASSIGN_REF_LONG(rawOffsetArg, rawOffset); + ZEND_TRY_ASSIGN_REF_LONG(dstOffsetArg, dstOffset); RETURN_TRUE; } From 1a1a83f1fc7dd5d63da57b06922b692da65cd5c1 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 14 Apr 2025 07:18:31 -0700 Subject: [PATCH 48/53] Fix GH-18136: tracing JIT floating point register clobbering on Windows and ARM64 On win64, xmm6-xmm15 are preserved registers, but the prologues and epilogues of JITted code don't handle these. The issue occurs when calling into the JIT code again via an internal handler (like call_user_func). Therefore, we want to save/restore xmm registers upon entering/leaving execute_ex. Since MSVC x64 does not support inline assembly, we create an assembly wrapper around the real execute_ex function. The alternative is to always save/restore these xmm registers into the fixed call frame, but this causes unnecessary overhead. The same issue occurs for ARM64 platforms for floating point register 8 to 15. However, there we can use inline asm to fix this. Closes GH-18352. --- NEWS | 2 ++ Zend/asm/save_xmm_x86_64_ms_masm.asm | 43 ++++++++++++++++++++++++++++ Zend/zend_vm_execute.h | 9 ++++++ Zend/zend_vm_execute.skl | 9 ++++++ ext/opcache/tests/jit/gh18136.phpt | 35 ++++++++++++++++++++++ win32/build/config.w32 | 11 ++++++- 6 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 Zend/asm/save_xmm_x86_64_ms_masm.asm create mode 100644 ext/opcache/tests/jit/gh18136.phpt diff --git a/NEWS b/NEWS index cab0e696f2a6f..720f0bbc6bcc7 100644 --- a/NEWS +++ b/NEWS @@ -38,6 +38,8 @@ PHP NEWS - Opcache: . Fixed bug GH-18294 (assertion failure zend_jit_ir.c). (nielsdos) . Fixed bug GH-18289 (Fix segfault in JIT). (Florian Engelhardt) + . Fixed bug GH-18136 (tracing JIT floating point register clobbering on + Windows and ARM64). (nielsdos) - OpenSSL: . Fix memory leak in openssl_sign() when passing invalid algorithm. diff --git a/Zend/asm/save_xmm_x86_64_ms_masm.asm b/Zend/asm/save_xmm_x86_64_ms_masm.asm new file mode 100644 index 0000000000000..1569d6bdb0e86 --- /dev/null +++ b/Zend/asm/save_xmm_x86_64_ms_masm.asm @@ -0,0 +1,43 @@ +.code + +; ZEND_API void execute_ex(zend_execute_data *ex) +PUBLIC execute_ex + +EXTERN execute_ex_real:PROC + +; Assembly wrapper around the real execute_ex function, so that we can +; save the preserved registers when re-entering the VM from JIT code. +; See GH-18136. +execute_ex PROC EXPORT FRAME + ; 10 floating points numbers + ; 32 bytes shadow space + ; 8 bytes to align after the return address + sub rsp, 8*10 + 32 + 8 + .allocstack 8*10 + 32 + 8 + .endprolog + movsd qword ptr [rsp + 32 + 8*0], xmm6 + movsd qword ptr [rsp + 32 + 8*1], xmm7 + movsd qword ptr [rsp + 32 + 8*2], xmm8 + movsd qword ptr [rsp + 32 + 8*3], xmm9 + movsd qword ptr [rsp + 32 + 8*4], xmm10 + movsd qword ptr [rsp + 32 + 8*5], xmm11 + movsd qword ptr [rsp + 32 + 8*6], xmm12 + movsd qword ptr [rsp + 32 + 8*7], xmm13 + movsd qword ptr [rsp + 32 + 8*8], xmm14 + movsd qword ptr [rsp + 32 + 8*9], xmm15 + call execute_ex_real + movsd xmm6, qword ptr [rsp + 32 + 8*0] + movsd xmm7, qword ptr [rsp + 32 + 8*1] + movsd xmm8, qword ptr [rsp + 32 + 8*2] + movsd xmm9, qword ptr [rsp + 32 + 8*3] + movsd xmm10, qword ptr [rsp + 32 + 8*4] + movsd xmm11, qword ptr [rsp + 32 + 8*5] + movsd xmm12, qword ptr [rsp + 32 + 8*6] + movsd xmm13, qword ptr [rsp + 32 + 8*7] + movsd xmm14, qword ptr [rsp + 32 + 8*8] + movsd xmm15, qword ptr [rsp + 32 + 8*9] + add rsp, 8*10 + 32 + 8 + ret +execute_ex ENDP + +END diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 5270fc841cd8d..dadc57d13f278 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -55037,10 +55037,19 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_NULL_HANDLER(ZEND_OPCODE_HANDL # pragma GCC optimize("no-gcse") # pragma GCC optimize("no-ivopts") #endif +#ifdef _WIN64 +/* See save_xmm_x86_64_ms_masm.asm */ +void execute_ex_real(zend_execute_data *ex) +#else ZEND_API void execute_ex(zend_execute_data *ex) +#endif { DCL_OPLINE +#if defined(__GNUC__) && defined(__aarch64__) + __asm__ __volatile__ (""::: "v8","v9","v10","v11","v12","v13","v14","v15"); +#endif + #if defined(ZEND_VM_IP_GLOBAL_REG) || defined(ZEND_VM_FP_GLOBAL_REG) struct { #ifdef ZEND_VM_HYBRID_JIT_RED_ZONE_SIZE diff --git a/Zend/zend_vm_execute.skl b/Zend/zend_vm_execute.skl index 717d4ffd3e8af..5b4799cd67c2a 100644 --- a/Zend/zend_vm_execute.skl +++ b/Zend/zend_vm_execute.skl @@ -5,10 +5,19 @@ # pragma GCC optimize("no-gcse") # pragma GCC optimize("no-ivopts") #endif +#ifdef _WIN64 +/* See save_xmm_x86_64_ms_masm.asm */ +void {%EXECUTOR_NAME%}_ex_real(zend_execute_data *ex) +#else ZEND_API void {%EXECUTOR_NAME%}_ex(zend_execute_data *ex) +#endif { DCL_OPLINE +#if defined(__GNUC__) && defined(__aarch64__) + __asm__ __volatile__ (""::: "v8","v9","v10","v11","v12","v13","v14","v15"); +#endif + {%HELPER_VARS%} {%INTERNAL_LABELS%} diff --git a/ext/opcache/tests/jit/gh18136.phpt b/ext/opcache/tests/jit/gh18136.phpt new file mode 100644 index 0000000000000..e1993440003e5 --- /dev/null +++ b/ext/opcache/tests/jit/gh18136.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-18136 (tracing JIT floating point register clobbering on Windows and ARM64) +--EXTENSIONS-- +opcache +--INI-- +opcache.jit=tracing +opcache.jit_buffer_size=64M +opcache.jit_hot_func=4 +opcache.jit_hot_loop=4 +--FILE-- + +--EXPECT-- +float(-347.3205211468715) diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 043f18b275b9d..7180e841ebc48 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -267,7 +267,11 @@ if (TARGET_ARCH == 'arm64') { DEFINE('FIBER_ASM_FLAGS', '/DBOOST_CONTEXT_EXPORT=EXPORT /nologo /c /Fo'); } -ADD_FLAG('ASM_OBJS', '$(BUILD_DIR)\\Zend\\jump_' + FIBER_ASM_ABI + '.obj $(BUILD_DIR)\\Zend\\make_' + FIBER_ASM_ABI + '.obj'); +var all_asm_objs = '$(BUILD_DIR)\\Zend\\jump_' + FIBER_ASM_ABI + '.obj $(BUILD_DIR)\\Zend\\make_' + FIBER_ASM_ABI + '.obj'; +if (TARGET_ARCH == 'x64') { + all_asm_objs += ' $(BUILD_DIR)\\Zend\\save_xmm_x86_64_ms_masm.obj'; +} +ADD_FLAG('ASM_OBJS', all_asm_objs); MFO.WriteLine('$(BUILD_DIR)\\Zend\\jump_' + FIBER_ASM_ABI + '.obj: Zend\\asm\\jump_' + FIBER_ASM_ABI + '.asm'); MFO.WriteLine('\t$(PHP_ASSEMBLER) $(FIBER_ASM_FLAGS) $(BUILD_DIR)\\Zend\\jump_$(FIBER_ASM_ABI).obj Zend\\asm\\jump_$(FIBER_ASM_ABI).asm'); @@ -275,6 +279,11 @@ MFO.WriteLine('\t$(PHP_ASSEMBLER) $(FIBER_ASM_FLAGS) $(BUILD_DIR)\\Zend\\jump_$( MFO.WriteLine('$(BUILD_DIR)\\Zend\\make_' + FIBER_ASM_ABI + '.obj: Zend\\asm\\make_' + FIBER_ASM_ABI + '.asm'); MFO.WriteLine('\t$(PHP_ASSEMBLER) $(FIBER_ASM_FLAGS) $(BUILD_DIR)\\Zend\\make_$(FIBER_ASM_ABI).obj Zend\\asm\\make_$(FIBER_ASM_ABI).asm'); +if (TARGET_ARCH == 'x64') { + MFO.WriteLine('$(BUILD_DIR)\\Zend\\save_xmm_x86_64_ms_masm.obj: Zend\\asm\\save_xmm_x86_64_ms_masm.asm'); + MFO.WriteLine('\t$(PHP_ASSEMBLER) $(FIBER_ASM_FLAGS) $(BUILD_DIR)\\Zend\\save_xmm_x86_64_ms_masm.obj Zend\\asm\\save_xmm_x86_64_ms_masm.asm'); +} + ADD_FLAG("CFLAGS_BD_ZEND", "/D ZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); if (VS_TOOLSET && VCVERS >= 1914) { ADD_FLAG("CFLAGS_BD_ZEND", "/d2FuncCache1"); From 81d9a27c47b12ad20e180afb09c6188c8d403f9f Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 20 Apr 2025 23:59:12 +0200 Subject: [PATCH 49/53] Fix some leaks in php_scandir Closes GH-18371. --- NEWS | 1 + main/php_scandir.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 55a58a4d8e78f..c24fefcc8c6ed 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,7 @@ PHP NEWS - Core: . Fixed bug GH-18304 (Changing the properties of a DateInterval through dynamic properties triggers a SegFault). (nielsdos) + . Fix some leaks in php_scandir. (nielsdos) - Filter: . Fixed bug GH-18309 (ipv6 filter integer overflow). (nielsdos) diff --git a/main/php_scandir.c b/main/php_scandir.c index 7d1eb36023665..7bf91bdf7f33d 100644 --- a/main/php_scandir.c +++ b/main/php_scandir.c @@ -83,7 +83,7 @@ PHPAPI int php_scandir(const char *dirname, struct dirent **namelist[], int (*se newv = (struct dirent **) realloc (vector, vector_size * sizeof (struct dirent *)); if (!newv) { - return -1; + goto fail; } vector = newv; } @@ -113,6 +113,7 @@ PHPAPI int php_scandir(const char *dirname, struct dirent **namelist[], int (*se free(vector[nfiles]); } free(vector); + closedir(dirp); return -1; } #endif From 4621423e5e3c303fac87f3c16b2679fbc62ebe3d Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 21 Apr 2025 15:54:29 +0200 Subject: [PATCH 50/53] Fix uouv in pg_put_copy_end() Closes GH-18383. --- NEWS | 3 +++ ext/pgsql/pgsql.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 0790cae953092..efb76ac127a60 100644 --- a/NEWS +++ b/NEWS @@ -56,6 +56,9 @@ PHP NEWS - PDO Sqlite: . Fix memory leak on error return of collation callback. (nielsdos) +- PgSql: + . Fix uouv in pg_put_copy_end(). (nielsdos) + - SPL: . Fixed bug GH-18322 (SplObjectStorage debug handler mismanages memory). (nielsdos) diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index c0d4a5117aefc..75b987d6b917e 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -6215,7 +6215,7 @@ PHP_FUNCTION(pg_put_copy_end) { zval *pgsql_link; pgsql_link_handle *link; - zend_string *error; + zend_string *error = NULL; char *err = NULL; ZEND_PARSE_PARAMETERS_START(1, 2) From bbd9732f838b5c1425df4f46823c7c69ed7b6cd0 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 30 Apr 2025 20:52:56 +0200 Subject: [PATCH 51/53] Revert "Fix infinite recursion on deprecated attribute evaluation" This reverts commit 272f7f75e2d4ff25d4dc00b0102c39fdb4f9c573. Reverts GH-17712 for the PHP-8.4 branch. This will be reapplied later with a fix for GH-18463 (GH-18464). --- .../deprecated/class_constants/gh17711.phpt | 28 ------------------- Zend/zend_API.c | 2 +- Zend/zend_compile.c | 4 --- Zend/zend_constants.c | 4 +-- Zend/zend_constants.h | 11 -------- Zend/zend_vm_def.h | 4 +-- Zend/zend_vm_execute.h | 24 ++++------------ ext/opcache/ZendAccelerator.c | 5 ---- 8 files changed, 9 insertions(+), 73 deletions(-) delete mode 100644 Zend/tests/attributes/deprecated/class_constants/gh17711.phpt diff --git a/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt b/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt deleted file mode 100644 index abec209343ab3..0000000000000 --- a/Zend/tests/attributes/deprecated/class_constants/gh17711.phpt +++ /dev/null @@ -1,28 +0,0 @@ ---TEST-- -GH-17711: Infinite recursion through deprecated class constants self-referencing through deprecation message ---FILE-- - ---EXPECTF-- -Deprecated: Constant C::C is deprecated, Message in %s on line %d -string(7) "Message" - -Deprecated: Constant D::C is deprecated, test in %s on line %d -string(4) "test" diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 6e254561d3745..5aac3c1f7d77c 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1439,7 +1439,7 @@ ZEND_API HashTable *zend_separate_class_constants_table(zend_class_entry *class_ ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&class_type->constants_table, key, c) { if (c->ce == class_type) { - if (Z_TYPE(c->value) == IS_CONSTANT_AST || (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) { + if (Z_TYPE(c->value) == IS_CONSTANT_AST) { new_c = zend_arena_alloc(&CG(arena), sizeof(zend_class_constant)); memcpy(new_c, c, sizeof(zend_class_constant)); c = new_c; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 195c65d504374..832dedc421042 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8822,10 +8822,6 @@ static void zend_compile_class_const_decl(zend_ast *ast, uint32_t flags, zend_as if (deprecated) { ZEND_CLASS_CONST_FLAGS(c) |= ZEND_ACC_DEPRECATED; - /* For deprecated constants, we need to flag the zval for recursion - * detection. Make sure the zval is separated out of shm. */ - ce->ce_flags |= ZEND_ACC_HAS_AST_CONSTANTS; - ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; } } } diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 2145cb1915354..d453b8bb73717 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -353,10 +353,8 @@ ZEND_API zval *zend_get_class_constant_ex(zend_string *class_name, zend_string * } if (UNEXPECTED(ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED)) { - if ((flags & ZEND_FETCH_CLASS_SILENT) == 0 && !CONST_IS_RECURSIVE(c)) { - CONST_PROTECT_RECURSION(c); + if ((flags & ZEND_FETCH_CLASS_SILENT) == 0) { zend_deprecated_class_constant(c, constant_name); - CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { goto failure; } diff --git a/Zend/zend_constants.h b/Zend/zend_constants.h index bd759c2891500..6f0710c0ce63e 100644 --- a/Zend/zend_constants.h +++ b/Zend/zend_constants.h @@ -27,17 +27,6 @@ #define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */ #define CONST_DEPRECATED (1<<2) /* Deprecated */ #define CONST_OWNED (1<<3) /* constant should be destroyed together with class */ -#define CONST_RECURSIVE (1<<4) /* Recursion protection for constant evaluation */ - -#define CONST_IS_RECURSIVE(c) (Z_CONSTANT_FLAGS((c)->value) & CONST_RECURSIVE) -#define CONST_PROTECT_RECURSION(c) \ - do { \ - Z_CONSTANT_FLAGS((c)->value) |= CONST_RECURSIVE; \ - } while (0) -#define CONST_UNPROTECT_RECURSION(c) \ - do { \ - Z_CONSTANT_FLAGS((c)->value) &= ~CONST_RECURSIVE; \ - } while (0) #define PHP_USER_CONSTANT 0x7fffff /* a constant defined in user space */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 37e0e0fb43d95..a4fa8063a5bd4 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -6094,10 +6094,8 @@ ZEND_VM_HANDLER(181, ZEND_FETCH_CLASS_CONSTANT, VAR|CONST|UNUSED|CLASS_FETCH, CO } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { - CONST_PROTECT_RECURSION(c); + if (UNEXPECTED(is_constant_deprecated)) { zend_deprecated_class_constant(c, constant_name); - CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index dadc57d13f278..a8bf9da828593 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -7615,10 +7615,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { - CONST_PROTECT_RECURSION(c); + if (UNEXPECTED(is_constant_deprecated)) { zend_deprecated_class_constant(c, constant_name); - CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -8777,10 +8775,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_CONS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { - CONST_PROTECT_RECURSION(c); + if (UNEXPECTED(is_constant_deprecated)) { zend_deprecated_class_constant(c, constant_name); - CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -25878,10 +25874,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { - CONST_PROTECT_RECURSION(c); + if (UNEXPECTED(is_constant_deprecated)) { zend_deprecated_class_constant(c, constant_name); - CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -26449,10 +26443,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_VAR_ } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { - CONST_PROTECT_RECURSION(c); + if (UNEXPECTED(is_constant_deprecated)) { zend_deprecated_class_constant(c, constant_name); - CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35290,10 +35282,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { - CONST_PROTECT_RECURSION(c); + if (UNEXPECTED(is_constant_deprecated)) { zend_deprecated_class_constant(c, constant_name); - CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); @@ -35651,10 +35641,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FETCH_CLASS_CONSTANT_SPEC_UNUS } bool is_constant_deprecated = ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED; - if (UNEXPECTED(is_constant_deprecated) && !CONST_IS_RECURSIVE(c)) { - CONST_PROTECT_RECURSION(c); + if (UNEXPECTED(is_constant_deprecated)) { zend_deprecated_class_constant(c, constant_name); - CONST_UNPROTECT_RECURSION(c); if (EG(exception)) { ZVAL_UNDEF(EX_VAR(opline->result.var)); diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index f71f43b330ffa..459449d85f23c 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -3800,11 +3800,6 @@ static bool preload_try_resolve_constants(zend_class_entry *ce) ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->constants_table, key, c) { val = &c->value; if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - /* For deprecated constants, we need to flag the zval for recursion - * detection. Make sure the zval is separated out of shm. */ - if (ZEND_CLASS_CONST_FLAGS(c) & ZEND_ACC_DEPRECATED) { - ok = false; - } if (EXPECTED(zend_update_class_constant(c, key, c->ce) == SUCCESS)) { was_changed = changed = true; } else { From bda0939bd2e52fcac94a299110716b6804d7f31b Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Wed, 30 Apr 2025 22:38:12 +0200 Subject: [PATCH 52/53] [skip ci] Remove NEWS entry for reverted fix --- NEWS | 2 -- 1 file changed, 2 deletions(-) diff --git a/NEWS b/NEWS index efb76ac127a60..104a053697439 100644 --- a/NEWS +++ b/NEWS @@ -3,8 +3,6 @@ PHP NEWS ?? ??? ????, PHP 8.4.7 - Core: - . Fixed bug GH-17711 and GH-18022 (Infinite recursion on deprecated attribute - evaluation). (ilutov) . Fixed bug GH-18038 (Lazy proxy calls magic methods twice). (Arnaud) . Fixed bug GH-18209 (Use-after-free in extract() with EXTR_REFS). (ilutov) . Fixed bug GH-18268 (Segfault in array_walk() on object with added property From ecdb771380638fb70c1c4134f56ce74b8a986273 Mon Sep 17 00:00:00 2001 From: Saki Takamachi Date: Tue, 6 May 2025 21:31:58 +0900 Subject: [PATCH 53/53] Update versions for PHP 8.4.7 --- Zend/zend.h | 2 +- configure.ac | 2 +- main/php_version.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Zend/zend.h b/Zend/zend.h index 2e5fb5d0ef091..fb09c6bdf50e5 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -20,7 +20,7 @@ #ifndef ZEND_H #define ZEND_H -#define ZEND_VERSION "4.4.7-dev" +#define ZEND_VERSION "4.4.7" #define ZEND_ENGINE_3 diff --git a/configure.ac b/configure.ac index 0e12d55b00963..0a4056d1bc547 100644 --- a/configure.ac +++ b/configure.ac @@ -17,7 +17,7 @@ dnl Basic autoconf initialization, generation of config.nice. dnl ---------------------------------------------------------------------------- AC_PREREQ([2.68]) -AC_INIT([PHP],[8.4.7-dev],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) +AC_INIT([PHP],[8.4.7],[https://github.com/php/php-src/issues],[php],[https://www.php.net]) AC_CONFIG_SRCDIR([main/php_version.h]) AC_CONFIG_AUX_DIR([build]) AC_PRESERVE_HELP_ORDER diff --git a/main/php_version.h b/main/php_version.h index 414e0e8f5a68a..aa5164884fc24 100644 --- a/main/php_version.h +++ b/main/php_version.h @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 8 #define PHP_MINOR_VERSION 4 #define PHP_RELEASE_VERSION 7 -#define PHP_EXTRA_VERSION "-dev" -#define PHP_VERSION "8.4.7-dev" +#define PHP_EXTRA_VERSION "" +#define PHP_VERSION "8.4.7" #define PHP_VERSION_ID 80407