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 #include <windows.h>
25 
26 // This must be included after windows.h.
27 #include <psapi.h>
28 
29 namespace fuzzer {
30 
31 static const FuzzingOptions* HandlerOpt = nullptr;
32 
33 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
34   switch (ExceptionInfo->ExceptionRecord->ExceptionCode) {
35     case EXCEPTION_ACCESS_VIOLATION:
36     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
37     case EXCEPTION_STACK_OVERFLOW:
38       if (HandlerOpt->HandleSegv)
39         Fuzzer::StaticCrashSignalCallback();
40       break;
41     case EXCEPTION_DATATYPE_MISALIGNMENT:
42     case EXCEPTION_IN_PAGE_ERROR:
43       if (HandlerOpt->HandleBus)
44         Fuzzer::StaticCrashSignalCallback();
45       break;
46     case EXCEPTION_ILLEGAL_INSTRUCTION:
47     case EXCEPTION_PRIV_INSTRUCTION:
48       if (HandlerOpt->HandleIll)
49         Fuzzer::StaticCrashSignalCallback();
50       break;
51     case EXCEPTION_FLT_DENORMAL_OPERAND:
52     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
53     case EXCEPTION_FLT_INEXACT_RESULT:
54     case EXCEPTION_FLT_INVALID_OPERATION:
55     case EXCEPTION_FLT_OVERFLOW:
56     case EXCEPTION_FLT_STACK_CHECK:
57     case EXCEPTION_FLT_UNDERFLOW:
58     case EXCEPTION_INT_DIVIDE_BY_ZERO:
59     case EXCEPTION_INT_OVERFLOW:
60       if (HandlerOpt->HandleFpe)
61         Fuzzer::StaticCrashSignalCallback();
62       break;
63     // This is an undocumented exception code corresponding to a Visual C++
64     // Exception.
65     //
66     // See: https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273
67     case 0xE06D7363:
68       if (HandlerOpt->HandleWinExcept)
69         Fuzzer::StaticCrashSignalCallback();
70       break;
71       // TODO: Handle (Options.HandleXfsz)
72   }
73   return EXCEPTION_CONTINUE_SEARCH;
74 }
75 
76 BOOL WINAPI CtrlHandler(DWORD dwCtrlType) {
77   switch (dwCtrlType) {
78     case CTRL_C_EVENT:
79       if (HandlerOpt->HandleInt)
80         Fuzzer::StaticInterruptCallback();
81       return TRUE;
82     case CTRL_BREAK_EVENT:
83       if (HandlerOpt->HandleTerm)
84         Fuzzer::StaticInterruptCallback();
85       return TRUE;
86   }
87   return FALSE;
88 }
89 
90 void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
91   Fuzzer::StaticAlarmCallback();
92 }
93 
94 class TimerQ {
95   HANDLE TimerQueue;
96  public:
97   TimerQ() : TimerQueue(NULL) {}
98   ~TimerQ() {
99     if (TimerQueue)
100       DeleteTimerQueueEx(TimerQueue, NULL);
101   }
102   void SetTimer(int Seconds) {
103     if (!TimerQueue) {
104       TimerQueue = CreateTimerQueue();
105       if (!TimerQueue) {
106         Printf("libFuzzer: CreateTimerQueue failed.\n");
107         exit(1);
108       }
109     }
110     HANDLE Timer;
111     if (!CreateTimerQueueTimer(&Timer, TimerQueue, AlarmHandler, NULL,
112         Seconds*1000, Seconds*1000, 0)) {
113       Printf("libFuzzer: CreateTimerQueueTimer failed.\n");
114       exit(1);
115     }
116   }
117 };
118 
119 static TimerQ Timer;
120 
121 static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); }
122 
123 void SetSignalHandler(const FuzzingOptions& Options) {
124   HandlerOpt = &Options;
125 
126   if (Options.HandleAlrm && Options.UnitTimeoutSec > 0)
127     Timer.SetTimer(Options.UnitTimeoutSec / 2 + 1);
128 
129   if (Options.HandleInt || Options.HandleTerm)
130     if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) {
131       DWORD LastError = GetLastError();
132       Printf("libFuzzer: SetConsoleCtrlHandler failed (Error code: %lu).\n",
133         LastError);
134       exit(1);
135     }
136 
137   if (Options.HandleSegv || Options.HandleBus || Options.HandleIll ||
138       Options.HandleFpe || Options.HandleWinExcept)
139     SetUnhandledExceptionFilter(ExceptionHandler);
140 
141   if (Options.HandleAbrt)
142     if (SIG_ERR == signal(SIGABRT, CrashHandler)) {
143       Printf("libFuzzer: signal failed with %d\n", errno);
144       exit(1);
145     }
146 }
147 
148 void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); }
149 
150 unsigned long GetPid() { return GetCurrentProcessId(); }
151 
152 size_t GetPeakRSSMb() {
153   PROCESS_MEMORY_COUNTERS info;
154   if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)))
155     return 0;
156   return info.PeakWorkingSetSize >> 20;
157 }
158 
159 FILE *OpenProcessPipe(const char *Command, const char *Mode) {
160   return _popen(Command, Mode);
161 }
162 
163 int CloseProcessPipe(FILE *F) {
164   return _pclose(F);
165 }
166 
167 int ExecuteCommand(const Command &Cmd) {
168   std::string CmdLine = Cmd.toString();
169   return system(CmdLine.c_str());
170 }
171 
172 bool ExecuteCommand(const Command &Cmd, std::string *CmdOutput) {
173   FILE *Pipe = _popen(Cmd.toString().c_str(), "r");
174   if (!Pipe)
175     return false;
176 
177   if (CmdOutput) {
178     char TmpBuffer[128];
179     while (fgets(TmpBuffer, sizeof(TmpBuffer), Pipe))
180       CmdOutput->append(TmpBuffer);
181   }
182   return _pclose(Pipe) == 0;
183 }
184 
185 const void *SearchMemory(const void *Data, size_t DataLen, const void *Patt,
186                          size_t PattLen) {
187   // TODO: make this implementation more efficient.
188   const char *Cdata = (const char *)Data;
189   const char *Cpatt = (const char *)Patt;
190 
191   if (!Data || !Patt || DataLen == 0 || PattLen == 0 || DataLen < PattLen)
192     return NULL;
193 
194   if (PattLen == 1)
195     return memchr(Data, *Cpatt, DataLen);
196 
197   const char *End = Cdata + DataLen - PattLen + 1;
198 
199   for (const char *It = Cdata; It < End; ++It)
200     if (It[0] == Cpatt[0] && memcmp(It, Cpatt, PattLen) == 0)
201       return It;
202 
203   return NULL;
204 }
205 
206 std::string DisassembleCmd(const std::string &FileName) {
207   Vector<std::string> command_vector;
208   command_vector.push_back("dumpbin /summary > nul");
209   if (ExecuteCommand(Command(command_vector)) == 0)
210     return "dumpbin /disasm " + FileName;
211   Printf("libFuzzer: couldn't find tool to disassemble (dumpbin)\n");
212   exit(1);
213 }
214 
215 std::string SearchRegexCmd(const std::string &Regex) {
216   return "findstr /r \"" + Regex + "\"";
217 }
218 
219 void DiscardOutput(int Fd) {
220   FILE* Temp = fopen("nul", "w");
221   if (!Temp)
222     return;
223   _dup2(_fileno(Temp), Fd);
224   fclose(Temp);
225 }
226 
227 } // namespace fuzzer
228 
229 #endif // LIBFUZZER_WINDOWS
230