1 //===- FuzzerUtilWindows.cpp - Misc utils for Windows. --------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // Misc utils implementation for Windows. 9 //===----------------------------------------------------------------------===// 10 #include "FuzzerPlatform.h" 11 #if LIBFUZZER_WINDOWS 12 #include "FuzzerCommand.h" 13 #include "FuzzerIO.h" 14 #include "FuzzerInternal.h" 15 #include <cassert> 16 #include <chrono> 17 #include <cstring> 18 #include <errno.h> 19 #include <io.h> 20 #include <iomanip> 21 #include <signal.h> 22 #include <stdio.h> 23 #include <sys/types.h> 24 // clang-format off 25 #include <windows.h> 26 // These must be included after windows.h. 27 // archicture need to be set before including 28 // libloaderapi 29 #include <libloaderapi.h> 30 #include <stringapiset.h> 31 #include <psapi.h> 32 // clang-format on 33 34 namespace fuzzer { 35 36 static const FuzzingOptions* HandlerOpt = nullptr; 37 38 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { 39 switch (ExceptionInfo->ExceptionRecord->ExceptionCode) { 40 case EXCEPTION_ACCESS_VIOLATION: 41 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: 42 case EXCEPTION_STACK_OVERFLOW: 43 if (HandlerOpt->HandleSegv) 44 Fuzzer::StaticCrashSignalCallback(); 45 break; 46 case EXCEPTION_DATATYPE_MISALIGNMENT: 47 case EXCEPTION_IN_PAGE_ERROR: 48 if (HandlerOpt->HandleBus) 49 Fuzzer::StaticCrashSignalCallback(); 50 break; 51 case EXCEPTION_ILLEGAL_INSTRUCTION: 52 case EXCEPTION_PRIV_INSTRUCTION: 53 if (HandlerOpt->HandleIll) 54 Fuzzer::StaticCrashSignalCallback(); 55 break; 56 case EXCEPTION_FLT_DENORMAL_OPERAND: 57 case EXCEPTION_FLT_DIVIDE_BY_ZERO: 58 case EXCEPTION_FLT_INEXACT_RESULT: 59 case EXCEPTION_FLT_INVALID_OPERATION: 60 case EXCEPTION_FLT_OVERFLOW: 61 case EXCEPTION_FLT_STACK_CHECK: 62 case EXCEPTION_FLT_UNDERFLOW: 63 case EXCEPTION_INT_DIVIDE_BY_ZERO: 64 case EXCEPTION_INT_OVERFLOW: 65 if (HandlerOpt->HandleFpe) 66 Fuzzer::StaticCrashSignalCallback(); 67 break; 68 // This is an undocumented exception code corresponding to a Visual C++ 69 // Exception. 70 // 71 // See: https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 72 case 0xE06D7363: 73 if (HandlerOpt->HandleWinExcept) 74 Fuzzer::StaticCrashSignalCallback(); 75 break; 76 // TODO: Handle (Options.HandleXfsz) 77 } 78 return EXCEPTION_CONTINUE_SEARCH; 79 } 80 81 BOOL WINAPI CtrlHandler(DWORD dwCtrlType) { 82 switch (dwCtrlType) { 83 case CTRL_C_EVENT: 84 if (HandlerOpt->HandleInt) 85 Fuzzer::StaticInterruptCallback(); 86 return TRUE; 87 case CTRL_BREAK_EVENT: 88 if (HandlerOpt->HandleTerm) 89 Fuzzer::StaticInterruptCallback(); 90 return TRUE; 91 } 92 return FALSE; 93 } 94 95 void CALLBACK AlarmHandler(PVOID, BOOLEAN) { 96 Fuzzer::StaticAlarmCallback(); 97 } 98 99 class TimerQ { 100 HANDLE TimerQueue; 101 public: 102 TimerQ() : TimerQueue(NULL) {} 103 ~TimerQ() { 104 if (TimerQueue) 105 DeleteTimerQueueEx(TimerQueue, NULL); 106 } 107 void SetTimer(int Seconds) { 108 if (!TimerQueue) { 109 TimerQueue = CreateTimerQueue(); 110 if (!TimerQueue) { 111 Printf("libFuzzer: CreateTimerQueue failed.\n"); 112 exit(1); 113 } 114 } 115 HANDLE Timer; 116 if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL, 117 Seconds*1000, Seconds*1000, 0)) { 118 Printf("libFuzzer: CreateTimerQueueTimer failed.\n"); 119 exit(1); 120 } 121 } 122 }; 123 124 static TimerQ Timer; 125 126 static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); } 127 128 void SetSignalHandler(const FuzzingOptions& Options) { 129 HandlerOpt = &Options; 130 131 if (Options.HandleAlrm && Options.UnitTimeoutSec > 0) 132 Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1); 133 134 if (Options.HandleInt || Options.HandleTerm) 135 if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { 136 DWORD LastError = GetLastError(); 137 Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n", 138 LastError); 139 exit(1); 140 } 141 142 if (Options.HandleSegv || Options.HandleBus || Options.HandleIll || 143 Options.HandleFpe || Options.HandleWinExcept) 144 SetUnhandledExceptionFilter(ExceptionHandler); 145 146 if (Options.HandleAbrt) 147 if (SIG_ERR == signal(SIGABRT, CrashHandler)) { 148 Printf("libFuzzer: signal failed with %d\n", errno); 149 exit(1); 150 } 151 } 152 153 void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); } 154 155 unsigned long GetPid() { return GetCurrentProcessId(); } 156 157 size_t GetPeakRSSMb() { 158 PROCESS_MEMORY_COUNTERS info; 159 if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info))) 160 return 0; 161 return info.PeakWorkingSetSize >> 20; 162 } 163 164 FILE *OpenProcessPipe(const char *Command, const char *Mode) { 165 return _popen(Command, Mode); 166 } 167 168 int CloseProcessPipe(FILE *F) { 169 return _pclose(F); 170 } 171 172 int ExecuteCommand(const Command &Cmd) { 173 std::string CmdLine = Cmd.toString(); 174 return system(CmdLine.c_str()); 175 } 176 177 bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) { 178 FILE *Pipe = _popen(Cmd.toString().c_str(), "r"); 179 if (!Pipe) 180 return false; 181 182 if (CmdOutput) { 183 char TmpBuffer[128]; 184 while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe)) 185 CmdOutput->append(TmpBuffer); 186 } 187 return _pclose(Pipe) == 0; 188 } 189 190 const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt, 191 size_t PattLen) { 192 // TODO: make this implementation more efficient. 193 const char *Cdata = (const char *)Data; 194 const char *Cpatt = (const char *)Patt; 195 196 if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen) 197 return NULL; 198 199 if (PattLen == 1) 200 return memchr(Data, *Cpatt, DataLen); 201 202 const char *End = Cdata + DataLen - PattLen + 1; 203 204 for (const char *It = Cdata; It < End; ++It) 205 if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0) 206 return It; 207 208 return NULL; 209 } 210 211 std::string DisassembleCmd(const std::string &FileName) { 212 std::vector<std::string> command_vector; 213 command_vector.push_back("dumpbin /summary > nul"); 214 if (ExecuteCommand(Command(command_vector)) == 0) 215 return "dumpbin /disasm " + FileName; 216 Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n"); 217 exit(1); 218 } 219 220 std::string SearchRegexCmd(const std::string &Regex) { 221 return "findstr /r \"" + Regex + "\""; 222 } 223 224 void DiscardOutput(int Fd) { 225 FILE* Temp = fopen("nul", "w"); 226 if (!Temp) 227 return; 228 _dup2(_fileno(Temp), Fd); 229 fclose(Temp); 230 } 231 232 size_t PageSize() { 233 static size_t PageSizeCached = []() -> size_t { 234 SYSTEM_INFO si; 235 GetSystemInfo(&si); 236 return si.dwPageSize; 237 }(); 238 return PageSizeCached; 239 } 240 241 void SetThreadName(std::thread &thread, const std::string &name) { 242 #ifndef __MINGW32__ 243 // Not setting the thread name in MinGW environments. MinGW C++ standard 244 // libraries can either use native Windows threads or pthreads, so we 245 // don't know with certainty what kind of thread handle we're getting 246 // from thread.native_handle() here. 247 typedef HRESULT(WINAPI * proc)(HANDLE, PCWSTR); 248 HMODULE kbase = GetModuleHandleA("KernelBase.dll"); 249 proc ThreadNameProc = 250 reinterpret_cast<proc>(GetProcAddress(kbase, "SetThreadDescription")); 251 if (ThreadNameProc) { 252 std::wstring buf; 253 auto sz = MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, nullptr, 0); 254 if (sz > 0) { 255 buf.resize(sz); 256 if (MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, &buf[0], sz) > 0) { 257 (void)ThreadNameProc(thread.native_handle(), buf.c_str()); 258 } 259 } 260 } 261 #endif 262 } 263 264 } // namespace fuzzer 265 266 #endif // LIBFUZZER_WINDOWS 267