blob: bb0ea2a3c98e41daa36b1dd5fea5668deaa7cd4b [file] [log] [blame]
//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Spawn an orchestrate separate fuzzing processes.
//===----------------------------------------------------------------------===//
#include "FuzzerCommand.h"
#include "FuzzerFork.h"
#include "FuzzerIO.h"
#include "FuzzerMerge.h"
#include "FuzzerSHA1.h"
#include "FuzzerUtil.h"
namespace fuzzer {
// This is just a skeleton of an experimental -fork=1 feature.
void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
const Vector<std::string> &Args,
const Vector<std::string> &CorpusDirs) {
Printf("INFO: -fork=1: doing fuzzing in a separate process in order to "
"be more resistant to crashes, timeouts, and OOMs\n");
Vector<SizedFile> SeedFiles;
for (auto &Dir : CorpusDirs)
GetSizedFilesFromDir(Dir, &SeedFiles);
std::sort(SeedFiles.begin(), SeedFiles.end());
auto CFPath = TempPath(".fork");
auto LogPath = TempPath(".log");
auto TempDir = TempPath(".scratch_dir");
std::string MainCorpusDir;
if (CorpusDirs.empty())
MkDir(MainCorpusDir = TempPath(".corpus_dir"));
else
MainCorpusDir = CorpusDirs[0];
MkDir(TempDir);
Vector<std::string> Files;
Set<uint32_t> Features;
if (!SeedFiles.empty()) {
CrashResistantMerge(Args, {}, SeedFiles, &Files, {}, &Features, CFPath,
false);
RemoveFile(CFPath);
}
Printf("INFO: -fork=1: %zd seeds, starting to fuzz; scratch: %s\n",
Files.size(), TempDir.c_str());
Command BaseCmd(Args);
BaseCmd.removeFlag("fork");
for (auto &C : CorpusDirs) // Remove all corpora from the args.
BaseCmd.removeArgument(C);
BaseCmd.addArgument(TempDir);
BaseCmd.addFlag("len_control", "0"); // len_control is bad for short runs.
BaseCmd.addFlag("reload", "0"); // working in an isolated dir, no reload.
int ExitCode = 0;
int max_total_time = 1;
for (size_t i = 0; i < 1000000; i++) {
// TODO: take new files from disk e.g. those generated by another process.
Command Cmd(BaseCmd);
if (size_t CorpusSubsetSize = std::min(Files.size(), (size_t)10)) {
std::string Seeds;
for (size_t i = 0; i < CorpusSubsetSize; i++) {
if (i) Seeds += ",";
Seeds += Files[Rand.SkewTowardsLast(Files.size())];
}
Cmd.addFlag("seed_inputs", Seeds);
}
if (Options.MaxTotalTimeSec > max_total_time)
max_total_time++;
if (!Cmd.hasFlag("max_total_time"))
Cmd.addFlag("max_total_time", std::to_string(max_total_time));
Cmd.setOutputFile(LogPath);
Cmd.combineOutAndErr();
RmFilesInDir(TempDir);
ExitCode = ExecuteCommand(Cmd);
// Printf("done [%d] %s\n", ExitCode, Cmd.toString().c_str());
if (ExitCode == Options.InterruptExitCode)
break;
Vector<SizedFile> TempFiles;
Vector<std::string>FilesToAdd;
Set<uint32_t> NewFeatures;
GetSizedFilesFromDir(TempDir, &TempFiles);
if (!TempFiles.empty())
CrashResistantMerge(Args, {}, TempFiles, &FilesToAdd, Features,
&NewFeatures, CFPath, false);
RemoveFile(CFPath);
for (auto &Path : FilesToAdd) {
auto U = FileToVector(Path);
auto NewPath = DirPlusFile(MainCorpusDir, Hash(U));
WriteToFile(U, NewPath);
Files.push_back(NewPath);
}
Features.insert(NewFeatures.begin(), NewFeatures.end());
Printf("INFO: temp_files: %zd files_added: %zd newft: %zd ft: %zd\n",
TempFiles.size(), FilesToAdd.size(), NewFeatures.size(),
Features.size());
// Continue if our crash is one of the ignorred ones.
if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode)
continue;
if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode)
continue;
// And exit if we don't ignore this crash.
if (ExitCode != 0) {
Printf("INFO: log from the inner process:\n%s",
FileToString(LogPath).c_str());
break;
}
}
RmFilesInDir(TempDir);
RmDir(TempDir);
if (CorpusDirs.empty()) {
RmFilesInDir(MainCorpusDir);
RmDir(MainCorpusDir);
}
// Use the exit code from the last child process.
Printf("Fork: exiting: %d\n", ExitCode);
exit(ExitCode);
}
} // namespace fuzzer