diff --git a/Zend/zend.c b/Zend/zend.c index be7fa210fff70..5ff84f15f4634 100644 --- a/Zend/zend.c +++ b/Zend/zend.c @@ -1175,20 +1175,24 @@ ZEND_COLD void zenderror(const char *error) /* {{{ */ } /* }}} */ -ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout(const char *filename, uint32_t lineno) /* {{{ */ +ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout_without_gc_protect(const char *filename, uint32_t lineno) { - if (!EG(bailout)) { zend_output_debug_string(1, "%s(%d) : Bailed out without a bailout address!", filename, lineno); exit(-1); } - gc_protect(1); CG(unclean_shutdown) = 1; CG(active_class_entry) = NULL; CG(in_compilation) = 0; EG(current_execute_data) = NULL; LONGJMP(*EG(bailout), FAILURE); } + +ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout(const char *filename, uint32_t lineno) /* {{{ */ +{ + gc_protect(true); + _zend_bailout_without_gc_protect(filename, lineno); +} /* }}} */ ZEND_API size_t zend_get_page_size(void) diff --git a/Zend/zend.h b/Zend/zend.h index ac3a826a4813b..7a1c5801a8ba8 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -249,6 +249,7 @@ typedef struct _zend_utility_values { typedef size_t (*zend_write_func_t)(const char *str, size_t str_length); +#define zend_bailout_without_gc_protect() _zend_bailout_without_gc_protect(__FILE__, __LINE__) #define zend_bailout() _zend_bailout(__FILE__, __LINE__) #define zend_try \ @@ -274,6 +275,7 @@ void zend_register_standard_ini_entries(void); zend_result zend_post_startup(void); void zend_set_utility_values(zend_utility_values *utility_values); +ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout_without_gc_protect(const char *filename, uint32_t lineno); ZEND_API ZEND_COLD ZEND_NORETURN void _zend_bailout(const char *filename, uint32_t lineno); ZEND_API size_t zend_get_page_size(void); diff --git a/ext/oci8/php_oci8_int.h b/ext/oci8/php_oci8_int.h index 171edfa53d24c..0d3931cde2bec 100644 --- a/ext/oci8/php_oci8_int.h +++ b/ext/oci8/php_oci8_int.h @@ -322,7 +322,7 @@ typedef struct { ub4 serverStatus = OCI_SERVER_NORMAL; \ switch (errcode) { \ case 1013: \ - zend_bailout(); \ + zend_bailout_without_gc_protect(); \ break; \ case 22: \ case 28: \ diff --git a/ext/pdo_oci/oci_driver.c b/ext/pdo_oci/oci_driver.c index f74c67ff4ed39..538f3ec16f135 100644 --- a/ext/pdo_oci/oci_driver.c +++ b/ext/pdo_oci/oci_driver.c @@ -123,7 +123,7 @@ ub4 _oci_error(OCIError *err, pdo_dbh_t *dbh, pdo_stmt_t *stmt, char *what, swor if (einfo->errcode) { switch (einfo->errcode) { case 1013: /* user requested cancel of current operation */ - zend_bailout(); + zend_bailout_without_gc_protect(); break; case 12154: /* ORA-12154: TNS:could not resolve service name */ diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 448d03b7cc75e..b5df8cd8b08cf 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -164,7 +164,7 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char #ifdef PHP_WIN32 efree(arch); #endif - zend_bailout(); + zend_bailout_without_gc_protect(); case PHAR_MIME_OTHER: /* send headers, output file contents */ efree(basename); @@ -176,7 +176,7 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char efree((void *) ctr.line); if (FAILURE == sapi_send_headers()) { - zend_bailout(); + zend_bailout_without_gc_protect(); } /* prepare to output */ @@ -207,7 +207,7 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char } } while (1); - zend_bailout(); + zend_bailout_without_gc_protect(); case PHAR_MIME_PHP: if (basename) { phar_mung_server_vars(arch, entry, entry_len, basename, ru_len); @@ -283,7 +283,7 @@ static int phar_file_action(phar_archive_data *phar, phar_entry_info *info, char efree(name); } zend_end_try(); - zend_bailout(); + zend_bailout_without_gc_protect(); } return PHAR_MIME_PHP; @@ -705,7 +705,7 @@ PHP_METHOD(Phar, webPhar) } efree(pt); - zend_bailout(); + zend_bailout_without_gc_protect(); default: zend_throw_exception_ex(phar_ce_PharException, 0, "phar error: rewrite callback must return a string or false"); @@ -751,7 +751,7 @@ PHP_METHOD(Phar, webPhar) efree(path_info); } - zend_bailout(); + zend_bailout_without_gc_protect(); } else { char *tmp = NULL, sa = '\0'; sapi_header_line ctr = {0}; @@ -785,14 +785,14 @@ PHP_METHOD(Phar, webPhar) sapi_header_op(SAPI_HEADER_REPLACE, &ctr); sapi_send_headers(); efree((void *) ctr.line); - zend_bailout(); + zend_bailout_without_gc_protect(); } } if (FAILURE == phar_get_archive(&phar, fname, fname_len, NULL, 0, NULL) || (info = phar_get_entry_info(phar, entry, entry_len, NULL, 0)) == NULL) { phar_do_404(phar, fname, fname_len, f404, f404_len, entry, entry_len); - zend_bailout(); + zend_bailout_without_gc_protect(); } if (mimeoverride && zend_hash_num_elements(Z_ARRVAL_P(mimeoverride))) { diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 3a4626aa5beee..601a5856ccad3 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -1518,7 +1518,7 @@ static zval *to_zval_object_ex(zval *ret, encodeTypePtr type, xmlNodePtr data, z master_to_zval(&data, attr->encode, dummy); } zend_catch { xmlFreeNode(dummy); - zend_bailout(); + zend_bailout_without_gc_protect(); } zend_end_try(); xmlFreeNode(dummy); set_zval_property(ret, attr->name, &data); diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index 3dd8e6c5d76e4..57f375199976c 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -1172,7 +1172,7 @@ static sdlPtr load_wsdl(zval *this_ptr, char *struri) } zend_catch { /* Avoid persistent memory leak. */ zend_hash_destroy(&ctx.docs); - zend_bailout(); + zend_bailout_without_gc_protect(); } zend_end_try(); zend_hash_destroy(&ctx.messages); diff --git a/ext/soap/soap.c b/ext/soap/soap.c index fbf6546beb824..d7cd72a7456d6 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -124,7 +124,7 @@ static void soap_error_handler(int error_num, zend_string *error_filename, const Z_OBJ(SOAP_GLOBAL(error_object)) = _old_error_object;\ SOAP_GLOBAL(soap_version) = _old_soap_version;\ if (_bailout) {\ - zend_bailout();\ + zend_bailout_without_gc_protect();\ } #define FETCH_THIS_SDL(ss) \ @@ -1356,7 +1356,7 @@ PHP_METHOD(SoapServer, handle) } zend_catch { /* Avoid leaking persistent memory */ xmlFreeDoc(doc_request); - zend_bailout(); + zend_bailout_without_gc_protect(); } zend_end_try(); xmlFreeDoc(doc_request); @@ -1767,7 +1767,7 @@ static ZEND_NORETURN void soap_server_fault(char* code, char* string, char *acto set_soap_fault(&ret, NULL, code, string, actor, details, name); /* TODO: Which function */ soap_server_fault_ex(NULL, &ret, NULL); - zend_bailout(); + zend_bailout_without_gc_protect(); } /* }}} */ @@ -1797,7 +1797,7 @@ static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, z add_soap_fault_ex(&fault, &SOAP_GLOBAL(error_object), code, ZSTR_VAL(message), NULL, NULL); Z_ADDREF(fault); zend_throw_exception_object(&fault); - zend_bailout(); + zend_bailout_without_gc_protect(); } else if (!use_exceptions || !SOAP_GLOBAL(error_code) || strcmp(SOAP_GLOBAL(error_code),"WSDL") != 0) { @@ -1859,7 +1859,7 @@ static zend_never_inline ZEND_COLD void soap_real_error_handler(int error_num, z if (fault) { soap_server_fault_ex(NULL, &fault_obj, NULL); - zend_bailout(); + zend_bailout_without_gc_protect(); } } } @@ -2187,7 +2187,7 @@ static int do_request(zval *this_ptr, xmlDoc *request, char *location, char *act zval_ptr_dtor(¶ms[0]); xmlFree(buf); if (_bailout) { - zend_bailout(); + zend_bailout_without_gc_protect(); } if (ret && Z_TYPE_P(Z_CLIENT_SOAP_FAULT_P(this_ptr)) == IS_OBJECT) { ret = FALSE; @@ -2407,7 +2407,7 @@ static void do_soap_call(zend_execute_data *execute_data, if (request) { xmlFreeDoc(request); } - zend_bailout(); + zend_bailout_without_gc_protect(); } SOAP_CLIENT_END_CODE(); } @@ -3749,7 +3749,7 @@ static xmlDocPtr serialize_response_call(sdlFunctionPtr function, char *function } zend_catch { /* Avoid persistent memory leak. */ xmlFreeDoc(doc); - zend_bailout(); + zend_bailout_without_gc_protect(); } zend_end_try(); if (function && function->responseName == NULL && @@ -3954,7 +3954,7 @@ static xmlDocPtr serialize_function_call(zval *this_ptr, sdlFunctionPtr function } zend_catch { /* Avoid persistent memory leak. */ xmlFreeDoc(doc); - zend_bailout(); + zend_bailout_without_gc_protect(); } zend_end_try(); return doc; diff --git a/ext/soap/tests/gh10026.phpt b/ext/soap/tests/gh10026.phpt new file mode 100644 index 0000000000000..cb1627c594408 --- /dev/null +++ b/ext/soap/tests/gh10026.phpt @@ -0,0 +1,65 @@ +--TEST-- +GH-10026 (Garbage collection stops after exception in SoapClient) +--EXTENSIONS-- +soap +--FILE-- +b = $b; + $b->a = $a; +} + +function failySoapCall() +{ + try { + new SoapClient('https://127.0.0.1/?WSDL'); + } catch (Exception) { + echo "Soap call failed\n"; + } +} + +function gc() +{ + gc_collect_cycles(); + echo "GC Runs: " . gc_status()['runs'] . "\n"; +} + + +buildCycle(); +gc(); +buildCycle(); +gc(); +buildCycle(); +gc(); + +failySoapCall(); + +buildCycle(); +gc(); +buildCycle(); +gc(); +buildCycle(); +gc(); +?> +--EXPECT-- +GC Runs: 1 +GC Runs: 2 +GC Runs: 3 +Soap call failed +GC Runs: 4 +GC Runs: 5 +GC Runs: 6 diff --git a/sapi/fuzzer/fuzzer-execute.c b/sapi/fuzzer/fuzzer-execute.c index abc5ebff2ece5..e1f54857147aa 100644 --- a/sapi/fuzzer/fuzzer-execute.c +++ b/sapi/fuzzer/fuzzer-execute.c @@ -32,7 +32,7 @@ static zend_always_inline void fuzzer_step(void) { /* Reset steps before bailing out, so code running after bailout (e.g. in * destructors) will get another MAX_STEPS, rather than UINT32_MAX steps. */ steps_left = MAX_STEPS; - zend_bailout(); + zend_bailout_without_gc_protect(); } } diff --git a/sapi/phpdbg/phpdbg_prompt.c b/sapi/phpdbg/phpdbg_prompt.c index 4c50653ce66f9..174d580bc6ba8 100644 --- a/sapi/phpdbg/phpdbg_prompt.c +++ b/sapi/phpdbg/phpdbg_prompt.c @@ -1549,7 +1549,7 @@ int phpdbg_interactive(bool allow_async_unsafe, char *input) /* {{{ */ ret = phpdbg_stack_execute(&stack, allow_async_unsafe); } zend_catch { phpdbg_stack_free(&stack); - zend_bailout(); + zend_bailout_without_gc_protect(); } zend_end_try(); switch (ret) {