Index: lib/Support/Windows/Path.inc =================================================================== --- lib/Support/Windows/Path.inc +++ lib/Support/Windows/Path.inc @@ -854,16 +854,39 @@ Mapping = 0; } +static bool hasFlushBufferKernelBug() { + static llvm::VersionTuple ver = GetWindowsOSVersion(); + static Optional HasBug{ver < llvm::VersionTuple(10, 0, 0, 17763)}; + return *HasBug; +} + +static bool isEXE(StringRef Magic) { + static const char PEMagic[] = {'P', 'E', '\0', '\0'}; + if (Magic.startswith(StringRef("MZ")) && Magic.size() >= 0x3c + 4) { + uint32_t off = read32le(Magic.data() + 0x3c); + // PE/COFF file, either EXE or DLL. + if (Magic.substr(off).startswith( + StringRef(PEMagic, sizeof(PEMagic)))) + return true; + } + return false; +} + mapped_file_region::~mapped_file_region() { if (Mapping) { + + bool Exe = isEXE(StringRef((char *)Mapping, Size)); + ::UnmapViewOfFile(Mapping); - if (Mode == mapmode::readwrite) { + if (Mode == mapmode::readwrite && Exe && hasFlushBufferKernelBug()) { // There is a Windows kernel bug, the exact trigger conditions of which // are not well understood. When triggered, dirty pages are not properly // flushed and subsequent process's attempts to read a file can return // invalid data. Calling FlushFileBuffers on the write handle is // sufficient to ensure that this bug is not triggered. + // The bug only occurs when writing an executable and executing it right + // after, under high I/O pressure. ::FlushFileBuffers(FileHandle); } Index: lib/Support/Windows/WindowsSupport.h =================================================================== --- lib/Support/Windows/WindowsSupport.h +++ lib/Support/Windows/WindowsSupport.h @@ -41,6 +41,7 @@ #include "llvm/Config/config.h" // Get build system configuration settings #include "llvm/Support/Chrono.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/VersionTuple.h" #include #include #include @@ -71,6 +72,38 @@ Mask) != FALSE; } +typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) + +inline llvm::VersionTuple GetWindowsOSVersion() { + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + auto getVer = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (getVer) { + RTL_OSVERSIONINFOEXW info{}; + info.dwOSVersionInfoSize = sizeof(info); + if (getVer((PRTL_OSVERSIONINFOW)&info) == STATUS_SUCCESS) { + return llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0, info.dwBuildNumber); + } + } + } + + OSVERSIONINFOEX info; + ZeroMemory(&info, sizeof(OSVERSIONINFOEX)); + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); +#pragma warning(push) +#pragma warning(disable : 4996) + // Starting with Microsoft SDK for Windows 8.1, this function is deprecated + // in favor of the new Windows Version Helper APIs. Since we don't specify a + // minimum SDK version, it's easier to simply disable the warning rather than + // try to support both APIs. + if (GetVersionEx((LPOSVERSIONINFO)&info) == 0) + return llvm::VersionTuple(); +#pragma warning(pop) + + return llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0, info.dwBuildNumber); +} + inline bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) { if (!ErrMsg) return true;