blob: bb0ea2a3c98e41daa36b1dd5fea5668deaa7cd4b [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//===----------------------------------------------------------------------===//
8// Spawn an orchestrate separate fuzzing processes.
9//===----------------------------------------------------------------------===//
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
20// This is just a skeleton of an experimental -fork=1 feature.
21void FuzzWithFork(Random &Rand, const FuzzingOptions &Options,
22 const Vector<std::string> &Args,
23 const Vector<std::string> &CorpusDirs) {
24 Printf("INFO: -fork=1: doing fuzzing in a separate process in order to "
25 "be more resistant to crashes, timeouts, and OOMs\n");
26
27 Vector<SizedFile> SeedFiles;
28 for (auto &Dir : CorpusDirs)
29 GetSizedFilesFromDir(Dir, &SeedFiles);
30 std::sort(SeedFiles.begin(), SeedFiles.end());
31 auto CFPath = TempPath(".fork");
32 auto LogPath = TempPath(".log");
33 auto TempDir = TempPath(".scratch_dir");
34 std::string MainCorpusDir;
35 if (CorpusDirs.empty())
36 MkDir(MainCorpusDir = TempPath(".corpus_dir"));
37 else
38 MainCorpusDir = CorpusDirs[0];
39 MkDir(TempDir);
40
41 Vector<std::string> Files;
42 Set<uint32_t> Features;
43 if (!SeedFiles.empty()) {
44 CrashResistantMerge(Args, {}, SeedFiles, &Files, {}, &Features, CFPath,
45 false);
46 RemoveFile(CFPath);
47 }
48 Printf("INFO: -fork=1: %zd seeds, starting to fuzz; scratch: %s\n",
49 Files.size(), TempDir.c_str());
50
51 Command BaseCmd(Args);
52 BaseCmd.removeFlag("fork");
53 for (auto &C : CorpusDirs) // Remove all corpora from the args.
54 BaseCmd.removeArgument(C);
55 BaseCmd.addArgument(TempDir);
56 BaseCmd.addFlag("len_control", "0"); // len_control is bad for short runs.
57 BaseCmd.addFlag("reload", "0"); // working in an isolated dir, no reload.
58 int ExitCode = 0;
59 int max_total_time = 1;
60 for (size_t i = 0; i < 1000000; i++) {
61 // TODO: take new files from disk e.g. those generated by another process.
62 Command Cmd(BaseCmd);
63 if (size_t CorpusSubsetSize = std::min(Files.size(), (size_t)10)) {
64 std::string Seeds;
65 for (size_t i = 0; i < CorpusSubsetSize; i++) {
66 if (i) Seeds += ",";
67 Seeds += Files[Rand.SkewTowardsLast(Files.size())];
68 }
69 Cmd.addFlag("seed_inputs", Seeds);
70 }
71 if (Options.MaxTotalTimeSec > max_total_time)
72 max_total_time++;
73 if (!Cmd.hasFlag("max_total_time"))
74 Cmd.addFlag("max_total_time", std::to_string(max_total_time));
75 Cmd.setOutputFile(LogPath);
76 Cmd.combineOutAndErr();
77 RmFilesInDir(TempDir);
78 ExitCode = ExecuteCommand(Cmd);
79 // Printf("done [%d] %s\n", ExitCode, Cmd.toString().c_str());
80 if (ExitCode == Options.InterruptExitCode)
81 break;
82 Vector<SizedFile> TempFiles;
83 Vector<std::string>FilesToAdd;
84 Set<uint32_t> NewFeatures;
85 GetSizedFilesFromDir(TempDir, &TempFiles);
86 if (!TempFiles.empty())
87 CrashResistantMerge(Args, {}, TempFiles, &FilesToAdd, Features,
88 &NewFeatures, CFPath, false);
89 RemoveFile(CFPath);
90 for (auto &Path : FilesToAdd) {
91 auto U = FileToVector(Path);
92 auto NewPath = DirPlusFile(MainCorpusDir, Hash(U));
93 WriteToFile(U, NewPath);
94 Files.push_back(NewPath);
95 }
96 Features.insert(NewFeatures.begin(), NewFeatures.end());
97 Printf("INFO: temp_files: %zd files_added: %zd newft: %zd ft: %zd\n",
98 TempFiles.size(), FilesToAdd.size(), NewFeatures.size(),
99 Features.size());
100 // Continue if our crash is one of the ignorred ones.
101 if (Options.IgnoreTimeouts && ExitCode == Options.TimeoutExitCode)
102 continue;
103 if (Options.IgnoreOOMs && ExitCode == Options.OOMExitCode)
104 continue;
105 // And exit if we don't ignore this crash.
106 if (ExitCode != 0) {
107 Printf("INFO: log from the inner process:\n%s",
108 FileToString(LogPath).c_str());
109 break;
110 }
111 }
112
113 RmFilesInDir(TempDir);
114 RmDir(TempDir);
115
116 if (CorpusDirs.empty()) {
117 RmFilesInDir(MainCorpusDir);
118 RmDir(MainCorpusDir);
119 }
120
121 // Use the exit code from the last child process.
122 Printf("Fork: exiting: %d\n", ExitCode);
123 exit(ExitCode);
124}
125
126} // namespace fuzzer
127