1 //===- FuzzerIOWindows.cpp - IO utils for Windows. ------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 // IO functions implementation for Windows.
10 //===----------------------------------------------------------------------===//
11 #include "FuzzerDefs.h"
12 #if LIBFUZZER_WINDOWS
13 
14 #include "FuzzerExtFunctions.h"
15 #include "FuzzerIO.h"
16 #include <cstdarg>
17 #include <cstdio>
18 #include <fstream>
19 #include <io.h>
20 #include <iterator>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <windows.h>
24 
25 namespace fuzzer {
26 
IsFile(const std::string & Path,const DWORD & FileAttributes)27 static bool IsFile(const std::string &Path, const DWORD &FileAttributes) {
28 
29   if (FileAttributes & FILE_ATTRIBUTE_NORMAL)
30     return true;
31 
32   if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
33     return false;
34 
35   HANDLE FileHandle(
36       CreateFileA(Path.c_str(), 0, FILE_SHARE_READ, NULL, OPEN_EXISTING,
37                   FILE_FLAG_BACKUP_SEMANTICS, 0));
38 
39   if (FileHandle == INVALID_HANDLE_VALUE) {
40     Printf("CreateFileA() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
41         GetLastError());
42     return false;
43   }
44 
45   DWORD FileType = GetFileType(FileHandle);
46 
47   if (FileType == FILE_TYPE_UNKNOWN) {
48     Printf("GetFileType() failed for \"%s\" (Error code: %lu).\n", Path.c_str(),
49         GetLastError());
50     CloseHandle(FileHandle);
51     return false;
52   }
53 
54   if (FileType != FILE_TYPE_DISK) {
55     CloseHandle(FileHandle);
56     return false;
57   }
58 
59   CloseHandle(FileHandle);
60   return true;
61 }
62 
IsFile(const std::string & Path)63 bool IsFile(const std::string &Path) {
64   DWORD Att = GetFileAttributesA(Path.c_str());
65 
66   if (Att == INVALID_FILE_ATTRIBUTES) {
67     Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
68         Path.c_str(), GetLastError());
69     return false;
70   }
71 
72   return IsFile(Path, Att);
73 }
74 
ListFilesInDirRecursive(const std::string & Dir,long * Epoch,std::vector<std::string> * V,bool TopDir)75 void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
76                              std::vector<std::string> *V, bool TopDir) {
77   auto E = GetEpoch(Dir);
78   if (Epoch)
79     if (E && *Epoch >= E) return;
80 
81   std::string Path(Dir);
82   assert(!Path.empty());
83   if (Path.back() != '\\')
84       Path.push_back('\\');
85   Path.push_back('*');
86 
87   // Get the first directory entry.
88   WIN32_FIND_DATAA FindInfo;
89   HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
90   if (FindHandle == INVALID_HANDLE_VALUE)
91   {
92     if (GetLastError() == ERROR_FILE_NOT_FOUND)
93       return;
94     Printf("No such directory: %s; exiting\n", Dir.c_str());
95     exit(1);
96   }
97 
98   do {
99     std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
100 
101     if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
102       size_t FilenameLen = strlen(FindInfo.cFileName);
103       if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
104           (FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
105                                FindInfo.cFileName[1] == '.'))
106         continue;
107 
108       ListFilesInDirRecursive(FileName, Epoch, V, false);
109     }
110     else if (IsFile(FileName, FindInfo.dwFileAttributes))
111       V->push_back(FileName);
112   } while (FindNextFileA(FindHandle, &FindInfo));
113 
114   DWORD LastError = GetLastError();
115   if (LastError != ERROR_NO_MORE_FILES)
116     Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
117 
118   FindClose(FindHandle);
119 
120   if (Epoch && TopDir)
121     *Epoch = E;
122 }
123 
GetSeparator()124 char GetSeparator() {
125   return '\\';
126 }
127 
OpenFile(int Fd,const char * Mode)128 FILE* OpenFile(int Fd, const char* Mode) {
129   return _fdopen(Fd, Mode);
130 }
131 
CloseFile(int Fd)132 int CloseFile(int Fd) {
133   return _close(Fd);
134 }
135 
DuplicateFile(int Fd)136 int DuplicateFile(int Fd) {
137   return _dup(Fd);
138 }
139 
RemoveFile(const std::string & Path)140 void RemoveFile(const std::string &Path) {
141   _unlink(Path.c_str());
142 }
143 
DiscardOutput(int Fd)144 void DiscardOutput(int Fd) {
145   FILE* Temp = fopen("nul", "w");
146   if (!Temp)
147     return;
148   _dup2(_fileno(Temp), Fd);
149   fclose(Temp);
150 }
151 
GetHandleFromFd(int fd)152 intptr_t GetHandleFromFd(int fd) {
153   return _get_osfhandle(fd);
154 }
155 
IsSeparator(char C)156 static bool IsSeparator(char C) {
157   return C == '\\' || C == '/';
158 }
159 
160 // Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
161 // Returns number of characters considered if successful.
ParseDrive(const std::string & FileName,const size_t Offset,bool Relative=true)162 static size_t ParseDrive(const std::string &FileName, const size_t Offset,
163                          bool Relative = true) {
164   if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':')
165     return 0;
166   if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
167     if (!Relative) // Accept relative path?
168       return 0;
169     else
170       return 2;
171   }
172   return 3;
173 }
174 
175 // Parse a file name, like: SomeFile.txt
176 // Returns number of characters considered if successful.
ParseFileName(const std::string & FileName,const size_t Offset)177 static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
178   size_t Pos = Offset;
179   const size_t End = FileName.size();
180   for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
181     ;
182   return Pos - Offset;
183 }
184 
185 // Parse a directory ending in separator, like: SomeDir\
186 // Returns number of characters considered if successful.
ParseDir(const std::string & FileName,const size_t Offset)187 static size_t ParseDir(const std::string &FileName, const size_t Offset) {
188   size_t Pos = Offset;
189   const size_t End = FileName.size();
190   if (Pos >= End || IsSeparator(FileName[Pos]))
191     return 0;
192   for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
193     ;
194   if (Pos >= End)
195     return 0;
196   ++Pos; // Include separator.
197   return Pos - Offset;
198 }
199 
200 // Parse a servername and share, like: SomeServer\SomeShare\
201 // Returns number of characters considered if successful.
ParseServerAndShare(const std::string & FileName,const size_t Offset)202 static size_t ParseServerAndShare(const std::string &FileName,
203                                   const size_t Offset) {
204   size_t Pos = Offset, Res;
205   if (!(Res = ParseDir(FileName, Pos)))
206     return 0;
207   Pos += Res;
208   if (!(Res = ParseDir(FileName, Pos)))
209     return 0;
210   Pos += Res;
211   return Pos - Offset;
212 }
213 
214 // Parse the given Ref string from the position Offset, to exactly match the given
215 // string Patt.
216 // Returns number of characters considered if successful.
ParseCustomString(const std::string & Ref,size_t Offset,const char * Patt)217 static size_t ParseCustomString(const std::string &Ref, size_t Offset,
218                                 const char *Patt) {
219   size_t Len = strlen(Patt);
220   if (Offset + Len > Ref.size())
221     return 0;
222   return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
223 }
224 
225 // Parse a location, like:
226 // \\?\UNC\Server\Share\  \\?\C:\  \\Server\Share\  \  C:\  C:
227 // Returns number of characters considered if successful.
ParseLocation(const std::string & FileName)228 static size_t ParseLocation(const std::string &FileName) {
229   size_t Pos = 0, Res;
230 
231   if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
232     Pos += Res;
233     if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
234       Pos += Res;
235       if ((Res = ParseServerAndShare(FileName, Pos)))
236         return Pos + Res;
237       return 0;
238     }
239     if ((Res = ParseDrive(FileName, Pos, false)))
240       return Pos + Res;
241     return 0;
242   }
243 
244   if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
245     ++Pos;
246     if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
247       ++Pos;
248       if ((Res = ParseServerAndShare(FileName, Pos)))
249         return Pos + Res;
250       return 0;
251     }
252     return Pos;
253   }
254 
255   if ((Res = ParseDrive(FileName, Pos)))
256     return Pos + Res;
257 
258   return Pos;
259 }
260 
DirName(const std::string & FileName)261 std::string DirName(const std::string &FileName) {
262   size_t LocationLen = ParseLocation(FileName);
263   size_t DirLen = 0, Res;
264   while ((Res = ParseDir(FileName, LocationLen + DirLen)))
265     DirLen += Res;
266   size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
267 
268   if (LocationLen + DirLen + FileLen != FileName.size()) {
269     Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
270     exit(1);
271   }
272 
273   if (DirLen) {
274     --DirLen; // Remove trailing separator.
275     if (!FileLen) { // Path ended in separator.
276       assert(DirLen);
277       // Remove file name from Dir.
278       while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
279         --DirLen;
280       if (DirLen) // Remove trailing separator.
281         --DirLen;
282     }
283   }
284 
285   if (!LocationLen) { // Relative path.
286     if (!DirLen)
287       return ".";
288     return std::string(".\\").append(FileName, 0, DirLen);
289   }
290 
291   return FileName.substr(0, LocationLen + DirLen);
292 }
293 
TmpDir()294 std::string TmpDir() {
295   std::string Tmp;
296   Tmp.resize(MAX_PATH + 1);
297   DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]);
298   if (Size == 0) {
299     Printf("Couldn't get Tmp path.\n");
300     exit(1);
301   }
302   Tmp.resize(Size);
303   return Tmp;
304 }
305 
IsInterestingCoverageFile(const std::string & FileName)306 bool IsInterestingCoverageFile(const std::string &FileName) {
307   if (FileName.find("Program Files") != std::string::npos)
308     return false;
309   if (FileName.find("compiler-rt\\lib\\") != std::string::npos)
310     return false; // sanitizer internal.
311   if (FileName == "<null>")
312     return false;
313   return true;
314 }
315 
RawPrint(const char * Str)316 void RawPrint(const char *Str) {
317   // Not tested, may or may not work. Fix if needed.
318   Printf("%s", Str);
319 }
320 
321 }  // namespace fuzzer
322 
323 #endif // LIBFUZZER_WINDOWS
324