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
34namespace fuzzer {
35
36static const FuzzingOptions* HandlerOpt = nullptr;
37
38static 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
81BOOL 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
95void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
96 Fuzzer::StaticAlarmCallback();
97}
98
99class 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
124static TimerQ Timer;
125
126static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); }
127
128void 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
153void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); }
154
155unsigned long GetPid() { return GetCurrentProcessId(); }
156
157size_t GetPeakRSSMb() {
158 PROCESS_MEMORY_COUNTERS info;
159 if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)))
160 return 0;
161 return info.PeakWorkingSetSize >> 20;
162}
163
164FILE *OpenProcessPipe(const char *Command, const char *Mode) {
165 return _popen(Command, Mode);
166}
167
168int CloseProcessPipe(FILE *F) {
169 return _pclose(F);
170}
171
172int ExecuteCommand(const Command &Cmd) {
173 std::string CmdLine = Cmd.toString();
174 return system(CmdLine.c_str());
175}
176
177bool 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
190const 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
211std::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
220std::string SearchRegexCmd(const std::string &Regex) {
221 return "findstr /r \"" + Regex + "\"";
222}
223
224void DiscardOutput(int Fd) {
225 FILE* Temp = fopen("nul", "w");
226 if (!Temp)
227 return;
228 _dup2(_fileno(Temp), Fd);
229 fclose(Temp);
230}
231
232size_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
241void SetThreadName(std::thread &thread, const std::string &name) {
242 typedef HRESULT(WINAPI * proc)(HANDLE, PCWSTR);
243 HMODULE kbase = GetModuleHandleA("KernelBase.dll");
244 proc ThreadNameProc =
245 reinterpret_cast<proc>(GetProcAddress(kbase, "SetThreadDescription"));
246 if (ThreadNameProc) {
247 std::wstring buf;
248 auto sz = MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, nullptr, 0);
249 if (sz > 0) {
250 buf.resize(sz);
251 if (MultiByteToWideChar(CP_UTF8, 0, name.data(), -1, &buf[0], sz) > 0) {
252 (void)ThreadNameProc(thread.native_handle(), buf.c_str());
253 }
254 }
255 }
256}
257
258} // namespace fuzzer
259
260#endif // LIBFUZZER_WINDOWS
261