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
29namespace fuzzer {
30
31static const FuzzingOptions* HandlerOpt = nullptr;
32
33static 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
76BOOL 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
90void CALLBACK AlarmHandler(PVOID, BOOLEAN) {
91  Fuzzer::StaticAlarmCallback();
92}
93
94class 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
119static TimerQ Timer;
120
121static void CrashHandler(int) { Fuzzer::StaticCrashSignalCallback(); }
122
123void 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
148void SleepSeconds(int Seconds) { Sleep(Seconds * 1000); }
149
150unsigned long GetPid() { return GetCurrentProcessId(); }
151
152size_t GetPeakRSSMb() {
153  PROCESS_MEMORY_COUNTERS info;
154  if (!GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)))
155    return 0;
156  return info.PeakWorkingSetSize >> 20;
157}
158
159FILE *OpenProcessPipe(const char *Command, const char *Mode) {
160  return _popen(Command, Mode);
161}
162
163int CloseProcessPipe(FILE *F) {
164  return _pclose(F);
165}
166
167int ExecuteCommand(const Command &Cmd) {
168  std::string CmdLine = Cmd.toString();
169  return system(CmdLine.c_str());
170}
171
172bool 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
185const 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
206std::string DisassembleCmd(const std::string &FileName) {
207  std::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
215std::string SearchRegexCmd(const std::string &Regex) {
216  return "findstr /r \"" + Regex + "\"";
217}
218
219void DiscardOutput(int Fd) {
220  FILE* Temp = fopen("nul", "w");
221  if (!Temp)
222    return;
223  _dup2(_fileno(Temp), Fd);
224  fclose(Temp);
225}
226
227size_t PageSize() {
228  static size_t PageSizeCached = []() -> size_t {
229    SYSTEM_INFO si;
230    GetSystemInfo(&si);
231    return si.dwPageSize;
232  }();
233  return PageSizeCached;
234}
235
236void SetThreadName(std::thread &thread, const std::string &name) {
237  // TODO ?
238  // to UTF-8 then SetThreadDescription ?
239}
240
241} // namespace fuzzer
242
243#endif // LIBFUZZER_WINDOWS
244