From c870e312f11cc438e09a1a39bcdb038c68808e7b Mon Sep 17 00:00:00 2001 From: Kamil Tekiela Date: Sun, 8 Nov 2026 18:20:54 +0000 Subject: [PATCH] Fix GH-12899 --- NEWS | 1 + ext/standard/tests/file/gh12899.phpt | 35 ++++++++++++++++++++++++++++ main/streams/streams.c | 6 ++--- 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 ext/standard/tests/file/gh12899.phpt diff --git a/NEWS b/NEWS index 9ae8c7add120..d0e00a9d4b1e 100644 --- a/NEWS +++ b/NEWS @@ -56,6 +56,7 @@ PHP NEWS brigade. (ndossche, Matthias Goergens, Jakub Zelenka) . Fixed bug GH-24175 (Use-after-free when pclose() closes a stream from its user filter). (Edmond) + . Fixed bug GH-12899 (copy() target is blank). (Kamil Tekiela) 19 Nov 2026, PHP 8.4.28 diff --git a/ext/standard/tests/file/gh12899.phpt b/ext/standard/tests/file/gh12899.phpt new file mode 100644 index 000000000000..d678bee864e6 --- /dev/null +++ b/ext/standard/tests/file/gh12899.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-12899 (copy() target is blank in PHP 8.3.0) +--SKIPIF-- + +--FILE-- + 0); + +var_dump(copy($src, $dest)); +var_dump(file_get_contents($dest) === $expected); + +$in = fopen($src, 'r'); +$out = fopen($dest, 'w'); +var_dump(stream_copy_to_stream($in, $out) === strlen($expected)); +fclose($in); +fclose($out); +var_dump(file_get_contents($dest) === $expected); +?> +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/main/streams/streams.c b/main/streams/streams.c index 615ff04361ac..faafaf67b8a5 100644 --- a/main/streams/streams.c +++ b/main/streams/streams.c @@ -1763,9 +1763,9 @@ PHPAPI zend_result _php_stream_copy_to_stream_ex(php_stream *src, php_stream *de /* there may be more data; continue copying using the fallback code below */ } else if (result == 0) { - /* end of file */ - *len = haveread; - return SUCCESS; + /* Nothing was copied yet, but this does not prove end-of-file: some kernels return 0 + * for sources that are not empty, e.g. procfs files or VirtualBox shared folders. + * Fall back to classic copying, which detects end-of-file reliably. */ } else if (result < 0) { switch (errno) { case EINVAL: