Home | History | Annotate | Line # | Download | only in fuzzer
      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 
     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 
     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 
     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 
     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 
     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 
    144 char GetSeparator() {
    145   return '\\';
    146 }
    147 
    148 FILE* OpenFile(int Fd, const char* Mode) {
    149   return _fdopen(Fd, Mode);
    150 }
    151 
    152 int CloseFile(int Fd) {
    153   return _close(Fd);
    154 }
    155 
    156 int DuplicateFile(int Fd) {
    157   return _dup(Fd);
    158 }
    159 
    160 void RemoveFile(const std::string &Path) {
    161   _unlink(Path.c_str());
    162 }
    163 
    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 
    172 intptr_t GetHandleFromFd(int fd) {
    173   return _get_osfhandle(fd);
    174 }
    175 
    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.
    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.
    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.
    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.
    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.
    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.
    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 
    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 
    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 
    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 
    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