From 95e001e27504e02d13dbcc1096daba640df34bcc Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Sun, 29 Mar 2020 14:45:05 +0200 Subject: [PATCH 1/3] Fix #79423: copy command is limited to size of file it can copy Passing `NULL` as `lpFileSizeHigh` to `GetFileSize()` gives wrong results for files larger than 0xFFFFFFFF bytes. We fix this by using `GetFileSizeEx()`, and proceed with `MIN(file_size, SIZE_MAX)`. --- ext/standard/tests/streams/bug79423.phpt | 31 ++++++++++++++++++++++++ main/streams/plain_wrapper.c | 15 +++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 ext/standard/tests/streams/bug79423.phpt diff --git a/ext/standard/tests/streams/bug79423.phpt b/ext/standard/tests/streams/bug79423.phpt new file mode 100644 index 0000000000000..3423312453973 --- /dev/null +++ b/ext/standard/tests/streams/bug79423.phpt @@ -0,0 +1,31 @@ +--TEST-- +Bug #79423 (copy command is limited to size of file it can copy) +--SKIPIF-- + +--FILE-- + +--CLEAN-- + +--EXPECT-- +bool(true) +bool(true) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 2b81912857626..2e7cbb9272e5d 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -749,6 +749,7 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void php_stream_mmap_range *range = (php_stream_mmap_range*)ptrparam; HANDLE hfile = (HANDLE)_get_osfhandle(fd); DWORD prot, acc, loffs = 0, delta = 0; + LARGE_INTEGER file_size; switch (value) { case PHP_STREAM_MMAP_SUPPORTED: @@ -785,7 +786,19 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void return PHP_STREAM_OPTION_RETURN_ERR; } - size = GetFileSize(hfile, NULL); + if (!GetFileSizeEx(hfile, &file_size)) { + return PHP_STREAM_OPTION_RETURN_ERR; + } +# if defined(_WIN64) + size = file_size.QuadPart; +# else + if (file_size.HighPart) { + /* file is too large; process as much as possible */ + size = SIZE_MAX; + } else { + size = file_size.LowPart; + } +# endif if (range->offset > size) { range->offset = size; } From 4ec9b0d10ff3c9f28aa3e9c5d2a706a9419a8cca Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Tue, 8 Sep 2020 14:49:15 +0200 Subject: [PATCH 2/3] Drop regression test The test is too resource hungry for CI, and also not unlikely for other test environments, since we're creating a 4GB file which is fully copied. --- ext/standard/tests/streams/bug79423.phpt | 31 ------------------------ 1 file changed, 31 deletions(-) delete mode 100644 ext/standard/tests/streams/bug79423.phpt diff --git a/ext/standard/tests/streams/bug79423.phpt b/ext/standard/tests/streams/bug79423.phpt deleted file mode 100644 index 3423312453973..0000000000000 --- a/ext/standard/tests/streams/bug79423.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -Bug #79423 (copy command is limited to size of file it can copy) ---SKIPIF-- - ---FILE-- - ---CLEAN-- - ---EXPECT-- -bool(true) -bool(true) From 16a5373f13808b4a125f3510f74b6b13a8366e18 Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 9 Sep 2020 19:21:41 +0200 Subject: [PATCH 3/3] More closely match POSIX semantics of stat() POSIX mandates that `stat()` should fail, if the file size in bytes cannot be correctly represented in the stat buffer's `st_size` member. We match that behavior, except that we basically treat `st_size` as unsigned here. We also fix a temporary resource leak, introduced by a recent commit. --- main/streams/plain_wrapper.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 2e7cbb9272e5d..d00a6efe29d28 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -787,14 +787,17 @@ static int php_stdiop_set_option(php_stream *stream, int option, int value, void } if (!GetFileSizeEx(hfile, &file_size)) { + CloseHandle(data->file_mapping); + data->file_mapping = NULL; return PHP_STREAM_OPTION_RETURN_ERR; } # if defined(_WIN64) size = file_size.QuadPart; # else if (file_size.HighPart) { - /* file is too large; process as much as possible */ - size = SIZE_MAX; + CloseHandle(data->file_mapping); + data->file_mapping = NULL; + return PHP_STREAM_OPTION_RETURN_ERR; } else { size = file_size.LowPart; }