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 fd4ef074861f..54ddcd48f2d3 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -273,9 +273,7 @@ void shutdown_destructors(void) /* {{{ */ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) { 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(); } diff --git a/ext/libxml/libxml.c b/ext/libxml/libxml.c index 2e75d697296a..b27985609cd3 100644 --- a/ext/libxml/libxml.c +++ b/ext/libxml/libxml.c @@ -40,6 +40,7 @@ #ifdef LIBXML_SCHEMAS_ENABLED #include #include +#include #endif #include "php_libxml.h" @@ -928,7 +929,16 @@ 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 ZTS +# ifdef LIBXML_SCHEMAS_ENABLED + xmlSchemaInitTypes(); +# endif +# ifdef LIBXML_RELAXNG_ENABLED + xmlRelaxNGInitTypes(); +# endif +#endif ZEND_IGNORE_LEAKS_END(); php_libxml_default_entity_loader = xmlGetExternalEntityLoader(); diff --git a/ext/openssl/openssl_backend_common.c b/ext/openssl/openssl_backend_common.c index 5671240c044e..611359cccaba 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 956f07e1a1e0..81c327dbfe34 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); 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-- 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) */ 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: