From 43970e3c860b0f11d4eade16af75b08e563aaf63 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 14 Oct 2025 18:13:15 +0200 Subject: [PATCH 1/9] Fix missing libctx in x509 info read bio --- ext/openssl/openssl_backend_common.c | 2 +- ext/openssl/openssl_backend_v1.c | 5 +++++ ext/openssl/openssl_backend_v3.c | 5 +++++ ext/openssl/php_openssl_backend.h | 1 + 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index ea52531bde45..c21e64a1306e 100644 --- a/ext/openssl/openssl_backend_common.c +++ b/ext/openssl/openssl_backend_common.c @@ -711,7 +711,7 @@ STACK_OF(X509) *php_openssl_load_all_certs_from_file( } /* This loads from a file, a stack of x509/crl/pkey sets */ - if (!(sk = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL))) { + if (!(sk = php_openssl_pem_read_bio_x509_info(in))) { php_openssl_store_errors(); php_error_docref(NULL, E_WARNING, "Error reading the file, %s", cert_path); sk_X509_free(stack); diff --git a/ext/openssl/openssl_backend_v1.c b/ext/openssl/openssl_backend_v1.c index e95f51905f69..873464574fb2 100644 --- a/ext/openssl/openssl_backend_v1.c +++ b/ext/openssl/openssl_backend_v1.c @@ -723,6 +723,11 @@ X509_REQ *php_openssl_pem_read_bio_x509_req(BIO *in) return PEM_read_bio_X509_REQ(in, NULL, NULL, NULL); } +STACK_OF(X509_INFO) *php_openssl_pem_read_bio_x509_info(BIO *in) +{ + return PEM_X509_INFO_read_bio(in, NULL, NULL, NULL); +} + EVP_PKEY *php_openssl_pem_read_bio_public_key(BIO *in) { return PEM_read_bio_PUBKEY(in, NULL, NULL, NULL); diff --git a/ext/openssl/openssl_backend_v3.c b/ext/openssl/openssl_backend_v3.c index e037d9e13f58..cd190546d500 100644 --- a/ext/openssl/openssl_backend_v3.c +++ b/ext/openssl/openssl_backend_v3.c @@ -903,6 +903,11 @@ X509_REQ *php_openssl_pem_read_bio_x509_req(BIO *in) return xr; } +STACK_OF(X509_INFO) *php_openssl_pem_read_bio_x509_info(BIO *in) +{ + return PEM_X509_INFO_read_bio_ex(in, NULL, NULL, NULL, PHP_OPENSSL_LIBCTX, PHP_OPENSSL_PROPQ); +} + EVP_PKEY *php_openssl_pem_read_bio_public_key(BIO *in) { return PEM_read_bio_PUBKEY_ex(in, NULL, NULL, NULL, PHP_OPENSSL_LIBCTX, PHP_OPENSSL_PROPQ); diff --git a/ext/openssl/php_openssl_backend.h b/ext/openssl/php_openssl_backend.h index 35af52c6c832..00da5e74fc1b 100644 --- a/ext/openssl/php_openssl_backend.h +++ b/ext/openssl/php_openssl_backend.h @@ -195,6 +195,7 @@ zend_string *php_openssl_pkey_derive(EVP_PKEY *key, EVP_PKEY *peer_key, size_t r X509 *php_openssl_pem_read_asn1_bio_x509(BIO *in); X509 *php_openssl_pem_read_bio_x509(BIO *in); X509_REQ *php_openssl_pem_read_bio_x509_req(BIO *in); +STACK_OF(X509_INFO) *php_openssl_pem_read_bio_x509_info(BIO *in); EVP_PKEY *php_openssl_pem_read_bio_public_key(BIO *in); EVP_PKEY *php_openssl_pem_read_bio_private_key(BIO *in, pem_password_cb *cb, void *u); PKCS7 *php_openssl_pem_read_bio_pkcs7(BIO *in); From 44aea65486eb74640c35fa83da7e2b1a022cbf8e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 14 Oct 2025 18:48:10 +0200 Subject: [PATCH 2/9] phar: Change hacky check into an exception (#20160) phar_metadata_tracker_unserialize_or_copy() is only ever preceded by phar_metadata_tracker_has_data() which won't throw. This check is quite hacky and it should always be the responsibility of the caller to avoid calling this, even the comment acknowledges this is a hack. --- ext/phar/phar.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ext/phar/phar.c b/ext/phar/phar.c index d2c25dfbdb71..6d408708fc24 100644 --- a/ext/phar/phar.c +++ b/ext/phar/phar.c @@ -595,13 +595,9 @@ zend_result phar_metadata_tracker_unserialize_or_copy(phar_metadata_tracker *tra const bool has_unserialize_options = unserialize_options != NULL && zend_hash_num_elements(unserialize_options) > 0; /* It should be impossible to create a zval in a persistent phar/entry. */ ZEND_ASSERT(!persistent || Z_ISUNDEF(tracker->val)); + ZEND_ASSERT(!EG(exception)); if (Z_ISUNDEF(tracker->val) || has_unserialize_options) { - if (EG(exception)) { - /* Because other parts of the phar code haven't been updated to check for exceptions after doing something that may throw, - * check for exceptions before potentially serializing/unserializing instead. */ - return FAILURE; - } /* Persistent phars should always be unserialized. */ const char *start; /* Assert it should not be possible to create raw data in a persistent phar (i.e. from cache_list) */ From 2e4dca57dc7256387ecb09b136f83283b8728af0 Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 14 Oct 2025 19:20:25 +0200 Subject: [PATCH 3/9] Fix GH-19994: openssl_get_cipher_methods inconsistent with fetched methods (#20167) This is fixed by libctx work so this just adds test to confirm it. The actual fix is 2f5ef4d2b729227f5847dd36faba94ee430cd096 . --- ext/openssl/tests/gh19994.phpt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 ext/openssl/tests/gh19994.phpt diff --git a/ext/openssl/tests/gh19994.phpt b/ext/openssl/tests/gh19994.phpt new file mode 100644 index 000000000000..3aa32cfb4d2b --- /dev/null +++ b/ext/openssl/tests/gh19994.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-19994: openssl_get_cipher_methods inconsistent with fetched methods +--EXTENSIONS-- +openssl +--FILE-- + +--EXPECT-- From f60134a3c96d2d373eaa6d9ada466adf503aae8d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Tue, 14 Oct 2025 19:22:39 +0200 Subject: [PATCH 4/9] Update NEWS with info about OpenSSL GH-19994 fix --- NEWS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/NEWS b/NEWS index 742c25877ff3..51f51a4c3c7c 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,10 @@ PHP NEWS . Fixed bug GH-20121 (JIT broken in ZTS builds on MacOS 15). (Arnaud, Shivam Mathur) +- OpenSSL: + . Fixed bug GH-19994 (openssl_get_cipher_methods inconsistent with fetching). + (Jakub Zelenka) + - SPL: . Fixed bug GH-20101 (SplHeap/SplPriorityQueue serialization exposes INDIRECTs). (nielsdos) From f2f45f37f8c8f028340528100f087317a97d2a43 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 14 Oct 2025 20:14:57 +0200 Subject: [PATCH 5/9] phar: Use object_init_with_constructor where possible (#20156) * phar: Use object_init_with_constructor where possible We could also chain the exceptions, but the current code keeps the existing behaviour by adding EG(exception) checks. * Drop redundant exception throwing --- ext/phar/phar_object.c | 51 ++++++++++++------------------------------ 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 2ea701743f9f..cde10da0c941 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1711,7 +1711,7 @@ PHP_METHOD(Phar, buildFromDirectory) { char *error; bool apply_reg = false; - zval arg, arg2, iter, iteriter, regexiter; + zval iter, iteriter, regexiter; struct _phar_t pass; zend_string *dir, *regex = NULL; @@ -1727,34 +1727,16 @@ PHP_METHOD(Phar, buildFromDirectory) RETURN_THROWS(); } - if (SUCCESS != object_init_ex(&iter, spl_ce_RecursiveDirectoryIterator)) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to instantiate directory iterator for %s", phar_obj->archive->fname); - RETURN_THROWS(); - } - - ZVAL_STR(&arg, dir); - ZVAL_LONG(&arg2, SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS); + zval args[2]; + ZVAL_STR(&args[0], dir); + ZVAL_LONG(&args[1], SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS); - zend_call_known_instance_method_with_2_params(spl_ce_RecursiveDirectoryIterator->constructor, - Z_OBJ(iter), NULL, &arg, &arg2); - - if (EG(exception)) { - zval_ptr_dtor(&iter); + if (SUCCESS != object_init_with_constructor(&iter, spl_ce_RecursiveDirectoryIterator, 2, args, NULL)) { RETURN_THROWS(); } - if (SUCCESS != object_init_ex(&iteriter, spl_ce_RecursiveIteratorIterator)) { + if (SUCCESS != object_init_with_constructor(&iteriter, spl_ce_RecursiveIteratorIterator, 1, &iter, NULL)) { zval_ptr_dtor(&iter); - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to instantiate directory iterator for %s", phar_obj->archive->fname); - RETURN_THROWS(); - } - - zend_call_known_instance_method_with_1_params(spl_ce_RecursiveIteratorIterator->constructor, - Z_OBJ(iteriter), NULL, &iter); - - if (EG(exception)) { - zval_ptr_dtor(&iter); - zval_ptr_dtor(&iteriter); RETURN_THROWS(); } @@ -1763,15 +1745,13 @@ PHP_METHOD(Phar, buildFromDirectory) if (regex && ZSTR_LEN(regex) > 0) { apply_reg = true; - if (SUCCESS != object_init_ex(®exiter, spl_ce_RegexIterator)) { + ZVAL_COPY_VALUE(&args[0], &iteriter); + ZVAL_STR(&args[1], regex); + + if (SUCCESS != object_init_with_constructor(®exiter, spl_ce_RegexIterator, 2, args, NULL)) { zval_ptr_dtor(&iteriter); - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to instantiate regex iterator for %s", phar_obj->archive->fname); RETURN_THROWS(); } - - ZVAL_STR(&arg2, regex); - zend_call_known_instance_method_with_2_params(spl_ce_RegexIterator->constructor, - Z_OBJ(regexiter), NULL, &iteriter, &arg2); } array_init(return_value); @@ -2199,15 +2179,12 @@ static zend_object *phar_rename_archive(phar_archive_data **sphar, char *ext) /* ce = phar_ce_archive; } - if (SUCCESS != object_init_ex(&ret, ce)) { - zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Unable to instantiate phar object when converting archive \"%s\"", phar->fname); + ZVAL_STRINGL(&arg1, phar->fname, phar->fname_len); + zend_result result = object_init_with_constructor(&ret, ce, 1, &arg1, NULL); + zval_ptr_dtor_str(&arg1); + if (SUCCESS != result) { return NULL; } - - ZVAL_STRINGL(&arg1, phar->fname, phar->fname_len); - - zend_call_known_instance_method_with_1_params(ce->constructor, Z_OBJ(ret), NULL, &arg1); - zval_ptr_dtor(&arg1); return Z_OBJ(ret); err_reused_oldpath: From f14e5fcfbd566491801d3b703f8a430e2e2c7b45 Mon Sep 17 00:00:00 2001 From: SpencerMalone Date: Sun, 12 Oct 2025 12:04:24 -0700 Subject: [PATCH 6/9] Fix not thread safe xmlSchemaParse calls in ZTS builds By calling xmlSchemaInitTypes during MINIT. See: https://gitlab.gnome.org/GNOME/libxml2/-/issues/930 Closes GH-20150. --- NEWS | 3 +++ ext/libxml/libxml.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 81bae73f4988..42b78f2ade31 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,9 @@ PHP NEWS . Fixed bug GH-20070 (Return type violation in imagefilter when an invalid filter is provided). (Girgias) +- LibXML: + . Fix not thread safe xmlSchemaParse calls. (SpencerMalone) + - Opcache: . Fixed bug GH-20081 (access to uninitialized vars in preload_load()). (Arnaud) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index eecbca4ed890..6864b25c8790 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -39,6 +39,7 @@ #ifdef LIBXML_SCHEMAS_ENABLED #include #include +#include #endif #include "php_libxml.h" @@ -933,7 +934,11 @@ PHP_LIBXML_API void php_libxml_initialize(void) if (!_php_libxml_initialized) { /* we should be the only one's to ever init!! */ ZEND_IGNORE_LEAKS_BEGIN(); + xmlInitParser(); +#ifdef LIBXML_SCHEMAS_ENABLED + xmlSchemaInitTypes(); +#endif ZEND_IGNORE_LEAKS_END(); _php_libxml_default_entity_loader = xmlGetExternalEntityLoader(); From 75d8449d1f5486b05b71aa51de119bbb1ac50376 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 14 Oct 2025 21:59:17 +0200 Subject: [PATCH 7/9] Fix not thread safe RelaxNG invocations in ZTS builds --- NEWS | 2 +- ext/libxml/libxml.c | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 42b78f2ade31..584064078a04 100644 --- a/NEWS +++ b/NEWS @@ -20,7 +20,7 @@ PHP NEWS filter is provided). (Girgias) - LibXML: - . Fix not thread safe xmlSchemaParse calls. (SpencerMalone) + . Fix not thread safe schema/relaxng calls. (SpencerMalone, nielsdos) - Opcache: . Fixed bug GH-20081 (access to uninitialized vars in preload_load()). diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 6864b25c8790..8f57d6fb688a 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -938,6 +938,9 @@ PHP_LIBXML_API void php_libxml_initialize(void) xmlInitParser(); #ifdef LIBXML_SCHEMAS_ENABLED xmlSchemaInitTypes(); +#endif +#ifdef LIBXML_RELAXNG_ENABLED + xmlRelaxNGInitTypes(); #endif ZEND_IGNORE_LEAKS_END(); From f3cb9a81fb77ba18043fd53514b94a442f30219e Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Tue, 14 Oct 2025 22:14:54 +0200 Subject: [PATCH 8/9] Wrap the normally lazy-initialized components in ZTS --- ext/libxml/libxml.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 8f57d6fb688a..d5009889549b 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -936,11 +936,13 @@ PHP_LIBXML_API void php_libxml_initialize(void) ZEND_IGNORE_LEAKS_BEGIN(); xmlInitParser(); -#ifdef LIBXML_SCHEMAS_ENABLED +#ifdef ZTS +# ifdef LIBXML_SCHEMAS_ENABLED xmlSchemaInitTypes(); -#endif -#ifdef LIBXML_RELAXNG_ENABLED +# endif +# ifdef LIBXML_RELAXNG_ENABLED xmlRelaxNGInitTypes(); +# endif #endif ZEND_IGNORE_LEAKS_END(); From e6e39e680efdcf5486565b0782bb806ac87e0806 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 15 Sep 2025 19:03:30 +0200 Subject: [PATCH 9/9] Don't bail when closing resources on shutdown Fixes GH-19844 Closes GH-19849 --- NEWS | 1 + Zend/tests/gh19844.phpt | 46 +++++++++++++++++++++++++++++++++++++++++ Zend/zend_execute_API.c | 4 +--- Zend/zend_list.c | 24 ++++++++++++++------- 4 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 Zend/tests/gh19844.phpt diff --git a/NEWS b/NEWS index 584064078a04..9c97a52402a7 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,7 @@ PHP NEWS . Fixed bug GH-19934 (CGI with auto_globals_jit=0 causes uouv). (ilutov) . Fixed bug GH-20073 (Assertion failure in WeakMap offset operations on reference). (nielsdos) + . Fixed bug GH-19844 (Don't bail when closing resources on shutdown). (ilutov) - DOM: . Partially fixed bug GH-16317 (DOM classes do not allow diff --git a/Zend/tests/gh19844.phpt b/Zend/tests/gh19844.phpt new file mode 100644 index 000000000000..297864073535 --- /dev/null +++ b/Zend/tests/gh19844.phpt @@ -0,0 +1,46 @@ +--TEST-- +GH-19844: Bail from stream_close() in zend_shutdown_executor_values() +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Fatal error: Bail in %s on line %d + +Fatal error: Bail in %s on line %d + +Fatal error: Bail in %s on line %d diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index d4a373616fe9..e2503d6d3045 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -274,9 +274,7 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) zval *zv; EG(flags) |= EG_FLAGS_IN_RESOURCE_SHUTDOWN; - zend_try { - zend_close_rsrc_list(&EG(regular_list)); - } zend_end_try(); + zend_close_rsrc_list(&EG(regular_list)); /* No PHP callback functions should be called after this point. */ EG(active) = 0; diff --git a/Zend/zend_list.c b/Zend/zend_list.c index bf599a2efca9..5add19256a69 100644 --- a/Zend/zend_list.c +++ b/Zend/zend_list.c @@ -217,15 +217,25 @@ void zend_close_rsrc_list(HashTable *ht) /* Reload ht->arData on each iteration, as it may be reallocated. */ uint32_t i = ht->nNumUsed; - while (i-- > 0) { - zval *p = ZEND_HASH_ELEMENT(ht, i); - if (Z_TYPE_P(p) != IS_UNDEF) { - zend_resource *res = Z_PTR_P(p); - if (res->type >= 0) { - zend_resource_dtor(res); +retry: + zend_try { + while (i-- > 0) { + zval *p = ZEND_HASH_ELEMENT(ht, i); + if (Z_TYPE_P(p) != IS_UNDEF) { + zend_resource *res = Z_PTR_P(p); + if (res->type >= 0) { + zend_resource_dtor(res); + } } } - } + } zend_catch { + /* If we have bailed, we probably executed user code (e.g. user stream + * API). Keep closing resources so they don't leak. User handlers must be + * called now so they aren't called in zend_deactivate() on + * zend_destroy_rsrc_list(&EG(regular_list)). At that point, the executor + * has already shut down and the process would crash. */ + goto retry; + } zend_end_try(); }