Description
libFuzzer has a runtime argument -stop-file
with this description:
stop_file: "Stop fuzzing ASAP if this file exists"
However fuzzing only stops if the file exists and has some content. If the file is empty, the fuzzing continues.
Steps to reproduce
-
Create a simple fuzz test, e.g.
#include <fuzzer/FuzzedDataProvider.h> #include <assert.h> extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { FuzzedDataProvider provider(data, size); int a = provider.ConsumeIntegral<int>(); int b = provider.ConsumeIntegral<int>(); assert(a+b == b+a); return 0; }
Save it as
test.cpp
-
Compile the test
clang++ -g -fsanitize=address,fuzzer test.cpp -o test
-
Run the resulting binary with the
-stop_file
argument:./test -stop_file=/tmp/stop-file
-
In a separate terminal create the stop-file as an empty file:
touch /tmp/stop-file
Observe that the fuzzing run continues.
-
Create a file with some content:
rm /tmp/stop-file # optional echo abc > /tmp/stop-file
Observe that the fuzzing stops quickly with a message like
Done 5036370 runs in 21 second(s)
.
Expected behavior
Either the flag description should say that the file can't be empty, or fuzzing should stop even if the file is empty.
Potential cause of the issue
I believe this is the part of the code that stops the fuzzing loop if the file exists:
if (!Options.StopFile.empty() &&
!FileToVector(Options.StopFile, 1, false).empty())
break;
FileToVector()
is implemented here:
Unit FileToVector(const std::string &Path, size_t MaxSize, bool ExitOnError) {
std::ifstream T(Path, std::ios::binary);
if (ExitOnError && !T) {
Printf("No such directory: %s; exiting\n", Path.c_str());
exit(1);
}
T.seekg(0, T.end);
auto EndPos = T.tellg();
if (EndPos < 0) return {};
size_t FileLen = EndPos;
if (MaxSize)
FileLen = std::min(FileLen, MaxSize);
T.seekg(0, T.beg);
Unit Res(FileLen);
T.read(reinterpret_cast<char *>(Res.data()), FileLen);
return Res;
}
This returns {}
if the file doesn't exist, but also if the file is empty, leading to this behavior.