From f68090dba5e153be94434a20386da45644fb0659 Mon Sep 17 00:00:00 2001 From: Dhrupo Nil Date: Sat, 31 Oct 2026 23:59:15 +0600 Subject: [PATCH] Fix GH-25093: Phar::webPhar() measures the redirect target by the on-disk path Before appending the entry to the Location header, webPhar() terminates the request URI right after the phar's file name. It advanced the pointer by the whole on-disk path length rather than by the file name length, so the terminator landed the length of the phar's directory prefix too far. When the request ends at the file name, that write lands past the end of the request URI buffer. When the URI is longer, which a rewrite callback that routes to the index allows, the terminator lands inside the URI and the redirect keeps part of the requested path. The rest of the function already measures the file name as fname_len - (basename - fname). --- NEWS | 2 + ext/phar/phar_object.c | 2 +- ext/phar/tests/gh25093.phpt | 76 +++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 ext/phar/tests/gh25093.phpt diff --git a/NEWS b/NEWS index 529f07daccb8..cb638e1f064f 100644 --- a/NEWS +++ b/NEWS @@ -232,6 +232,8 @@ PHP NEWS compressed contents after changing compression). (Raphael Paez) . Fixed bug GH-24740 (Oversized tar metadata exhausts the memory limit). (Lazizbek Ergashev) + . Fixed bug GH-25093 (Phar::webPhar() builds the redirect target from the + on-disk path length). (dhrupo) - Phpdbg: . Fix wrong command names. (ndossche, Weilin Du) diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index e2f36f1c7b24..cb071f0ca300 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -784,7 +784,7 @@ PHP_METHOD(Phar, webPhar) sapi_header_op(SAPI_HEADER_REPLACE, &ctr); if (not_cgi) { - tmp = strstr(path_info, basename) + fname_len; + tmp = strstr(path_info, basename) + (fname_len - (basename - fname)); sa = *tmp; *tmp = '\0'; } diff --git a/ext/phar/tests/gh25093.phpt b/ext/phar/tests/gh25093.phpt new file mode 100644 index 000000000000..3a573e893f86 --- /dev/null +++ b/ext/phar/tests/gh25093.phpt @@ -0,0 +1,76 @@ +--TEST-- +GH-25093 (Phar::webPhar() builds the redirect target from the on-disk path length) +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +--SKIPIF-- + +--FILE-- +docRoot/sub"); +@unlink("$server->docRoot/sub/app.phar"); +$phar = new Phar("$server->docRoot/sub/app.phar"); +$phar["index.php"] = "setStub(''); +unset($phar); + +function request_redirect(string $uri): void +{ + $client = stream_socket_client("tcp://" . PHP_CLI_SERVER_ADDRESS); + fwrite($client, "GET $uri HTTP/1.1\r\nHost: " . PHP_CLI_SERVER_HOSTNAME . "\r\nConnection: close\r\n\r\n"); + $response = stream_get_contents($client); + fclose($client); + + foreach (explode("\r\n", $response) as $line) { + if ($line === "") { + break; + } + if (str_starts_with($line, "HTTP/") || str_starts_with($line, "Location:")) { + echo $line, "\n"; + } + } +} + +// Requested path longer than the phar's path on disk: the redirect target kept +// part of the requested path. +request_redirect("/sub/app.phar/" . str_repeat("a", strlen($server->docRoot) + 16)); + +// Requested path ending at the phar: the redirect target was measured past the +// end of the request URI. +request_redirect("/sub/app.phar"); +?> +--CLEAN-- + +--EXPECT-- +HTTP/1.1 301 Moved Permanently +Location: /sub/app.phar/index.php +HTTP/1.1 301 Moved Permanently +Location: /sub/app.phar/index.php