diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp --- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -250,6 +250,13 @@ } } +static void ValidateDirectoryExists(const std::string &Path) { + if (!Path.empty() && !IsDirectory(Path)) { + Printf("ERROR: The required directory \"%s\" does not exist\n", Path.c_str()); + exit(1); + } +} + std::string CloneArgsWithoutX(const Vector &Args, const char *X1, const char *X2) { std::string Cmd; @@ -678,13 +685,32 @@ Options.MallocLimitMb = Options.RssLimitMb; if (Flags.runs >= 0) Options.MaxNumberOfRuns = Flags.runs; - if (!Inputs->empty() && !Flags.minimize_crash_internal_step) - Options.OutputCorpus = (*Inputs)[0]; + if (!Inputs->empty() && !Flags.minimize_crash_internal_step) { + // Ensure output corpus assumed to be the first arbitrary argument input + // is not a path to an existing file. + std::string OutputCorpusDir = (*Inputs)[0]; + if (!IsFile(OutputCorpusDir)) { + Options.OutputCorpus = OutputCorpusDir; + ValidateDirectoryExists(Options.OutputCorpus); + } + } Options.ReportSlowUnits = Flags.report_slow_units; - if (Flags.artifact_prefix) + if (Flags.artifact_prefix) { Options.ArtifactPrefix = Flags.artifact_prefix; - if (Flags.exact_artifact_path) + + // Since the prefix could be a full path to a file name prefix, assume + // that if the path ends with the platform's separator that a directory + // is desired + std::string ArtifactPathDir = Options.ArtifactPrefix; + if (!IsSeparator(ArtifactPathDir[ArtifactPathDir.length() - 1])) { + ArtifactPathDir = DirName(ArtifactPathDir); + } + ValidateDirectoryExists(ArtifactPathDir); + } + if (Flags.exact_artifact_path) { Options.ExactArtifactPath = Flags.exact_artifact_path; + ValidateDirectoryExists(DirName(Options.ExactArtifactPath)); + } Vector Dictionary; if (Flags.dict) if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) @@ -707,8 +733,10 @@ Options.FocusFunction = Flags.focus_function; if (Flags.data_flow_trace) Options.DataFlowTrace = Flags.data_flow_trace; - if (Flags.features_dir) + if (Flags.features_dir) { Options.FeaturesDir = Flags.features_dir; + ValidateDirectoryExists(Options.FeaturesDir); + } if (Flags.collect_data_flow) Options.CollectDataFlow = Flags.collect_data_flow; if (Flags.stop_file) diff --git a/compiler-rt/lib/fuzzer/FuzzerIO.h b/compiler-rt/lib/fuzzer/FuzzerIO.h --- a/compiler-rt/lib/fuzzer/FuzzerIO.h +++ b/compiler-rt/lib/fuzzer/FuzzerIO.h @@ -58,6 +58,7 @@ // Platform specific functions: bool IsFile(const std::string &Path); +bool IsDirectory(const std::string &Path); size_t FileSize(const std::string &Path); void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, @@ -82,6 +83,7 @@ void GetSizedFilesFromDir(const std::string &Dir, Vector *V); char GetSeparator(); +bool IsSeparator(char C); // Similar to the basename utility: returns the file name w/o the dir prefix. std::string Basename(const std::string &Path); diff --git a/compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp b/compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp --- a/compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp @@ -31,7 +31,7 @@ return S_ISREG(St.st_mode); } -static bool IsDirectory(const std::string &Path) { +bool IsDirectory(const std::string &Path) { struct stat St; if (stat(Path.c_str(), &St)) return false; @@ -104,6 +104,10 @@ return '/'; } +bool IsSeparator(char C) { + return C == '/'; +} + FILE* OpenFile(int Fd, const char* Mode) { return fdopen(Fd, Mode); } diff --git a/compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp b/compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp --- a/compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp @@ -76,6 +76,18 @@ return FileAttrs & FILE_ATTRIBUTE_DIRECTORY; } +bool IsDirectory(const std::string &Path) { + DWORD Att = GetFileAttributesA(Path.c_str()); + + if (Att == INVALID_FILE_ATTRIBUTES) { + Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n", + Path.c_str(), GetLastError()); + return false; + } + + return IsDir(Att); +} + std::string Basename(const std::string &Path) { size_t Pos = Path.find_last_of("/\\"); if (Pos == std::string::npos) return Path; @@ -227,7 +239,7 @@ return _get_osfhandle(fd); } -static bool IsSeparator(char C) { +bool IsSeparator(char C) { return C == '\\' || C == '/'; } diff --git a/compiler-rt/test/fuzzer/fuzzer-dirs.test b/compiler-rt/test/fuzzer/fuzzer-dirs.test --- a/compiler-rt/test/fuzzer/fuzzer-dirs.test +++ b/compiler-rt/test/fuzzer/fuzzer-dirs.test @@ -16,6 +16,10 @@ LONG: INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 8192 bytes RUN: rm -rf %t/SUB1 -RUN: not %run %t-SimpleTest NONEXISTENT_DIR 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR -NONEXISTENT_DIR: No such file or directory: NONEXISTENT_DIR; exiting - +RUN: rm -rf %t.dir && mkdir -p %t.dir +RUN: not %run %t-SimpleTest -artifact_prefix=%t.dir/NONEXISTENT_DIR/ 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX +RUN: not %run %t-SimpleTest -artifact_prefix=%t.dir/NONEXISTENT_DIR/myprefix 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX +RUN: not %run %t-SimpleTest -features_dir=%t.dir/NONEXISTENT_DIR/ 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX +RUN: not %run %t-SimpleTest %t.dir/NONEXISTENT_DIR 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX +RUN: not %run %t-SimpleTest -exact_artifact_path=%t.dir/NONEXISTENT_DIR/myprefix 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX +NONEXISTENT_DIR_RGX: ERROR: The required directory "{{.*/NONEXISTENT_DIR/?}}" does not exist