From ac86e1046852c0882a4a0c3f4083a5fdf801aac5 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 5 May 2026 11:20:26 +0200 Subject: [PATCH 01/16] Fix compiler warning with GCC 16: variable 'offset' set but not used [-Werror=unused-but-set-variable=] --- ext/mysqlnd/mysqlnd_result.c | 6 ++---- ext/opcache/zend_persist.c | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index ae8dc77b4cd9..f43b1d7f59d3 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -994,8 +994,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int array_init_size(return_value, array_size); HashTable *row_ht = Z_ARRVAL_P(return_value); - MYSQLND_FIELD *field = meta->fields; - for (unsigned i = 0; i < meta->field_count; i++, field++) { + for (unsigned i = 0; i < meta->field_count; i++) { zval *data = &row_data[i]; if (flags & MYSQLND_FETCH_NUM) { @@ -1038,10 +1037,9 @@ MYSQLND_METHOD(mysqlnd_res, fetch_row_c)(MYSQLND_RES * result) mysqlnd_result_free_prev_data(result); if (result->m.fetch_row(result, &row_data, 0, &fetched_anything) == PASS && fetched_anything) { unsigned field_count = result->field_count; - MYSQLND_FIELD *field = result->meta->fields; ret = mnd_emalloc(field_count * sizeof(char *)); - for (unsigned i = 0; i < field_count; i++, field++) { + for (unsigned i = 0; i < field_count; i++) { zval *data = &row_data[i]; if (Z_TYPE_P(data) != IS_NULL) { convert_to_string(data); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index f8e11e4d01ec..3f80b7d65d99 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -525,9 +525,8 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc zend_op *new_opcodes = zend_shared_memdup_put(op_array->opcodes, sizeof(zend_op) * op_array->last); zend_op *opline = new_opcodes; zend_op *end = new_opcodes + op_array->last; - int offset = 0; - for (; opline < end ; opline++, offset++) { + for (; opline < end ; opline++) { #if ZEND_USE_ABS_CONST_ADDR if (opline->op1_type == IS_CONST) { opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals)); From 6a2751450930e111ec960f5fb76754db21b9d919 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 5 May 2026 11:22:09 +0200 Subject: [PATCH 02/16] Fix compiler warning with glibc 2.43 support of C23 const-preserving standard library macros: assignment discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers] See https://sourceware.org/cgit/glibc/commit/?id=cd748a63ab1 Closes GH-21950 --- ext/mysqlnd/mysqlnd_alloc.c | 10 +++++----- ext/mysqlnd/mysqlnd_connection.c | 2 +- ext/mysqlnd/mysqlnd_wireprotocol.c | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ext/mysqlnd/mysqlnd_alloc.c b/ext/mysqlnd/mysqlnd_alloc.c index 06971b3487dd..3c059f327ac9 100644 --- a/ext/mysqlnd/mysqlnd_alloc.c +++ b/ext/mysqlnd/mysqlnd_alloc.c @@ -201,7 +201,7 @@ static void _mysqlnd_efree(void *ptr MYSQLND_MEM_D) #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif @@ -232,7 +232,7 @@ static void _mysqlnd_pefree(void *ptr, bool persistent MYSQLND_MEM_D) #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif @@ -264,7 +264,7 @@ static char * _mysqlnd_pememdup(const char * const ptr, size_t length, bool pers #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif @@ -295,7 +295,7 @@ static char * _mysqlnd_pestrndup(const char * const ptr, size_t length, bool per #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif @@ -336,7 +336,7 @@ static char * _mysqlnd_pestrdup(const char * const ptr, bool persistent MYSQLND_ TRACE_ALLOC_ENTER(mysqlnd_pestrdup_name); #if PHP_DEBUG { - char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); + const char * fn = strrchr(__zend_filename, PHP_DIR_SEPARATOR); TRACE_ALLOC_INF_FMT("file=%-15s line=%4d", fn? fn + 1:__zend_filename, __zend_lineno); } #endif diff --git a/ext/mysqlnd/mysqlnd_connection.c b/ext/mysqlnd/mysqlnd_connection.c index 2402ad6fc8f4..2d7d97b237e5 100644 --- a/ext/mysqlnd/mysqlnd_connection.c +++ b/ext/mysqlnd/mysqlnd_connection.c @@ -557,7 +557,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_scheme)(MYSQLND_CONN_DATA * conn, MYSQLND_ /* IPv6 without square brackets so without port */ transport.l = mnd_sprintf(&transport.s, 0, "tcp://[%s]:%u", hostname.s, port); } else { - char *p; + const char *p; /* IPv6 addresses are in the format [address]:port */ if (hostname.s[0] == '[') { /* IPv6 */ diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 36fd53233737..64c2c7969619 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -461,7 +461,7 @@ php_mysqlnd_greet_read(MYSQLND_CONN_DATA * conn, void * _packet) packet->auth_protocol = estrdup(""); } else { /* Check if NUL present */ - char *null_terminator = memchr(p, '\0', remaining_size); + const char *null_terminator = memchr(p, '\0', remaining_size); size_t auth_protocol_len; if (null_terminator) { /* If present, do basically estrdup */ From 33caad78f044bbe3137f26b164e233ef6b634e81 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Wed, 6 May 2026 12:39:47 +0200 Subject: [PATCH 03/16] Fix bad merge --- ext/mysqlnd/mysqlnd_result.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/mysqlnd/mysqlnd_result.c b/ext/mysqlnd/mysqlnd_result.c index 1f811c8795df..b4d0bdbd8d2c 100644 --- a/ext/mysqlnd/mysqlnd_result.c +++ b/ext/mysqlnd/mysqlnd_result.c @@ -990,7 +990,7 @@ MYSQLND_METHOD(mysqlnd_res, fetch_into)(MYSQLND_RES * result, const unsigned int HashTable *row_ht = Z_ARRVAL_P(return_value); MYSQLND_FIELD *field = meta->fields; - for (unsigned i = 0; i < meta->field_count; i++) { + for (unsigned i = 0; i < meta->field_count; i++, field++) { zval *data = &row_data[i]; if (flags & MYSQLND_FETCH_NUM) { From aee3b3ac9b816b0def1c462695b483b49a83148e Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sun, 3 May 2026 19:56:53 +0200 Subject: [PATCH 04/16] GHSA-85c2-q967-79q5: [soap] Fix stale SOAP_GLOBAL(ref_map) pointer with Apache Map Fixes GHSA-85c2-q967-79q5 Fixes CVE-2026-6722 --- ext/soap/php_encoding.c | 3 +- ext/soap/tests/GHSA-85c2-q967-79q5.phpt | 61 +++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 ext/soap/tests/GHSA-85c2-q967-79q5.phpt diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index a685dbcd228a..725c279d8d42 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -365,6 +365,7 @@ static bool soap_check_xml_ref(zval *data, xmlNodePtr node) static void soap_add_xml_ref(zval *data, xmlNodePtr node) { if (SOAP_GLOBAL(ref_map)) { + Z_TRY_ADDREF_P(data); zend_hash_index_update(SOAP_GLOBAL(ref_map), (zend_ulong)node, data); } } @@ -3448,7 +3449,7 @@ void encode_reset_ns() } else { SOAP_GLOBAL(ref_map) = emalloc(sizeof(HashTable)); } - zend_hash_init(SOAP_GLOBAL(ref_map), 0, NULL, NULL, 0); + zend_hash_init(SOAP_GLOBAL(ref_map), 0, NULL, ZVAL_PTR_DTOR, 0); } void encode_finish() diff --git a/ext/soap/tests/GHSA-85c2-q967-79q5.phpt b/ext/soap/tests/GHSA-85c2-q967-79q5.phpt new file mode 100644 index 000000000000..8bcac26ad187 --- /dev/null +++ b/ext/soap/tests/GHSA-85c2-q967-79q5.phpt @@ -0,0 +1,61 @@ +--TEST-- +GHSA-85c2-q967-79q5: Stale SOAP_GLOBAL(ref_map) pointer with Apache Map +--CREDITS-- +brettgervasoni +--EXTENSIONS-- +soap +--FILE-- + + + + + + + + foo + bar + + + foo + baz + + + + + + +XML; + +$s = new SoapServer(null, ['uri' => 'urn:a']); +$s->setClass(Handler::class); +$s->handle($envelope); +var_dump($result); + +?> +--EXPECTF-- + + +array(2) { + [0]=> + array(1) { + ["foo"]=> + string(3) "baz" + } + [1]=> + object(stdClass)#%d (1) { + ["object"]=> + string(3) "bar" + } +} From db2a7f9348fd5dda5fd162061786a664c417bf5b Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sun, 3 May 2026 19:57:16 +0200 Subject: [PATCH 05/16] GHSA-m33r-qmcv-p97q: [soap] Fix use-after-free after header parsing failure with SOAP_PERSISTENCE_SESSION Fixes GHSA-m33r-qmcv-p97q Fixes CVE-2026-7261 --- ext/soap/soap.c | 12 ++++- ext/soap/tests/GHSA-m33r-qmcv-p97q.phpt | 58 +++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/GHSA-m33r-qmcv-p97q.phpt diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 1350c0c0a344..02501b8d73ff 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -1438,12 +1438,20 @@ PHP_METHOD(SoapServer, handle) instanceof_function(Z_OBJCE(h->retval), soap_fault_class_entry)) { php_output_discard(); soap_server_fault_ex(function, &h->retval, h); - if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(soap_obj);} + if (service->type == SOAP_CLASS && soap_obj) { + if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { + zval_ptr_dtor(soap_obj); + } + } goto fail; } else if (EG(exception)) { php_output_discard(); _soap_server_exception(service, function, ZEND_THIS); - if (service->type == SOAP_CLASS && soap_obj) {zval_ptr_dtor(soap_obj);} + if (service->type == SOAP_CLASS && soap_obj) { + if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { + zval_ptr_dtor(soap_obj); + } + } goto fail; } } else if (h->mustUnderstand) { diff --git a/ext/soap/tests/GHSA-m33r-qmcv-p97q.phpt b/ext/soap/tests/GHSA-m33r-qmcv-p97q.phpt new file mode 100644 index 000000000000..bcf441ccd18a --- /dev/null +++ b/ext/soap/tests/GHSA-m33r-qmcv-p97q.phpt @@ -0,0 +1,58 @@ +--TEST-- +GHSA-m33r-qmcv-p97q: Use-after-free after header parsing failure with SOAP_PERSISTENCE_SESSION +--CREDITS-- +Ilia Alshanetsky (iliaal) +--EXTENSIONS-- +soap +session +--FILE-- + 'urn:a']); +$srv->setClass(Handler::class); +$srv->setPersistence(SOAP_PERSISTENCE_SESSION); + +$srv->handle(<< + + + + + + + + +XML); + +$srv->handle(<< + + + + + + + + +XML); + +?> +--EXPECT-- + +SOAP-ENV:Serverdenied + +SOAP-ENV:Serverdenied From 79551ab8b1a97760c739e372f9bc359619f3554d Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sat, 25 Apr 2026 00:44:37 +0200 Subject: [PATCH 06/16] GHSA-hmxp-6pc4-f3vv: [soap] Fix broken Apache map value NULL check Fixes GHSA-hmxp-6pc4-f3vv Fixes CVE-2026-7262 --- ext/soap/php_encoding.c | 2 +- ext/soap/tests/GHSA-hmxp-6pc4-f3vv.phpt | 39 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 ext/soap/tests/GHSA-hmxp-6pc4-f3vv.phpt diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 725c279d8d42..0865726ceb9b 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2722,7 +2722,7 @@ static zval *to_zval_map(zval *ret, encodeTypePtr type, xmlNodePtr data) } xmlValue = get_node(item->children, "value"); - if (!xmlKey) { + if (!xmlValue) { soap_error0(E_ERROR, "Encoding: Can't decode apache map, missing value"); } diff --git a/ext/soap/tests/GHSA-hmxp-6pc4-f3vv.phpt b/ext/soap/tests/GHSA-hmxp-6pc4-f3vv.phpt new file mode 100644 index 000000000000..e46ab2e4607d --- /dev/null +++ b/ext/soap/tests/GHSA-hmxp-6pc4-f3vv.phpt @@ -0,0 +1,39 @@ +--TEST-- +GHSA-hmxp-6pc4-f3vv: Null pointer dereference on missing Apache map value +--CREDITS-- +Ilia Alshanetsky (iliaal) +--EXTENSIONS-- +soap +--FILE-- + + + + + + + hello + + + + +XML; + +$server = new SoapServer(null, [ + 'uri' => 'urn:test', + 'typemap' => [['type_name' => 'anything']], +]); +$server->addFunction('test'); +function test($m) { return null; } +$server->handle($request); + +?> +--EXPECT-- + +SOAP-ENV:ServerSOAP-ERROR: Encoding: Can't decode apache map, missing value From 99a5ad7441de9914246c7863adb6997396008b9d Mon Sep 17 00:00:00 2001 From: Jakub Zelenka Date: Sun, 3 May 2026 20:01:41 +0200 Subject: [PATCH 07/16] GHSA-7qg2-v9fj-4mwv: [fpm] XSS within status endpoint Fixes GHSA-7qg2-v9fj-4mwv Fixes CVE-2026-6735 --- sapi/fpm/fpm/fpm_status.c | 34 +++++++++++-- .../tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt | 48 +++++++++++++++++++ 2 files changed, 78 insertions(+), 4 deletions(-) create mode 100644 sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c index cebaa18c964b..0339b75a1449 100644 --- a/sapi/fpm/fpm/fpm_status.c +++ b/sapi/fpm/fpm/fpm_status.c @@ -528,8 +528,8 @@ int fpm_status_handle_request(void) /* {{{ */ if (full_syntax) { unsigned int i; int first; - zend_string *tmp_query_string; - char *query_string; + zend_string *tmp_query_string, *tmp_request_uri_string; + char *query_string, *request_uri_string; struct timeval duration, now; float cpu; @@ -554,13 +554,36 @@ int fpm_status_handle_request(void) /* {{{ */ } } + request_uri_string = NULL; + tmp_request_uri_string = NULL; + if (proc->request_uri[0] != '\0') { + if (encode_html) { + tmp_request_uri_string = php_escape_html_entities_ex( + (const unsigned char *) proc->request_uri, + strlen(proc->request_uri), 1, ENT_DISALLOWED | ENT_HTML_DOC_XML1 | ENT_COMPAT, + NULL, /* double_encode */ 1, /* quiet */ 0); + request_uri_string = ZSTR_VAL(tmp_request_uri_string); + } else if (encode_json) { + tmp_request_uri_string = php_json_encode_string(proc->request_uri, + strlen(proc->request_uri), PHP_JSON_INVALID_UTF8_IGNORE); + request_uri_string = ZSTR_VAL(tmp_request_uri_string); + /* remove quotes around the string */ + if (ZSTR_LEN(tmp_request_uri_string) >= 2) { + request_uri_string[ZSTR_LEN(tmp_request_uri_string) - 1] = '\0'; + ++request_uri_string; + } + } else { + request_uri_string = proc->request_uri; + } + } + query_string = NULL; tmp_query_string = NULL; if (proc->query_string[0] != '\0') { if (encode_html) { tmp_query_string = php_escape_html_entities_ex( (const unsigned char *) proc->query_string, - strlen(proc->query_string), 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, + strlen(proc->query_string), 1, ENT_DISALLOWED | ENT_HTML_DOC_XML1 | ENT_COMPAT, NULL, /* double_encode */ 1, /* quiet */ 0); } else if (encode_json) { tmp_query_string = php_json_encode_string(proc->query_string, @@ -599,7 +622,7 @@ int fpm_status_handle_request(void) /* {{{ */ proc->requests, duration.tv_sec * 1000000UL + duration.tv_usec, proc->request_method[0] != '\0' ? proc->request_method : "-", - proc->request_uri[0] != '\0' ? proc->request_uri : "-", + request_uri_string ? request_uri_string : "-", query_string ? "?" : "", query_string ? query_string : "", proc->content_length, @@ -610,6 +633,9 @@ int fpm_status_handle_request(void) /* {{{ */ PUTS(buffer); efree(buffer); + if (tmp_request_uri_string) { + zend_string_free(tmp_request_uri_string); + } if (tmp_query_string) { zend_string_free(tmp_query_string); } diff --git a/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt b/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt new file mode 100644 index 000000000000..475bc130a42b --- /dev/null +++ b/sapi/fpm/tests/ghsa-7qg2-v9fj-4mwv-status-xss.phpt @@ -0,0 +1,48 @@ +--TEST-- +FPM: GHSA-7qg2-v9fj-4mwv - status xss +--SKIPIF-- + +--FILE-- +start(); +$tester->expectLogStartNotices(); +$responses = $tester + ->multiRequest([ + ['uri' => '/', 'query' => ''], + ['uri' => '/status', 'query' => 'full&html', 'delay' => 100000], + ]); +var_dump(strpos($responses[1]->getBody(), '