mirror of
https://github.com/php/php-src.git
synced 2026-10-29 02:07:35 +08:00
[http] Fix out-of-bounds read on empty Location header
An empty Location header allocates a single byte for the NUL terminator, so reading location[1] in the relative-redirect branch over-reads heap memory and could append a garbage-derived path to the redirect target instead of the correct host root. Use location_len instead of strlen, and skip the relative join when location_len is 0, so the second byte is never read. Closes GH-23467
This commit is contained in:
3 files changed
+40
-2
No files matched your search
@@ -59,6 +59,8 @@ PHP NEWS
|
||||
(Weilin Du)
|
||||
|
||||
- Standard:
|
||||
. Fixed an out-of-bounds read when following a redirect response with an
|
||||
empty Location header. (iliaal)
|
||||
. Fixed a memory leak in array_merge_recursive() when the recursive merge of
|
||||
an object converted to an array fails. (David Carlier)
|
||||
|
||||
|
||||
@@ -1052,7 +1052,7 @@ finish:
|
||||
|
||||
char *new_path = NULL;
|
||||
|
||||
if (strlen(header_info.location) < 8 ||
|
||||
if (header_info.location_len < 8 ||
|
||||
(strncasecmp(header_info.location, "http://", sizeof("http://")-1) &&
|
||||
strncasecmp(header_info.location, "https://", sizeof("https://")-1) &&
|
||||
strncasecmp(header_info.location, "ftp://", sizeof("ftp://")-1) &&
|
||||
@@ -1060,7 +1060,7 @@ finish:
|
||||
{
|
||||
char *loc_path = NULL;
|
||||
if (*header_info.location != '/') {
|
||||
if (*(header_info.location+1) != '\0' && resource->path) {
|
||||
if (header_info.location_len > 0 && resource->path) {
|
||||
char *s = strrchr(ZSTR_VAL(resource->path), '/');
|
||||
if (!s) {
|
||||
s = ZSTR_VAL(resource->path);
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
--TEST--
|
||||
Empty Location header must not over-read when building the redirect target
|
||||
--FILE--
|
||||
<?php
|
||||
$serverCode = <<<'CODE'
|
||||
$server = stream_socket_server("tcp://127.0.0.1:0", $errno, $errstr);
|
||||
phpt_notify_server_start($server);
|
||||
|
||||
for ($n = 0; $n < 4; $n++) {
|
||||
$conn = stream_socket_accept($server, 10);
|
||||
if (!$conn) {
|
||||
break;
|
||||
}
|
||||
$req = fgets($conn);
|
||||
while (trim(fgets($conn)) !== '') {}
|
||||
$uri = explode(' ', $req)[1];
|
||||
if ($n < 3) {
|
||||
fwrite($conn, "HTTP/1.1 302 Found\r\nLocation:\r\nContent-Length: 0\r\n\r\n");
|
||||
} else {
|
||||
$body = "uri=$uri";
|
||||
fwrite($conn, "HTTP/1.1 200 OK\r\nContent-Length: " . strlen($body) . "\r\n\r\n$body");
|
||||
}
|
||||
fclose($conn);
|
||||
}
|
||||
CODE;
|
||||
|
||||
$clientCode = <<<'CODE'
|
||||
$ctx = stream_context_create(['http' => ['follow_location' => 1]]);
|
||||
echo @file_get_contents("http://{{ ADDR }}/a/b", false, $ctx), "\n";
|
||||
CODE;
|
||||
|
||||
include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
?>
|
||||
--EXPECT--
|
||||
uri=/
|
||||
Reference in new issue
Block a user