diff --git a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp index 4669b12786fc2f..2615014a02153d 100644 --- a/compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -250,11 +250,26 @@ static void WorkerThread(const Command &BaseCmd, std::atomic *Counter, } } -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()); +static void ValidateDirectoryExists(const std::string &Path, + bool CreateDirectory) { + if (Path.empty()) { + Printf("ERROR: Provided directory path is an empty string\n"); exit(1); } + + if (IsDirectory(Path)) + return; + + if (CreateDirectory) { + if (!MkDirRecursive(Path)) { + Printf("ERROR: Failed to create directory \"%s\"\n", Path.c_str()); + exit(1); + } + return; + } + + Printf("ERROR: The required directory \"%s\" does not exist\n", Path.c_str()); + exit(1); } std::string CloneArgsWithoutX(const Vector &Args, @@ -691,7 +706,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { std::string OutputCorpusDir = (*Inputs)[0]; if (!IsFile(OutputCorpusDir)) { Options.OutputCorpus = OutputCorpusDir; - ValidateDirectoryExists(Options.OutputCorpus); + ValidateDirectoryExists(Options.OutputCorpus, Flags.create_missing_dirs); } } Options.ReportSlowUnits = Flags.report_slow_units; @@ -705,11 +720,12 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { if (!IsSeparator(ArtifactPathDir[ArtifactPathDir.length() - 1])) { ArtifactPathDir = DirName(ArtifactPathDir); } - ValidateDirectoryExists(ArtifactPathDir); + ValidateDirectoryExists(ArtifactPathDir, Flags.create_missing_dirs); } if (Flags.exact_artifact_path) { Options.ExactArtifactPath = Flags.exact_artifact_path; - ValidateDirectoryExists(DirName(Options.ExactArtifactPath)); + ValidateDirectoryExists(DirName(Options.ExactArtifactPath), + Flags.create_missing_dirs); } Vector Dictionary; if (Flags.dict) @@ -735,7 +751,7 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { Options.DataFlowTrace = Flags.data_flow_trace; if (Flags.features_dir) { Options.FeaturesDir = Flags.features_dir; - ValidateDirectoryExists(Options.FeaturesDir); + ValidateDirectoryExists(Options.FeaturesDir, Flags.create_missing_dirs); } if (Flags.collect_data_flow) Options.CollectDataFlow = Flags.collect_data_flow; diff --git a/compiler-rt/lib/fuzzer/FuzzerFlags.def b/compiler-rt/lib/fuzzer/FuzzerFlags.def index 832224a705d2b6..81147914660320 100644 --- a/compiler-rt/lib/fuzzer/FuzzerFlags.def +++ b/compiler-rt/lib/fuzzer/FuzzerFlags.def @@ -167,3 +167,7 @@ FUZZER_DEPRECATED_FLAG(use_clang_coverage) FUZZER_FLAG_STRING(data_flow_trace, "Experimental: use the data flow trace") FUZZER_FLAG_STRING(collect_data_flow, "Experimental: collect the data flow trace") + +FUZZER_FLAG_INT(create_missing_dirs, 0, "Automatically attempt to create " + "directories for arguments that would normally expect them to already " + "exist (i.e. artifact_prefix, exact_artifact_path, features_dir, corpus)") diff --git a/compiler-rt/lib/fuzzer/FuzzerIO.cpp b/compiler-rt/lib/fuzzer/FuzzerIO.cpp index cbb1dbe1b86d2c..c3330c3425d091 100644 --- a/compiler-rt/lib/fuzzer/FuzzerIO.cpp +++ b/compiler-rt/lib/fuzzer/FuzzerIO.cpp @@ -144,6 +144,38 @@ void VPrintf(bool Verbose, const char *Fmt, ...) { fflush(OutputFile); } +static bool MkDirRecursiveInner(const std::string &Leaf) { + // Prevent chance of potential infinite recursion + if (Leaf == ".") + return true; + + const std::string &Dir = DirName(Leaf); + + if (IsDirectory(Dir)) { + MkDir(Leaf); + return IsDirectory(Leaf); + } + + bool ret = MkDirRecursiveInner(Dir); + if (!ret) { + // Give up early if a previous MkDir failed + return ret; + } + + MkDir(Leaf); + return IsDirectory(Leaf); +} + +bool MkDirRecursive(const std::string &Dir) { + if (Dir.empty()) + return false; + + if (IsDirectory(Dir)) + return true; + + return MkDirRecursiveInner(Dir); +} + void RmDirRecursive(const std::string &Dir) { IterateDirRecursive( Dir, [](const std::string &Path) {}, diff --git a/compiler-rt/lib/fuzzer/FuzzerIO.h b/compiler-rt/lib/fuzzer/FuzzerIO.h index 8def2e96304e75..6e3a0b470c5f6a 100644 --- a/compiler-rt/lib/fuzzer/FuzzerIO.h +++ b/compiler-rt/lib/fuzzer/FuzzerIO.h @@ -64,6 +64,7 @@ size_t FileSize(const std::string &Path); void ListFilesInDirRecursive(const std::string &Dir, long *Epoch, Vector *V, bool TopDir); +bool MkDirRecursive(const std::string &Dir); void RmDirRecursive(const std::string &Dir); // Iterate files and dirs inside Dir, recursively. diff --git a/compiler-rt/test/fuzzer/fuzzer-dirs.test b/compiler-rt/test/fuzzer/fuzzer-dirs.test index 2bf2a8b143300c..3dce0cd5657452 100644 --- a/compiler-rt/test/fuzzer/fuzzer-dirs.test +++ b/compiler-rt/test/fuzzer/fuzzer-dirs.test @@ -16,6 +16,7 @@ RUN: %run %t-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=LONG LONG: INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 8192 bytes RUN: rm -rf %t/SUB1 +# Verify error message prints to console when directory does not exist 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 @@ -23,3 +24,39 @@ RUN: not %run %t-SimpleTest -features_dir=%t.dir/NONEXISTENT_DIR/ 2>&1 | FileChe 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 + +# Verify error message prints to console when given directory is an empty +# string +RUN: not %run %t-SimpleTest "" 2>&1 | FileCheck %s --check-prefix=INVALID_DIR_RGX +INVALID_DIR_RGX: ERROR: Provided directory path is an empty string + +# Verify error message prints to console when directory creation fails +# 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 || true) +RUN: not %run %t-SimpleTest -create_missing_dirs=1 %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\?}}" + +# Verify directories and sub-directories are created when -create_missing_dirs=1 +RUN: not %run %t-SimpleTest -create_missing_dirs=1 -artifact_prefix=%t.dir/subdira/./././artifacts/ -features_dir=%t.dir/subdirb/../dummy_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: test -e %t.dir/dummy_dir + +# Verify directories and sub-directories are created for exact_artifact_path +# when -create_missing_dirs=1 +RUN: not %run %t-SimpleTest -create_missing_dirs=1 -exact_artifact_path=%t.dir/subdird/exact_artifacts/abc +RUN: test -e %t.dir/subdird/exact_artifacts/abc + +# Verify directories and sub-directories are created for artifact_prefix when +# it's referring to a file name prefix and -create_missing_dirs=1 +RUN: not %run %t-SimpleTest -create_missing_dirs=1 -artifact_prefix=%t.dir/subdire/myprefix +RUN: test -e %t.dir/subdire/ && not test -e %t.dir/subdire/myprefix + +# Verify directories are created when referring to relative paths and +# -create_missing_dirs=1 +RUN: cd %t.dir && not %run %t-SimpleTest -create_missing_dirs=1 -artifact_prefix=cwd_artifacts/ -features_dir=cwd_features/subdirtest/ ./cwd_corpus +RUN: test -e %t.dir/cwd_artifacts/ +RUN: test -e %t.dir/cwd_features/subdirtest/ +RUN: test -e %t.dir/cwd_corpus/