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