Skip to content

Commit 2343791

Browse files
divinity76bukka
authored andcommitted
Fix GH-13203: file_put_contents fail on strings over 4GB on Windows
Closes GH-13205
1 parent a284c3e commit 2343791

File tree

3 files changed

+71
-5
lines changed

3 files changed

+71
-5
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ PHP NEWS
3232
. Fixed bug GH-11808 (Live filesystem modified by tests). (nielsdos)
3333
. Fixed GH-13402 (Added validation of `\n` in $additional_headers of mail()).
3434
(SakiTakamachi)
35+
. Fixed bug GH-13203 (file_put_contents fail on strings over 4GB on Windows).
36+
(divinity76)
3537

3638
- XML:
3739
. Fixed bug GH-13517 (Multiple test failures when building with
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
--TEST--
2+
Test file_put_contents() function with 5GB string
3+
--SKIPIF--
4+
<?php
5+
if (PHP_INT_SIZE < 5) {
6+
// 4=4gb, 5=549gb, 8=9exabytes
7+
skip("skip PHP_INT_SIZE<5 will not fit test string in RAM");
8+
}
9+
if (getenv('SKIP_SLOW_TESTS')) {
10+
die('skip slow test');
11+
}
12+
function get_system_memory(): int|float|false
13+
{
14+
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
15+
// Windows-based memory check
16+
@exec('wmic OS get FreePhysicalMemory', $output);
17+
if (isset($output[1])) {
18+
return ((int)trim($output[1])) * 1024;
19+
}
20+
} else {
21+
// Unix/Linux-based memory check
22+
$memInfo = @file_get_contents("/proc/meminfo");
23+
if ($memInfo) {
24+
preg_match('/MemFree:\s+(\d+) kB/', $memInfo, $matches);
25+
return $matches[1] * 1024; // Convert to bytes
26+
}
27+
}
28+
return false;
29+
}
30+
if (get_system_memory() < 10 * 1024 * 1024 * 1024) {
31+
die('skip Reason: Insufficient RAM (less than 10GB)');
32+
}
33+
$tmpfile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "test_file_put_contents_5gb.bin";
34+
$tmpfileh = fopen($tmpfile, "wb");
35+
if ($tmpfileh === false) {
36+
die('skip Reason: Unable to create temporary file');
37+
}
38+
fclose($tmpfileh);
39+
unlink($tmpfile);
40+
if (disk_free_space(dirname($tmpfile)) < 10 * 1024 * 1024 * 1024) {
41+
die('skip Reason: Insufficient disk space (less than 10GB)');
42+
}
43+
?>
44+
--INI--
45+
memory_limit=6G
46+
--FILE--
47+
<?php
48+
$tmpfile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . "test_file_put_contents_5gb.bin";
49+
$large_string = str_repeat('a', 5 * 1024 * 1024 * 1024);
50+
$result = file_put_contents($tmpfile, $large_string);
51+
if ($result !== strlen($large_string)) {
52+
echo "Could only write $result bytes of " . strlen($large_string) . " bytes.";
53+
var_dump(error_get_last());
54+
} else {
55+
echo "File written successfully.";
56+
}
57+
clearstatcache(true, $tmpfile);
58+
if (file_exists($tmpfile)) {
59+
unlink($tmpfile);
60+
}
61+
?>
62+
--CLEAN--
63+
<?php
64+
@unlink(sys_get_temp_dir() . DIRECTORY_SEPARATOR . "test_file_put_contents_5gb.bin");
65+
?>
66+
--EXPECT--
67+
File written successfully.

main/streams/plain_wrapper.c

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
# include "win32/time.h"
4141
# include "win32/ioutil.h"
4242
# include "win32/readdir.h"
43+
# include <limits.h>
4344
#endif
4445

4546
#define php_stream_fopen_from_fd_int(fd, mode, persistent_id) _php_stream_fopen_from_fd_int((fd), (mode), (persistent_id) STREAMS_CC)
@@ -353,11 +354,7 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun
353354

354355
if (data->fd >= 0) {
355356
#ifdef PHP_WIN32
356-
ssize_t bytes_written;
357-
if (ZEND_SIZE_T_UINT_OVFL(count)) {
358-
count = UINT_MAX;
359-
}
360-
bytes_written = _write(data->fd, buf, (unsigned int)count);
357+
ssize_t bytes_written = _write(data->fd, buf, (unsigned int)(count > INT_MAX ? INT_MAX : count));
361358
#else
362359
ssize_t bytes_written = write(data->fd, buf, count);
363360
#endif

0 commit comments

Comments
 (0)