From 39e7411994eb3d718bfdb3eba73ba9007abf446a Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 24 Jun 2026 07:34:59 -0400 Subject: [PATCH] soap: do not overwrite the parsed host on a protocol-relative redirect When a redirect Location is a protocol-relative reference (//host/path), php_url_parse() already fills new_url->host, but the scheme-less redirect handling overwrote it with a copy of the request host without releasing the parsed one. That leaks a zend_string per such redirect and pins the redirect back to the original host instead of the one the server named. Inherit host and port from the request URL only when new_url->host is NULL, mirroring the scheme guard directly above. --- ext/soap/php_http.c | 6 ++- .../bugs/protocol_relative_redirect.phpt | 49 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 ext/soap/tests/bugs/protocol_relative_redirect.phpt diff --git a/ext/soap/php_http.c b/ext/soap/php_http.c index 0bec42afbc37..e9fd68007d26 100644 --- a/ext/soap/php_http.c +++ b/ext/soap/php_http.c @@ -1151,8 +1151,10 @@ int make_http_soap_request(zval *this_ptr, zend_string_release_ex(http_body, 0); if (new_url->scheme == NULL && new_url->path != NULL) { new_url->scheme = phpurl->scheme ? zend_string_copy(phpurl->scheme) : NULL; - new_url->host = phpurl->host ? zend_string_copy(phpurl->host) : NULL; - new_url->port = phpurl->port; + if (new_url->host == NULL) { + new_url->host = phpurl->host ? zend_string_copy(phpurl->host) : NULL; + new_url->port = phpurl->port; + } if (new_url->path && ZSTR_VAL(new_url->path)[0] != '/') { if (phpurl->path) { char *t = ZSTR_VAL(phpurl->path); diff --git a/ext/soap/tests/bugs/protocol_relative_redirect.phpt b/ext/soap/tests/bugs/protocol_relative_redirect.phpt new file mode 100644 index 000000000000..e8f30ca66872 --- /dev/null +++ b/ext/soap/tests/bugs/protocol_relative_redirect.phpt @@ -0,0 +1,49 @@ +--TEST-- +SOAP client follows a protocol-relative (//host/path) redirect Location without leaking the host +--EXTENSIONS-- +soap +--SKIPIF-- + +--FILE-- +', + '', + 'ok', + ''; +} else { + http_response_code(302); + header("Location: //" . $_SERVER["HTTP_HOST"] . "/redirected"); +} +PHP; + +php_cli_server_start($code, null, $args); + +$client = new SoapClient(null, [ + 'location' => 'http://' . PHP_CLI_SERVER_ADDRESS . '/start', + 'uri' => 'test-uri', +]); + +try { + $client->__soapCall("foo", []); + echo "redirect followed\n"; +} catch (SoapFault $e) { + echo "SoapFault: " . $e->getMessage() . "\n"; +} +?> +--EXPECT-- +redirect followed