Skip to content

Commit 2392ff0

Browse files
committed
[libFuzzer] Error and exit if user supplied fuzzer writeable directories don't exist
Currently, libFuzzer will exit with an error message if a non-existent corpus directory is provided. However, if a user provides a non-existent directory for the `artifact_prefix`, `exact_artifact_path`, or `features_dir`, libFuzzer will continue execution but silently fail to write artifacts/features. To improve the user experience, this PR adds validation for the existence of all user supplied directories before executing the main fuzzing loop. If they don't exist, libFuzzer will exit with an error message. Patch By: dgg5503 Reviewed By: morehouse Differential Revision: https://reviews.llvm.org/D84808
1 parent 5b9c2b1 commit 2392ff0

File tree

5 files changed

+60
-10
lines changed

5 files changed

+60
-10
lines changed

compiler-rt/lib/fuzzer/FuzzerDriver.cpp

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@ static void WorkerThread(const Command &BaseCmd, std::atomic<unsigned> *Counter,
250250
}
251251
}
252252

253+
static void ValidateDirectoryExists(const std::string &Path) {
254+
if (!Path.empty() && !IsDirectory(Path)) {
255+
Printf("ERROR: The required directory \"%s\" does not exist\n", Path.c_str());
256+
exit(1);
257+
}
258+
}
259+
253260
std::string CloneArgsWithoutX(const Vector<std::string> &Args,
254261
const char *X1, const char *X2) {
255262
std::string Cmd;
@@ -678,13 +685,32 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
678685
Options.MallocLimitMb = Options.RssLimitMb;
679686
if (Flags.runs >= 0)
680687
Options.MaxNumberOfRuns = Flags.runs;
681-
if (!Inputs->empty() && !Flags.minimize_crash_internal_step)
682-
Options.OutputCorpus = (*Inputs)[0];
688+
if (!Inputs->empty() && !Flags.minimize_crash_internal_step) {
689+
// Ensure output corpus assumed to be the first arbitrary argument input
690+
// is not a path to an existing file.
691+
std::string OutputCorpusDir = (*Inputs)[0];
692+
if (!IsFile(OutputCorpusDir)) {
693+
Options.OutputCorpus = OutputCorpusDir;
694+
ValidateDirectoryExists(Options.OutputCorpus);
695+
}
696+
}
683697
Options.ReportSlowUnits = Flags.report_slow_units;
684-
if (Flags.artifact_prefix)
698+
if (Flags.artifact_prefix) {
685699
Options.ArtifactPrefix = Flags.artifact_prefix;
686-
if (Flags.exact_artifact_path)
700+
701+
// Since the prefix could be a full path to a file name prefix, assume
702+
// that if the path ends with the platform's separator that a directory
703+
// is desired
704+
std::string ArtifactPathDir = Options.ArtifactPrefix;
705+
if (!IsSeparator(ArtifactPathDir[ArtifactPathDir.length() - 1])) {
706+
ArtifactPathDir = DirName(ArtifactPathDir);
707+
}
708+
ValidateDirectoryExists(ArtifactPathDir);
709+
}
710+
if (Flags.exact_artifact_path) {
687711
Options.ExactArtifactPath = Flags.exact_artifact_path;
712+
ValidateDirectoryExists(DirName(Options.ExactArtifactPath));
713+
}
688714
Vector<Unit> Dictionary;
689715
if (Flags.dict)
690716
if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary))
@@ -707,8 +733,10 @@ int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) {
707733
Options.FocusFunction = Flags.focus_function;
708734
if (Flags.data_flow_trace)
709735
Options.DataFlowTrace = Flags.data_flow_trace;
710-
if (Flags.features_dir)
736+
if (Flags.features_dir) {
711737
Options.FeaturesDir = Flags.features_dir;
738+
ValidateDirectoryExists(Options.FeaturesDir);
739+
}
712740
if (Flags.collect_data_flow)
713741
Options.CollectDataFlow = Flags.collect_data_flow;
714742
if (Flags.stop_file)

compiler-rt/lib/fuzzer/FuzzerIO.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ void RawPrint(const char *Str);
5858

5959
// Platform specific functions:
6060
bool IsFile(const std::string &Path);
61+
bool IsDirectory(const std::string &Path);
6162
size_t FileSize(const std::string &Path);
6263

6364
void ListFilesInDirRecursive(const std::string &Dir, long *Epoch,
@@ -82,6 +83,7 @@ struct SizedFile {
8283
void GetSizedFilesFromDir(const std::string &Dir, Vector<SizedFile> *V);
8384

8485
char GetSeparator();
86+
bool IsSeparator(char C);
8587
// Similar to the basename utility: returns the file name w/o the dir prefix.
8688
std::string Basename(const std::string &Path);
8789

compiler-rt/lib/fuzzer/FuzzerIOPosix.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ bool IsFile(const std::string &Path) {
3131
return S_ISREG(St.st_mode);
3232
}
3333

34-
static bool IsDirectory(const std::string &Path) {
34+
bool IsDirectory(const std::string &Path) {
3535
struct stat St;
3636
if (stat(Path.c_str(), &St))
3737
return false;
@@ -104,6 +104,10 @@ char GetSeparator() {
104104
return '/';
105105
}
106106

107+
bool IsSeparator(char C) {
108+
return C == '/';
109+
}
110+
107111
FILE* OpenFile(int Fd, const char* Mode) {
108112
return fdopen(Fd, Mode);
109113
}

compiler-rt/lib/fuzzer/FuzzerIOWindows.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@ static bool IsDir(DWORD FileAttrs) {
7676
return FileAttrs & FILE_ATTRIBUTE_DIRECTORY;
7777
}
7878

79+
bool IsDirectory(const std::string &Path) {
80+
DWORD Att = GetFileAttributesA(Path.c_str());
81+
82+
if (Att == INVALID_FILE_ATTRIBUTES) {
83+
Printf("GetFileAttributesA() failed for \"%s\" (Error code: %lu).\n",
84+
Path.c_str(), GetLastError());
85+
return false;
86+
}
87+
88+
return IsDir(Att);
89+
}
90+
7991
std::string Basename(const std::string &Path) {
8092
size_t Pos = Path.find_last_of("/\\");
8193
if (Pos == std::string::npos) return Path;
@@ -227,7 +239,7 @@ intptr_t GetHandleFromFd(int fd) {
227239
return _get_osfhandle(fd);
228240
}
229241

230-
static bool IsSeparator(char C) {
242+
bool IsSeparator(char C) {
231243
return C == '\\' || C == '/';
232244
}
233245

compiler-rt/test/fuzzer/fuzzer-dirs.test

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ RUN: %run %t-SimpleTest %t/SUB1 -runs=0 2>&1 | FileCheck %s --check-prefix=LONG
1616
LONG: INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 8192 bytes
1717
RUN: rm -rf %t/SUB1
1818

19-
RUN: not %run %t-SimpleTest NONEXISTENT_DIR 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR
20-
NONEXISTENT_DIR: No such file or directory: NONEXISTENT_DIR; exiting
21-
19+
RUN: rm -rf %t.dir && mkdir -p %t.dir
20+
RUN: not %run %t-SimpleTest -artifact_prefix=%t.dir/NONEXISTENT_DIR/ 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX
21+
RUN: not %run %t-SimpleTest -artifact_prefix=%t.dir/NONEXISTENT_DIR/myprefix 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX
22+
RUN: not %run %t-SimpleTest -features_dir=%t.dir/NONEXISTENT_DIR/ 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX
23+
RUN: not %run %t-SimpleTest %t.dir/NONEXISTENT_DIR 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX
24+
RUN: not %run %t-SimpleTest -exact_artifact_path=%t.dir/NONEXISTENT_DIR/myprefix 2>&1 | FileCheck %s --check-prefix=NONEXISTENT_DIR_RGX
25+
NONEXISTENT_DIR_RGX: ERROR: The required directory "{{.*/NONEXISTENT_DIR/?}}" does not exist

0 commit comments

Comments
 (0)