Reland "fix hanging of write on Windows"

This is a reland of eb075dcc21

Original change's description:
> fix hanging of write on Windows
> 
> When using File::Write(), Var size in int64_t is casted to DWORD(unsigned long). When Var size is out of DWORD range, casted value will be zero. Before calling into File::Write() on Windows, validate the size.
> 
> Bug: https://github.com/dart-lang/sdk/issues/40339
> Change-Id: I36fade62dfa3025f418405cb3e45c286dd6b7db1
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134440
> Reviewed-by: Zach Anderson <zra@google.com>
> Commit-Queue: Zichang Guo <zichangguo@google.com>

Bug: https://github.com/dart-lang/sdk/issues/40339
Change-Id: I5a07c58709c62b996a55a76272636602dc80e20d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134783
Commit-Queue: Zichang Guo <zichangguo@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
This commit is contained in:
Zichang Guo 2020-02-10 22:16:55 +00:00 committed by commit-bot@chromium.org
parent 85c197a848
commit af4a3112c4
4 changed files with 32 additions and 3 deletions

View file

@ -161,7 +161,8 @@ int64_t File::Read(void* buffer, int64_t num_bytes) {
}
int64_t File::Write(const void* buffer, int64_t num_bytes) {
ASSERT(handle_->fd() >= 0);
// Invalid argument error will pop if num_bytes exceeds the limit.
ASSERT(handle_->fd() >= 0 && num_bytes <= kMaxInt32);
return TEMP_FAILURE_RETRY(write(handle_->fd(), buffer, num_bytes));
}

View file

@ -56,7 +56,13 @@ bool File::WriteFully(const void* buffer, int64_t num_bytes) {
int64_t remaining = num_bytes;
const char* current_buffer = reinterpret_cast<const char*>(buffer);
while (remaining > 0) {
int64_t bytes_written = Write(current_buffer, remaining);
// On Windows, narrowing conversion from int64_t to DWORD will cause
// unexpected error.
// On MacOS, a single write() with more than kMaxInt32 will have
// "invalid argument" error.
// Therefore, limit the size for single write.
int64_t byte_to_write = num_bytes > kMaxInt32 ? kMaxInt32 : num_bytes;
int64_t bytes_written = Write(current_buffer, byte_to_write);
if (bytes_written < 0) {
return false;
}

View file

@ -146,7 +146,8 @@ int64_t File::Read(void* buffer, int64_t num_bytes) {
int64_t File::Write(const void* buffer, int64_t num_bytes) {
int fd = handle_->fd();
ASSERT(fd >= 0);
// Avoid narrowing conversion
ASSERT(fd >= 0 && num_bytes <= MAXDWORD && num_bytes >= 0);
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
DWORD written = 0;
BOOL result = WriteFile(handle, buffer, num_bytes, &written, NULL);

View file

@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";
@ -27,6 +28,25 @@ testWriteAsStringSync(dir) {
Expect.equals('$data$data', f.readAsStringSync());
}
testWriteWithLargeList(dir) {
// 0x100000000 exceeds the maximum of unsigned long.
// This should no longer hang.
// Issue: https://github.com/dart-lang/sdk/issues/40339
var bytes;
try {
bytes = Uint8List(0x100000000);
} catch (e) {
// Create a big Uint8List may lead to OOM. This is acceptable.
Expect.isTrue(e.toString().contains('Out of Memory'));
return;
}
if (Platform.isWindows) {
File('NUL').writeAsBytesSync(bytes);
} else {
File('/dev/null').writeAsBytesSync(bytes);
}
}
Future testWriteAsBytes(dir) {
var completer = new Completer();
var f = new File('${dir.path}/bytes.txt');
@ -73,6 +93,7 @@ main() {
var tempDir = Directory.systemTemp.createTempSync('dart_file_write_as');
testWriteAsBytesSync(tempDir);
testWriteAsStringSync(tempDir);
testWriteWithLargeList(tempDir);
testWriteAsBytes(tempDir).then((_) {
return testWriteAsString(tempDir);
}).then((_) {