New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix GH-13203 file_put_contents fail on strings over 4GB on Windows #13205
Conversation
actually exist a macro to do the cast (not implemented exactly how I would have preferred, but it's fine. is UINT_MAX actually guaranteed to be unsigned int? does specifications actually dictate that, or did the compiler developers just chose to implement it like that?)
I'm not very familiar with that, but did this solve the problem? |
@SakiTakamachi don't actually know. Was hoping WINDOWS_X64_ZTS CI tester would tell me, but I can't actually see if the test passed or was skipped due to system resource constraints, I tried getting MSVC to work, but that's seemingly non-trivial: MSVC2022 is not yet supported, and I would need to download MSVC2017 or something to actually compile PHP on MSVC? I gave up. Hopefully a developer actually equipped with MSVC can test it |
I see, if I can look it this week, I'll check this out. |
I'm familiar with doing Windows debugs and I have a dedicated VM to do so, so I can also check tonight |
It might be potentially better to enable chunking for big writes so we don't need to write it all in one go. |
PHP_WIN32 is also set on 64-bit Windows. This is because Win32 is actually the name of the subsystem, not the bitness. |
Just checked, CI skipped your test. |
I tried testing this, but my VM just dies when trying to do large I/O, so someone else will have to ¯_(ツ)_/¯ |
I have a real machine so I can take a look later this week. |
@nielsdos |
good point.. @nielsdos could you check if your VM hits the
condition on line 359? maybe the real issue is in ZEND_SIZE_T_UINT_OVFL() failing? or maybe i'm looking in the wrong place completely |
Yes that condition is hit and the body is executed. |
@nielsdos try again now.
#include <iostream>
#include <stdio.h>
#include <stdint.h>
#include <BaseTsd.h>
#include <io.h>
#include<thread>
#include<chrono>
int main()
{
FILE* h;
tmpfile_s(&h);
int h_int = _fileno(h);
std::string gb5(size_t(5) * 1024 * 1024 * 1024, 'a');
while (gb5.length() > 0) {
int written_now = _write(h_int, gb5.data(), gb5.length() > UINT_MAX ? UINT_MAX-5 : gb5.length());
std::cout << "written_now" << written_now << std::endl;
gb5.erase(0, written_now);
std::this_thread::sleep_for(std::chrono::seconds(3));
}
}
|
The patch currently works. |
so basically, if you try to use _write() above INT_MAX, it becomes impossible to differentiate between error and success 😵 |
The following works too (tested), and do less system calls probably: $ git diff main\streams\plain_wrapper.c
diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c
index e9a30f3334..c5aa807c4f 100644
--- a/main/streams/plain_wrapper.c
+++ b/main/streams/plain_wrapper.c
@@ -358,10 +358,11 @@ static ssize_t php_stdiop_write(php_stream *stream, const char *buf, size_t coun
count = UINT_MAX;
}
bytes_written = _write(data->fd, buf, (unsigned int)count);
+ if (errno != 0) {
#else
ssize_t bytes_written = write(data->fd, buf, count);
-#endif
if (bytes_written < 0) {
+#endif
if (PHP_IS_TRANSIENT_ERROR(errno)) {
return 0;
}
|
Indeed, a real footgun. It's possible to check errno instead but yeah... |
makes me uneasy, bytes_written may be negative and then |
Yeah that's true. And would also cause problems in 32-bit where sizeof(ssize_t) == sizeof(int)... |
It seemed like there was a lot of discussion going on while I was getting ready in the morning. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks reasonable, just test needs to not use tmpfile and one NIT possibly.
time to merge this? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested this change and it works for me.
Code looks good. Waiting if bukka has anything left to say on this as he was the one requesting changes 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks mostly good, just one minor thing. I will also try to find some time to try the test on my local Windows to see if that actually works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just tested it on Windows and after modifying that skip check as suggested, it seems to work fine. Fails without the change and pass with the change...
Co-authored-by: Jakub Zelenka <bukka@php.net>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me except for one nits about code style.
(edit)
But I'm not as familiar with this as other members, so you should wait for other members' opinions.
Co-authored-by: Saki Takamachi <34942839+SakiTakamachi@users.noreply.github.com>
MSDN pages mention the buffer size upper limit is INT_MAX not UINT_MAX. inspired by phpGH-13205.
fix GH-13203