xref: /freebsd/contrib/llvm-project/compiler-rt/lib/fuzzer/FuzzerUtilWindows.cpp (revision 718519f4efc71096422fc71dab90b2a3369871ff)
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