Skip to content

Commit 4d4272d

Browse files
committed
[Support] Avoid a VirtualBox shared folders mmap bug
In acd8791, a call to FlushFileBuffers was added to work around a rare kernel bug. In 3b9b4d2, the scope of that workaround was limited, for performance reasons, as the flushes are quite expensive. On VirtualBox shared folders, closing a memory mapping that has been written to, also needs to be explicitly flushed, if renaming the output file before it is closed. Contrary to the kernel bug, this always happens on such mounts. In these cases, the output ends up as a file of the right size, but the contents are all zeros. The sequence to trigger the issue on the VirtualBox Shared Folders is this, summarized: file = CreateFile() mapping = CreateFileMapping(file) mem = MapViewOfFile() CloseHandle(mapping) write(mem) UnmapViewOfFile(mem) SetFileInformationByHandle(file, FileRenameInfo) CloseHandle(file) With this sequence, the output file always ends up with all zeros. See mstorsjo/llvm-mingw#393 for a full reproduction example. To avoid this issue, call FlushFileBuffers() when the file may reside on a VitualBox shared folder. As the flushes are expensive, only do them when the output isn't on a local file system. The issue with VirtualBox shared folders could also be fixed by calling FlushViewOfFile before UnmapViewOfFile, and doing that could be slightly less expensive than FlushFileBuffers. Empirically, the difference between the two is very small though, and as it's not easy to verify whether switching FlushFileBuffers to FlushViewOfFile helps with the rare kernel bug, keep using FlushFileBuffers for both cases, for code simplicity. This fixes downstream bug mstorsjo/llvm-mingw#393.
1 parent 08e4386 commit 4d4272d

File tree

1 file changed

+23
-2
lines changed

1 file changed

+23
-2
lines changed

llvm/lib/Support/Windows/Path.inc

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -959,15 +959,36 @@ void mapped_file_region::unmapImpl() {
959959

960960
::UnmapViewOfFile(Mapping);
961961

962-
if (Mode == mapmode::readwrite && Exe && hasFlushBufferKernelBug()) {
962+
if (Mode == mapmode::readwrite) {
963+
bool DoFlush = Exe && hasFlushBufferKernelBug();
963964
// There is a Windows kernel bug, the exact trigger conditions of which
964965
// are not well understood. When triggered, dirty pages are not properly
965966
// flushed and subsequent process's attempts to read a file can return
966967
// invalid data. Calling FlushFileBuffers on the write handle is
967968
// sufficient to ensure that this bug is not triggered.
968969
// The bug only occurs when writing an executable and executing it right
969970
// after, under high I/O pressure.
970-
::FlushFileBuffers(FileHandle);
971+
if (!DoFlush) {
972+
// Separately, on VirtualBox Shared Folder mounts, writes via memory
973+
// maps always end up unflushed (regardless of version of Windows),
974+
// unless flushed with this explicit call, if they are renamed with
975+
// SetFileInformationByHandle(FileRenameInfo) before closing the output
976+
// handle.
977+
//
978+
// As the flushing is quite expensive, use a heuristic to limit the
979+
// cases where we do the flushing. Only do the flushing if we aren't
980+
// sure we are on a local file system.
981+
bool IsLocal = false;
982+
SmallVector<wchar_t, 128> FinalPath;
983+
if (!realPathFromHandle(FileHandle, FinalPath)) {
984+
// Not checking the return value here - if the check fails, assume the
985+
// file isn't local.
986+
is_local_internal(FinalPath, IsLocal);
987+
}
988+
DoFlush = !IsLocal;
989+
}
990+
if (DoFlush)
991+
::FlushFileBuffers(FileHandle);
971992
}
972993

973994
::CloseHandle(FileHandle);

0 commit comments

Comments
 (0)