Skip to content

Commit 8daed6d

Browse files
committed
Merge branch 'PHP-7.4' into PHP-8.0
* PHP-7.4: Fix #81145: copy() and stream_copy_to_stream() fail for +4GB files
2 parents df16fd1 + 2555efa commit 8daed6d

File tree

3 files changed

+59
-4
lines changed

3 files changed

+59
-4
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? 2021, PHP 8.0.9
44

5+
- Core:
6+
. Fixed bug #81145 (copy() and stream_copy_to_stream() fail for +4GB files).
7+
(cmb, Nikita)
8+
59
- Intl:
610
. Fixed bug #72809 (Locale::lookup() wrong result with canonicalize option).
711
(cmb)

ext/standard/tests/file/bug81145.phpt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
--TEST--
2+
Bug #81145 (copy() and stream_copy_to_stream() fail for +4GB files)
3+
--SKIPIF--
4+
<?php
5+
if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
6+
if (PHP_INT_SIZE !== 8) die("skip this test is for 64bit platforms only");
7+
if (disk_free_space(__DIR__) < 0x220000000) die("skip insuffient disk space");
8+
if (PHP_OS_FAMILY !== "Windows") {
9+
exec("fallocate -h", $output, $status);
10+
if ($status !== 0) die("skip fallocate(1) not available");
11+
}
12+
?>
13+
--FILE--
14+
<?php
15+
$src = __DIR__ . "/bug81145_src.bin";
16+
$dst = __DIR__ . "/bug81145_dst.bin";
17+
define('SIZE_4G', 0x100000000);
18+
19+
//Create file and append random content at the 4GB boundary
20+
if (PHP_OS_FAMILY !== "Windows") {
21+
exec("fallocate -l " . (SIZE_4G-0x100) . " " . escapeshellarg($src));
22+
} else {
23+
exec("fsutil file createnew " . escapeshellarg($src) . " " . (SIZE_4G-0x100));
24+
}
25+
$fp = fopen($src, "ab");
26+
fwrite($fp, random_bytes(0x200));
27+
fclose($fp);
28+
copy($src, $dst);
29+
if (filesize($src) !== filesize($dst)) {
30+
die("Files have different sizes!");
31+
}
32+
$f1 = fopen($src,'rb') or die("src open failed");
33+
$f2 = fopen($dst,'rb') or die("dst open failed");
34+
35+
//Seek to 4 GB boundary, as this is the location where the problem occurs
36+
fseek($f1, SIZE_4G - 0x100, SEEK_SET);
37+
fseek($f2, SIZE_4G - 0x100, SEEK_SET);
38+
echo (fread($f1,0x200) === fread($f2,0x200) ? "Identical" : "Copy failed");
39+
fclose($f1);
40+
fclose($f2);
41+
?>
42+
--CLEAN--
43+
<?php
44+
@unlink(__DIR__ . "/bug81145_src.bin");
45+
@unlink(__DIR__ . "/bug81145_dst.bin");
46+
?>
47+
--EXPECT--
48+
Identical

main/streams/plain_wrapper.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void
777777
{
778778
php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam;
779779
HANDLE hfile = (HANDLE)_get_osfhandle(fd);
780-
DWORD prot, acc, loffs = 0, delta = 0;
780+
DWORD prot, acc, loffs = 0, hoffs = 0, delta = 0;
781781
LARGE_INTEGER file_size;
782782

783783
switch (value) {
@@ -845,16 +845,19 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void
845845

846846
GetSystemInfo(&info);
847847
gran = info.dwAllocationGranularity;
848-
loffs = ((DWORD)range->offset / gran) * gran;
849-
delta = (DWORD)range->offset - loffs;
848+
ZEND_ASSERT(gran != 0 && (gran & (gran - 1)) == 0);
849+
size_t rounded_offset = (range->offset / gran) * gran;
850+
delta = range->offset - rounded_offset;
851+
loffs = (DWORD)rounded_offset;
852+
hoffs = (DWORD)(rounded_offset >> 32);
850853
}
851854

852855
/* MapViewOfFile()ing zero bytes would map to the end of the file; match *nix behavior instead */
853856
if (range->length + delta == 0) {
854857
return PHP_STREAM_OPTION_RETURN_ERR;
855858
}
856859

857-
data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, 0, loffs, range->length + delta);
860+
data->last_mapped_addr = MapViewOfFile(data->file_mapping, acc, hoffs, loffs, range->length + delta);
858861

859862
if (data->last_mapped_addr) {
860863
/* give them back the address of the start offset they requested */

0 commit comments

Comments
 (0)