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 
Basename(const std::string & Path)75 std::string Basename(const std::string &Path) {
76   size_t Pos = Path.find_last_of("/\\");
77   if (Pos == std::string::npos) return Path;
78   assert(Pos < Path.size());
79   return Path.substr(Pos + 1);
80 }
81 
FileSize(const std::string & Path)82 size_t FileSize(const std::string &Path) {
83   WIN32_FILE_ATTRIBUTE_DATA attr;
84   if (!GetFileAttributesExA(Path.c_str(), GetFileExInfoStandard, &attr)) {
85     Printf("GetFileAttributesExA() failed for \"%s\" (Error code: %lu).\n",
86            Path.c_str(), GetLastError());
87     return 0;
88   }
89   ULARGE_INTEGER size;
90   size.HighPart = attr.nFileSizeHigh;
91   size.LowPart = attr.nFileSizeLow;
92   return size.QuadPart;
93 }
94 
ListFilesInDirRecursive(const std::string & Dir,long * Epoch,Vector<std::string> * V,bool TopDir)95 void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
96                              Vector<std::string> *V, bool TopDir) {
97   auto E = GetEpoch(Dir);
98   if (Epoch)
99     if (E && *Epoch >= E) return;
100 
101   std::string Path(Dir);
102   assert(!Path.empty());
103   if (Path.back() != '\\')
104       Path.push_back('\\');
105   Path.push_back('*');
106 
107   // Get the first directory entry.
108   WIN32_FIND_DATAA FindInfo;
109   HANDLE FindHandle(FindFirstFileA(Path.c_str(), &FindInfo));
110   if (FindHandle == INVALID_HANDLE_VALUE)
111   {
112     if (GetLastError() == ERROR_FILE_NOT_FOUND)
113       return;
114     Printf("No such file or directory: %s; exiting\n", Dir.c_str());
115     exit(1);
116   }
117 
118   do {
119     std::string FileName = DirPlusFile(Dir, FindInfo.cFileName);
120 
121     if (FindInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
122       size_t FilenameLen = strlen(FindInfo.cFileName);
123       if ((FilenameLen == 1 && FindInfo.cFileName[0] == '.') ||
124           (FilenameLen == 2 && FindInfo.cFileName[0] == '.' &&
125                                FindInfo.cFileName[1] == '.'))
126         continue;
127 
128       ListFilesInDirRecursive(FileName, Epoch, V, false);
129     }
130     else if (IsFile(FileName, FindInfo.dwFileAttributes))
131       V->push_back(FileName);
132   } while (FindNextFileA(FindHandle, &FindInfo));
133 
134   DWORD LastError = GetLastError();
135   if (LastError != ERROR_NO_MORE_FILES)
136     Printf("FindNextFileA failed (Error code: %lu).\n", LastError);
137 
138   FindClose(FindHandle);
139 
140   if (Epoch && TopDir)
141     *Epoch = E;
142 }
143 
GetSeparator()144 char GetSeparator() {
145   return '\\';
146 }
147 
OpenFile(int Fd,const char * Mode)148 FILE* OpenFile(int Fd, const char* Mode) {
149   return _fdopen(Fd, Mode);
150 }
151 
CloseFile(int Fd)152 int CloseFile(int Fd) {
153   return _close(Fd);
154 }
155 
DuplicateFile(int Fd)156 int DuplicateFile(int Fd) {
157   return _dup(Fd);
158 }
159 
RemoveFile(const std::string & Path)160 void RemoveFile(const std::string &Path) {
161   _unlink(Path.c_str());
162 }
163 
DiscardOutput(int Fd)164 void DiscardOutput(int Fd) {
165   FILE* Temp = fopen("nul", "w");
166   if (!Temp)
167     return;
168   _dup2(_fileno(Temp), Fd);
169   fclose(Temp);
170 }
171 
GetHandleFromFd(int fd)172 intptr_t GetHandleFromFd(int fd) {
173   return _get_osfhandle(fd);
174 }
175 
IsSeparator(char C)176 static bool IsSeparator(char C) {
177   return C == '\\' || C == '/';
178 }
179 
180 // Parse disk designators, like "C:\". If Relative == true, also accepts: "C:".
181 // Returns number of characters considered if successful.
ParseDrive(const std::string & FileName,const size_t Offset,bool Relative=true)182 static size_t ParseDrive(const std::string &FileName, const size_t Offset,
183                          bool Relative = true) {
184   if (Offset + 1 >= FileName.size() || FileName[Offset + 1] != ':')
185     return 0;
186   if (Offset + 2 >= FileName.size() || !IsSeparator(FileName[Offset + 2])) {
187     if (!Relative) // Accept relative path?
188       return 0;
189     else
190       return 2;
191   }
192   return 3;
193 }
194 
195 // Parse a file name, like: SomeFile.txt
196 // Returns number of characters considered if successful.
ParseFileName(const std::string & FileName,const size_t Offset)197 static size_t ParseFileName(const std::string &FileName, const size_t Offset) {
198   size_t Pos = Offset;
199   const size_t End = FileName.size();
200   for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
201     ;
202   return Pos - Offset;
203 }
204 
205 // Parse a directory ending in separator, like: `SomeDir\`
206 // Returns number of characters considered if successful.
ParseDir(const std::string & FileName,const size_t Offset)207 static size_t ParseDir(const std::string &FileName, const size_t Offset) {
208   size_t Pos = Offset;
209   const size_t End = FileName.size();
210   if (Pos >= End || IsSeparator(FileName[Pos]))
211     return 0;
212   for(; Pos < End && !IsSeparator(FileName[Pos]); ++Pos)
213     ;
214   if (Pos >= End)
215     return 0;
216   ++Pos; // Include separator.
217   return Pos - Offset;
218 }
219 
220 // Parse a servername and share, like: `SomeServer\SomeShare\`
221 // Returns number of characters considered if successful.
ParseServerAndShare(const std::string & FileName,const size_t Offset)222 static size_t ParseServerAndShare(const std::string &FileName,
223                                   const size_t Offset) {
224   size_t Pos = Offset, Res;
225   if (!(Res = ParseDir(FileName, Pos)))
226     return 0;
227   Pos += Res;
228   if (!(Res = ParseDir(FileName, Pos)))
229     return 0;
230   Pos += Res;
231   return Pos - Offset;
232 }
233 
234 // Parse the given Ref string from the position Offset, to exactly match the given
235 // string Patt.
236 // Returns number of characters considered if successful.
ParseCustomString(const std::string & Ref,size_t Offset,const char * Patt)237 static size_t ParseCustomString(const std::string &Ref, size_t Offset,
238                                 const char *Patt) {
239   size_t Len = strlen(Patt);
240   if (Offset + Len > Ref.size())
241     return 0;
242   return Ref.compare(Offset, Len, Patt) == 0 ? Len : 0;
243 }
244 
245 // Parse a location, like:
246 // \\?\UNC\Server\Share\  \\?\C:\  \\Server\Share\  \  C:\  C:
247 // Returns number of characters considered if successful.
ParseLocation(const std::string & FileName)248 static size_t ParseLocation(const std::string &FileName) {
249   size_t Pos = 0, Res;
250 
251   if ((Res = ParseCustomString(FileName, Pos, R"(\\?\)"))) {
252     Pos += Res;
253     if ((Res = ParseCustomString(FileName, Pos, R"(UNC\)"))) {
254       Pos += Res;
255       if ((Res = ParseServerAndShare(FileName, Pos)))
256         return Pos + Res;
257       return 0;
258     }
259     if ((Res = ParseDrive(FileName, Pos, false)))
260       return Pos + Res;
261     return 0;
262   }
263 
264   if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
265     ++Pos;
266     if (Pos < FileName.size() && IsSeparator(FileName[Pos])) {
267       ++Pos;
268       if ((Res = ParseServerAndShare(FileName, Pos)))
269         return Pos + Res;
270       return 0;
271     }
272     return Pos;
273   }
274 
275   if ((Res = ParseDrive(FileName, Pos)))
276     return Pos + Res;
277 
278   return Pos;
279 }
280 
DirName(const std::string & FileName)281 std::string DirName(const std::string &FileName) {
282   size_t LocationLen = ParseLocation(FileName);
283   size_t DirLen = 0, Res;
284   while ((Res = ParseDir(FileName, LocationLen + DirLen)))
285     DirLen += Res;
286   size_t FileLen = ParseFileName(FileName, LocationLen + DirLen);
287 
288   if (LocationLen + DirLen + FileLen != FileName.size()) {
289     Printf("DirName() failed for \"%s\", invalid path.\n", FileName.c_str());
290     exit(1);
291   }
292 
293   if (DirLen) {
294     --DirLen; // Remove trailing separator.
295     if (!FileLen) { // Path ended in separator.
296       assert(DirLen);
297       // Remove file name from Dir.
298       while (DirLen && !IsSeparator(FileName[LocationLen + DirLen - 1]))
299         --DirLen;
300       if (DirLen) // Remove trailing separator.
301         --DirLen;
302     }
303   }
304 
305   if (!LocationLen) { // Relative path.
306     if (!DirLen)
307       return ".";
308     return std::string(".\\").append(FileName, 0, DirLen);
309   }
310 
311   return FileName.substr(0, LocationLen + DirLen);
312 }
313 
TmpDir()314 std::string TmpDir() {
315   std::string Tmp;
316   Tmp.resize(MAX_PATH + 1);
317   DWORD Size = GetTempPathA(Tmp.size(), &Tmp[0]);
318   if (Size == 0) {
319     Printf("Couldn't get Tmp path.\n");
320     exit(1);
321   }
322   Tmp.resize(Size);
323   return Tmp;
324 }
325 
IsInterestingCoverageFile(const std::string & FileName)326 bool IsInterestingCoverageFile(const std::string &FileName) {
327   if (FileName.find("Program Files") != std::string::npos)
328     return false;
329   if (FileName.find("compiler-rt\\lib\\") != std::string::npos)
330     return false; // sanitizer internal.
331   if (FileName == "<null>")
332     return false;
333   return true;
334 }
335 
RawPrint(const char * Str)336 void RawPrint(const char *Str) {
337   // Not tested, may or may not work. Fix if needed.
338   Printf("%s", Str);
339 }
340 
341 }  // namespace fuzzer
342 
343 #endif // LIBFUZZER_WINDOWS
344