blob: 5126fe74c4e0b30ad75a924abd89f16e3a00f7a7 [file] [log] [blame]
kcc2e6ca5c2019-02-12 22:48:55 +00001//===- FuzzerFork.cpp - run fuzzing in separate subprocesses --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
kcc64bcb922019-02-13 04:04:45 +00008// Spawn and orchestrate separate fuzzing processes.
kcc2e6ca5c2019-02-12 22:48:55 +00009//===----------------------------------------------------------------------===//
10
11#include "FuzzerCommand.h"
12#include "FuzzerFork.h"
13#include "FuzzerIO.h"
14#include "FuzzerMerge.h"
15#include "FuzzerSHA1.h"
16#include "FuzzerUtil.h"
17
18namespace fuzzer {
19
kcc64bcb922019-02-13 04:04:45 +000020struct FuzzJob {
21 // Inputs.
22 Command Cmd;
23 Vector<std::string> Files;
24 std::string CorpusDir;
25 std::string LogPath;
26 std::string CFPath;
27 int MaxTotalTimeSec;
28
29 // Fuzzing Outputs.
30 int ExitCode;
31};
32
33struct GlobalEnv {
34 const Vector<std::string> *Args;
35 std::string MainCorpusDir;
36 Set<uint32_t> Features;
37 Vector<std::string> Files;
38};
39
40void RunOneFuzzingJob(FuzzJob *Job) {
41 Command &Cmd = Job->Cmd;
42 if (!Job->Files.empty()) {
43 std::string Seeds;
44 for (const auto &File : Job->Files)
45 Seeds += (Seeds.empty() ? "" : ",") + File;
46 Cmd.addFlag("seed_inputs", Seeds);
47 }
48 Cmd.addFlag("max_total_time", std::to_string(Job->MaxTotalTimeSec));
49 Cmd.setOutputFile(Job->LogPath);
50 Cmd.combineOutAndErr();
51 Cmd.addArgument(Job->CorpusDir);
52 RmDirRecursive(Job->CorpusDir);
53 MkDir(Job->CorpusDir);
54 Job->ExitCode = ExecuteCommand(Cmd);
55}
56
57void RunOneMergeJob(GlobalEnv *Env, FuzzJob *Job) {
58 Vector<SizedFile> TempFiles;
59 GetSizedFilesFromDir(Job->CorpusDir, &TempFiles);
60
61 Vector<std::string>FilesToAdd;
62 Set<uint32_t> NewFeatures;
63 CrashResistantMerge(*Env->Args, {}, TempFiles, &FilesToAdd, Env->Features,
64 &NewFeatures, Job->CFPath, false);
65 RemoveFile(Job->CFPath);
66 for (auto &Path : FilesToAdd) {
67 auto U = FileToVector(Path);
68 auto NewPath = DirPlusFile(Env->MainCorpusDir, Hash(U));
69 WriteToFile(U, NewPath);
70 Env->Files.push_back(NewPath);
71 }
72 RmDirRecursive(Job->CorpusDir);
73 Env->Features.insert(NewFeatures.begin(), NewFeatures.end());
74 Printf("INFO: temp_files: %zd files_added: %zd newft: %zd ft: %zd\n",
75 TempFiles.size(), FilesToAdd.size(), NewFeatures.size(),
76 Env->Features.size());
77}
78
kcc2e6ca5c2019-02-12 22:48:55 +000079// This is just a skeleton of an experimental -fork=1 feature.
80void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
81 const Vector<std::string> &Args,
82 const Vector<std::string> &CorpusDirs) {
83 Printf("INFO: -fork=1: doing fuzzing in a separate process in order to "
84 "be more resistant to crashes, timeouts, and OOMs\n");
85
kcc64bcb922019-02-13 04:04:45 +000086 GlobalEnv Env;
87 Env.Args = &Args;
88
kcc2e6ca5c2019-02-12 22:48:55 +000089 Vector<SizedFile> SeedFiles;
90 for (auto &Dir : CorpusDirs)
91 GetSizedFilesFromDir(Dir, &SeedFiles);
92 std::sort(SeedFiles.begin(), SeedFiles.end());
kcc64bcb922019-02-13 04:04:45 +000093 auto TempDir = TempPath(".dir");
94 RmDirRecursive(TempDir); // just in case there is a leftover from an old run.
kcc2e6ca5c2019-02-12 22:48:55 +000095 MkDir(TempDir);
96
kcc64bcb922019-02-13 04:04:45 +000097 auto CFPath = DirPlusFile(TempDir, "merge.txt");
98 auto LogPath = DirPlusFile(TempDir, "sub.log");
kcc2e6ca5c2019-02-12 22:48:55 +000099
kcc64bcb922019-02-13 04:04:45 +0000100 if (CorpusDirs.empty())
101 MkDir(Env.MainCorpusDir = DirPlusFile(TempDir, "C"));
102 else
103 Env.MainCorpusDir = CorpusDirs[0];
104
105 auto TempCorpusDir = DirPlusFile(TempDir, "C0");
106
107 CrashResistantMerge(*Env.Args, {}, SeedFiles, &Env.Files, {}, &Env.Features,
108 CFPath, false);
109 RemoveFile(CFPath);
110 Printf("INFO: -fork=1: %zd seeds, starting to fuzz; scratch: %s\n",
111 Env.Files.size(), TempDir.c_str());
112
113 Command BaseCmd(*Env.Args);
kcc2e6ca5c2019-02-12 22:48:55 +0000114 BaseCmd.removeFlag("fork");
115 for (auto &C : CorpusDirs) // Remove all corpora from the args.
116 BaseCmd.removeArgument(C);
kcc2e6ca5c2019-02-12 22:48:55 +0000117 BaseCmd.addFlag("reload", "0"); // working in an isolated dir, no reload.
118 int ExitCode = 0;
kcc64bcb922019-02-13 04:04:45 +0000119
120
121 for (size_t i = 1; i < 1000000; i++) {
kcc2e6ca5c2019-02-12 22:48:55 +0000122 // TODO: take new files from disk e.g. those generated by another process.
kcc64bcb922019-02-13 04:04:45 +0000123
124 FuzzJob Job;
125 Job.Cmd = BaseCmd;
126 if (size_t CorpusSubsetSize = std::min(Env.Files.size(), (size_t)100))
127 for (size_t i = 0; i < CorpusSubsetSize; i++)
128 Job.Files.push_back(Env.Files[Rand.SkewTowardsLast(Env.Files.size())]);
129 Job.CorpusDir = TempCorpusDir;
130 Job.LogPath = LogPath;
131 Job.CFPath = CFPath;
132 // Start from very short runs and gradually increase them.
133 Job.MaxTotalTimeSec = std::min(300, (int)i);
134 RunOneFuzzingJob(&Job);
135
136 if (Options.Verbosity >= 2)
137 Printf("done [%d] %s\n", Job.ExitCode, Job.Cmd.toString().c_str());
138 if (Job.ExitCode == Options.InterruptExitCode)
kcc2e6ca5c2019-02-12 22:48:55 +0000139 break;
kcc64bcb922019-02-13 04:04:45 +0000140
141 RunOneMergeJob(&Env, &Job);
142
kcc2e6ca5c2019-02-12 22:48:55 +0000143 // Continue if our crash is one of the ignorred ones.
kcc64bcb922019-02-13 04:04:45 +0000144 if (Options.IgnoreTimeouts && Job.ExitCode == Options.TimeoutExitCode)
kcc2e6ca5c2019-02-12 22:48:55 +0000145 continue;
kcc64bcb922019-02-13 04:04:45 +0000146 if (Options.IgnoreOOMs && Job.ExitCode == Options.OOMExitCode)
kcc2e6ca5c2019-02-12 22:48:55 +0000147 continue;
148 // And exit if we don't ignore this crash.
kcc64bcb922019-02-13 04:04:45 +0000149 if (Job.ExitCode != 0) {
kcc2e6ca5c2019-02-12 22:48:55 +0000150 Printf("INFO: log from the inner process:\n%s",
151 FileToString(LogPath).c_str());
kcc64bcb922019-02-13 04:04:45 +0000152 ExitCode = Job.ExitCode;
kcc2e6ca5c2019-02-12 22:48:55 +0000153 break;
154 }
155 }
156
kcc64bcb922019-02-13 04:04:45 +0000157 RmDirRecursive(TempDir);
kcc2e6ca5c2019-02-12 22:48:55 +0000158
159 // Use the exit code from the last child process.
160 Printf("Fork: exiting: %d\n", ExitCode);
161 exit(ExitCode);
162}
163
164} // namespace fuzzer
165