Index: compiler-rt/lib/fuzzer/FuzzerDriver.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -250,6 +250,15 @@ } } +static void InitializeArgumentDirectory(const std::string &Path) { + if (!Path.empty() && + !IsDirectory(Path) && + MkDirRecursive(Path)) { + Printf("ERROR: Failed to create directory \"%s\"\n", Path.c_str()); + exit(1); + } +} + std::string CloneArgsWithoutX(const Vector &Args, const char *X1, const char *X2) { std::string Cmd; @@ -678,13 +687,33 @@ 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; + InitializeArgumentDirectory(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); + } + + InitializeArgumentDirectory(ArtifactPathDir); + } + if (Flags.exact_artifact_path) { Options.ExactArtifactPath = Flags.exact_artifact_path; + InitializeArgumentDirectory(DirName(Options.ExactArtifactPath)); + } Vector Dictionary; if (Flags.dict) if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) @@ -707,8 +736,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; + InitializeArgumentDirectory(Options.FeaturesDir); + } if (Flags.collect_data_flow) Options.CollectDataFlow = Flags.collect_data_flow; if (Flags.stop_file) Index: compiler-rt/lib/fuzzer/FuzzerIO.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIO.h +++ compiler-rt/lib/fuzzer/FuzzerIO.h @@ -58,11 +58,13 @@ // 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, Vector *V, bool TopDir); +int MkDirRecursive(const std::string &Dir); void RmDirRecursive(const std::string &Dir); // Iterate files and dirs inside Dir, recursively. @@ -82,6 +84,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); @@ -96,7 +99,7 @@ intptr_t GetHandleFromFd(int fd); -void MkDir(const std::string &Path); +int MkDir(const std::string &Path); void RmDir(const std::string &Path); const std::string &getDevNull(); Index: compiler-rt/lib/fuzzer/FuzzerIO.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIO.cpp +++ compiler-rt/lib/fuzzer/FuzzerIO.cpp @@ -144,6 +144,26 @@ fflush(OutputFile); } +static int MkDirRecursiveInner(const std::string &Dir, const std::string &Leaf) { + if (IsDirectory(Dir)) { + return MkDir(Leaf); + } + + int ret = MkDirRecursiveInner(DirName(Dir), Dir); + if (ret) { + // Give up early if a previous MkDir failed + return ret; + } + + return MkDir(Leaf); +} + +int MkDirRecursive(const std::string &Dir) { + if (Dir.empty()) + return -1; + return MkDirRecursiveInner(DirName(Dir), Dir); +} + void RmDirRecursive(const std::string &Dir) { IterateDirRecursive( Dir, [](const std::string &Path) {}, Index: compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp +++ 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); } @@ -158,8 +162,8 @@ write(2, Str, strlen(Str)); } -void MkDir(const std::string &Path) { - mkdir(Path.c_str(), 0700); +int MkDir(const std::string &Path) { + return mkdir(Path.c_str(), 0700); } void RmDir(const std::string &Path) { Index: compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp +++ 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 == '/'; } @@ -391,10 +403,11 @@ _write(2, Str, strlen(Str)); } -void MkDir(const std::string &Path) { - if (CreateDirectoryA(Path.c_str(), nullptr)) return; +int MkDir(const std::string &Path) { + if (CreateDirectoryA(Path.c_str(), nullptr)) return 0; Printf("CreateDirectoryA failed for %s (Error code: %lu).\n", Path.c_str(), GetLastError()); + return -1; } void RmDir(const std::string &Path) { Index: compiler-rt/test/fuzzer/fuzzer-dirs.test =================================================================== --- compiler-rt/test/fuzzer/fuzzer-dirs.test +++ compiler-rt/test/fuzzer/fuzzer-dirs.test @@ -16,6 +16,20 @@ 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 +# For platforms without functioning chmod (i.e. Windows), use a forbidden +# character in the directory name. +RUN: rm -rf %t.dir && mkdir -p %t.dir/access_restricted && chmod u-w %t.dir/access_restricted +RUN: not %run %t-SimpleTest %t.dir/access_restricted/?corpus? 2>&1 | FileCheck %s --check-prefix=DIR_CREATION_FAILURE +DIR_CREATION_FAILURE: ERROR: Failed to create directory {{.*/access_restricted/\?corpus\?}} +RUN: rm -rf %t.dir && mkdir -p %t.dir +RUN: not %run %t-SimpleTest -artifact_prefix=%t.dir/subdira/artifacts/ -features_dir=%t.dir/subdirb/features/ %t.dir/subdirc/corpus +RUN: test -e %t.dir/subdira/artifacts/ +RUN: test -e %t.dir/subdirb/features/ +RUN: test -e %t.dir/subdirc/corpus/ + +RUN: not %run %t-SimpleTest -exact_artifact_path=%t.dir/subdird/exact_artifacts/abc +RUN: test -e %t.dir/subdird/exact_artifacts/abc + +RUN: not %run %t-SimpleTest -artifact_prefix=%t.dir/subdire/myprefix +RUN: test -e %t.dir/subdire/ && not test -e %t.dir/subdire/myprefix