File: | projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp |
Warning: | line 750, column 12 Potential leak of memory pointed to by 'F' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | //===- FuzzerDriver.cpp - FuzzerDriver function and flags -----------------===// | |||
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 | // FuzzerDriver and flag parsing. | |||
9 | //===----------------------------------------------------------------------===// | |||
10 | ||||
11 | #include "FuzzerCommand.h" | |||
12 | #include "FuzzerCorpus.h" | |||
13 | #include "FuzzerFork.h" | |||
14 | #include "FuzzerIO.h" | |||
15 | #include "FuzzerInterface.h" | |||
16 | #include "FuzzerInternal.h" | |||
17 | #include "FuzzerMerge.h" | |||
18 | #include "FuzzerMutate.h" | |||
19 | #include "FuzzerRandom.h" | |||
20 | #include "FuzzerTracePC.h" | |||
21 | #include <algorithm> | |||
22 | #include <atomic> | |||
23 | #include <chrono> | |||
24 | #include <cstdlib> | |||
25 | #include <cstring> | |||
26 | #include <mutex> | |||
27 | #include <string> | |||
28 | #include <thread> | |||
29 | #include <fstream> | |||
30 | ||||
31 | // This function should be present in the libFuzzer so that the client | |||
32 | // binary can test for its existence. | |||
33 | #if LIBFUZZER_MSVC0 | |||
34 | extern "C" void __libfuzzer_is_present() {} | |||
35 | #pragma comment(linker, "/include:__libfuzzer_is_present") | |||
36 | #else | |||
37 | extern "C" __attribute__((used)) void __libfuzzer_is_present() {} | |||
38 | #endif // LIBFUZZER_MSVC | |||
39 | ||||
40 | namespace fuzzer { | |||
41 | ||||
42 | // Program arguments. | |||
43 | struct FlagDescription { | |||
44 | const char *Name; | |||
45 | const char *Description; | |||
46 | int Default; | |||
47 | int *IntFlag; | |||
48 | const char **StrFlag; | |||
49 | unsigned int *UIntFlag; | |||
50 | }; | |||
51 | ||||
52 | struct { | |||
53 | #define FUZZER_DEPRECATED_FLAG(Name) | |||
54 | #define FUZZER_FLAG_INT(Name, Default, Description) int Name; | |||
55 | #define FUZZER_FLAG_UNSIGNED(Name, Default, Description) unsigned int Name; | |||
56 | #define FUZZER_FLAG_STRING(Name, Description) const char *Name; | |||
57 | #include "FuzzerFlags.def" | |||
58 | #undef FUZZER_DEPRECATED_FLAG | |||
59 | #undef FUZZER_FLAG_INT | |||
60 | #undef FUZZER_FLAG_UNSIGNED | |||
61 | #undef FUZZER_FLAG_STRING | |||
62 | } Flags; | |||
63 | ||||
64 | static const FlagDescription FlagDescriptions [] { | |||
65 | #define FUZZER_DEPRECATED_FLAG(Name) \ | |||
66 | {#Name, "Deprecated; don't use", 0, nullptr, nullptr, nullptr}, | |||
67 | #define FUZZER_FLAG_INT(Name, Default, Description) \ | |||
68 | {#Name, Description, Default, &Flags.Name, nullptr, nullptr}, | |||
69 | #define FUZZER_FLAG_UNSIGNED(Name, Default, Description) \ | |||
70 | {#Name, Description, static_cast<int>(Default), \ | |||
71 | nullptr, nullptr, &Flags.Name}, | |||
72 | #define FUZZER_FLAG_STRING(Name, Description) \ | |||
73 | {#Name, Description, 0, nullptr, &Flags.Name, nullptr}, | |||
74 | #include "FuzzerFlags.def" | |||
75 | #undef FUZZER_DEPRECATED_FLAG | |||
76 | #undef FUZZER_FLAG_INT | |||
77 | #undef FUZZER_FLAG_UNSIGNED | |||
78 | #undef FUZZER_FLAG_STRING | |||
79 | }; | |||
80 | ||||
81 | static const size_t kNumFlags = | |||
82 | sizeof(FlagDescriptions) / sizeof(FlagDescriptions[0]); | |||
83 | ||||
84 | static Vector<std::string> *Inputs; | |||
85 | static std::string *ProgName; | |||
86 | ||||
87 | static void PrintHelp() { | |||
88 | Printf("Usage:\n"); | |||
89 | auto Prog = ProgName->c_str(); | |||
90 | Printf("\nTo run fuzzing pass 0 or more directories.\n"); | |||
91 | Printf("%s [-flag1=val1 [-flag2=val2 ...] ] [dir1 [dir2 ...] ]\n", Prog); | |||
92 | ||||
93 | Printf("\nTo run individual tests without fuzzing pass 1 or more files:\n"); | |||
94 | Printf("%s [-flag1=val1 [-flag2=val2 ...] ] file1 [file2 ...]\n", Prog); | |||
95 | ||||
96 | Printf("\nFlags: (strictly in form -flag=value)\n"); | |||
97 | size_t MaxFlagLen = 0; | |||
98 | for (size_t F = 0; F < kNumFlags; F++) | |||
99 | MaxFlagLen = std::max(strlen(FlagDescriptions[F].Name), MaxFlagLen); | |||
100 | ||||
101 | for (size_t F = 0; F < kNumFlags; F++) { | |||
102 | const auto &D = FlagDescriptions[F]; | |||
103 | if (strstr(D.Description, "internal flag") == D.Description) continue; | |||
104 | Printf(" %s", D.Name); | |||
105 | for (size_t i = 0, n = MaxFlagLen - strlen(D.Name); i < n; i++) | |||
106 | Printf(" "); | |||
107 | Printf("\t"); | |||
108 | Printf("%d\t%s\n", D.Default, D.Description); | |||
109 | } | |||
110 | Printf("\nFlags starting with '--' will be ignored and " | |||
111 | "will be passed verbatim to subprocesses.\n"); | |||
112 | } | |||
113 | ||||
114 | static const char *FlagValue(const char *Param, const char *Name) { | |||
115 | size_t Len = strlen(Name); | |||
116 | if (Param[0] == '-' && strstr(Param + 1, Name) == Param + 1 && | |||
117 | Param[Len + 1] == '=') | |||
118 | return &Param[Len + 2]; | |||
119 | return nullptr; | |||
120 | } | |||
121 | ||||
122 | // Avoid calling stol as it triggers a bug in clang/glibc build. | |||
123 | static long MyStol(const char *Str) { | |||
124 | long Res = 0; | |||
125 | long Sign = 1; | |||
126 | if (*Str == '-') { | |||
127 | Str++; | |||
128 | Sign = -1; | |||
129 | } | |||
130 | for (size_t i = 0; Str[i]; i++) { | |||
131 | char Ch = Str[i]; | |||
132 | if (Ch < '0' || Ch > '9') | |||
133 | return Res; | |||
134 | Res = Res * 10 + (Ch - '0'); | |||
135 | } | |||
136 | return Res * Sign; | |||
137 | } | |||
138 | ||||
139 | static bool ParseOneFlag(const char *Param) { | |||
140 | if (Param[0] != '-') return false; | |||
141 | if (Param[1] == '-') { | |||
142 | static bool PrintedWarning = false; | |||
143 | if (!PrintedWarning) { | |||
144 | PrintedWarning = true; | |||
145 | Printf("INFO: libFuzzer ignores flags that start with '--'\n"); | |||
146 | } | |||
147 | for (size_t F = 0; F < kNumFlags; F++) | |||
148 | if (FlagValue(Param + 1, FlagDescriptions[F].Name)) | |||
149 | Printf("WARNING: did you mean '%s' (single dash)?\n", Param + 1); | |||
150 | return true; | |||
151 | } | |||
152 | for (size_t F = 0; F < kNumFlags; F++) { | |||
153 | const char *Name = FlagDescriptions[F].Name; | |||
154 | const char *Str = FlagValue(Param, Name); | |||
155 | if (Str) { | |||
156 | if (FlagDescriptions[F].IntFlag) { | |||
157 | int Val = MyStol(Str); | |||
158 | *FlagDescriptions[F].IntFlag = Val; | |||
159 | if (Flags.verbosity >= 2) | |||
160 | Printf("Flag: %s %d\n", Name, Val); | |||
161 | return true; | |||
162 | } else if (FlagDescriptions[F].UIntFlag) { | |||
163 | unsigned int Val = std::stoul(Str); | |||
164 | *FlagDescriptions[F].UIntFlag = Val; | |||
165 | if (Flags.verbosity >= 2) | |||
166 | Printf("Flag: %s %u\n", Name, Val); | |||
167 | return true; | |||
168 | } else if (FlagDescriptions[F].StrFlag) { | |||
169 | *FlagDescriptions[F].StrFlag = Str; | |||
170 | if (Flags.verbosity >= 2) | |||
171 | Printf("Flag: %s %s\n", Name, Str); | |||
172 | return true; | |||
173 | } else { // Deprecated flag. | |||
174 | Printf("Flag: %s: deprecated, don't use\n", Name); | |||
175 | return true; | |||
176 | } | |||
177 | } | |||
178 | } | |||
179 | Printf("\n\nWARNING: unrecognized flag '%s'; " | |||
180 | "use -help=1 to list all flags\n\n", Param); | |||
181 | return true; | |||
182 | } | |||
183 | ||||
184 | // We don't use any library to minimize dependencies. | |||
185 | static void ParseFlags(const Vector<std::string> &Args) { | |||
186 | for (size_t F = 0; F < kNumFlags; F++) { | |||
187 | if (FlagDescriptions[F].IntFlag) | |||
188 | *FlagDescriptions[F].IntFlag = FlagDescriptions[F].Default; | |||
189 | if (FlagDescriptions[F].UIntFlag) | |||
190 | *FlagDescriptions[F].UIntFlag = | |||
191 | static_cast<unsigned int>(FlagDescriptions[F].Default); | |||
192 | if (FlagDescriptions[F].StrFlag) | |||
193 | *FlagDescriptions[F].StrFlag = nullptr; | |||
194 | } | |||
195 | Inputs = new Vector<std::string>; | |||
196 | for (size_t A = 1; A < Args.size(); A++) { | |||
197 | if (ParseOneFlag(Args[A].c_str())) { | |||
198 | if (Flags.ignore_remaining_args) | |||
199 | break; | |||
200 | continue; | |||
201 | } | |||
202 | Inputs->push_back(Args[A]); | |||
203 | } | |||
204 | } | |||
205 | ||||
206 | static std::mutex Mu; | |||
207 | ||||
208 | static void PulseThread() { | |||
209 | while (true) { | |||
210 | SleepSeconds(600); | |||
211 | std::lock_guard<std::mutex> Lock(Mu); | |||
212 | Printf("pulse...\n"); | |||
213 | } | |||
214 | } | |||
215 | ||||
216 | static void WorkerThread(const Command &BaseCmd, std::atomic<unsigned> *Counter, | |||
217 | unsigned NumJobs, std::atomic<bool> *HasErrors) { | |||
218 | while (true) { | |||
219 | unsigned C = (*Counter)++; | |||
220 | if (C >= NumJobs) break; | |||
221 | std::string Log = "fuzz-" + std::to_string(C) + ".log"; | |||
222 | Command Cmd(BaseCmd); | |||
223 | Cmd.setOutputFile(Log); | |||
224 | Cmd.combineOutAndErr(); | |||
225 | if (Flags.verbosity) { | |||
226 | std::string CommandLine = Cmd.toString(); | |||
227 | Printf("%s\n", CommandLine.c_str()); | |||
228 | } | |||
229 | int ExitCode = ExecuteCommand(Cmd); | |||
230 | if (ExitCode != 0) | |||
231 | *HasErrors = true; | |||
232 | std::lock_guard<std::mutex> Lock(Mu); | |||
233 | Printf("================== Job %u exited with exit code %d ============\n", | |||
234 | C, ExitCode); | |||
235 | fuzzer::CopyFileToErr(Log); | |||
236 | } | |||
237 | } | |||
238 | ||||
239 | std::string CloneArgsWithoutX(const Vector<std::string> &Args, | |||
240 | const char *X1, const char *X2) { | |||
241 | std::string Cmd; | |||
242 | for (auto &S : Args) { | |||
243 | if (FlagValue(S.c_str(), X1) || FlagValue(S.c_str(), X2)) | |||
244 | continue; | |||
245 | Cmd += S + " "; | |||
246 | } | |||
247 | return Cmd; | |||
248 | } | |||
249 | ||||
250 | static int RunInMultipleProcesses(const Vector<std::string> &Args, | |||
251 | unsigned NumWorkers, unsigned NumJobs) { | |||
252 | std::atomic<unsigned> Counter(0); | |||
253 | std::atomic<bool> HasErrors(false); | |||
254 | Command Cmd(Args); | |||
255 | Cmd.removeFlag("jobs"); | |||
256 | Cmd.removeFlag("workers"); | |||
257 | Vector<std::thread> V; | |||
258 | std::thread Pulse(PulseThread); | |||
259 | Pulse.detach(); | |||
260 | for (unsigned i = 0; i < NumWorkers; i++) | |||
261 | V.push_back(std::thread(WorkerThread, std::ref(Cmd), &Counter, NumJobs, &HasErrors)); | |||
262 | for (auto &T : V) | |||
263 | T.join(); | |||
264 | return HasErrors ? 1 : 0; | |||
265 | } | |||
266 | ||||
267 | static void RssThread(Fuzzer *F, size_t RssLimitMb) { | |||
268 | while (true) { | |||
269 | SleepSeconds(1); | |||
270 | size_t Peak = GetPeakRSSMb(); | |||
271 | if (Peak > RssLimitMb) | |||
272 | F->RssLimitCallback(); | |||
273 | } | |||
274 | } | |||
275 | ||||
276 | static void StartRssThread(Fuzzer *F, size_t RssLimitMb) { | |||
277 | if (!RssLimitMb) return; | |||
278 | std::thread T(RssThread, F, RssLimitMb); | |||
279 | T.detach(); | |||
280 | } | |||
281 | ||||
282 | int RunOneTest(Fuzzer *F, const char *InputFilePath, size_t MaxLen) { | |||
283 | Unit U = FileToVector(InputFilePath); | |||
284 | if (MaxLen && MaxLen < U.size()) | |||
285 | U.resize(MaxLen); | |||
286 | F->ExecuteCallback(U.data(), U.size()); | |||
287 | F->TryDetectingAMemoryLeak(U.data(), U.size(), true); | |||
288 | return 0; | |||
289 | } | |||
290 | ||||
291 | static bool AllInputsAreFiles() { | |||
292 | if (Inputs->empty()) return false; | |||
293 | for (auto &Path : *Inputs) | |||
294 | if (!IsFile(Path)) | |||
295 | return false; | |||
296 | return true; | |||
297 | } | |||
298 | ||||
299 | static std::string GetDedupTokenFromFile(const std::string &Path) { | |||
300 | auto S = FileToString(Path); | |||
301 | auto Beg = S.find("DEDUP_TOKEN:"); | |||
302 | if (Beg == std::string::npos) | |||
303 | return ""; | |||
304 | auto End = S.find('\n', Beg); | |||
305 | if (End == std::string::npos) | |||
306 | return ""; | |||
307 | return S.substr(Beg, End - Beg); | |||
308 | } | |||
309 | ||||
310 | int CleanseCrashInput(const Vector<std::string> &Args, | |||
311 | const FuzzingOptions &Options) { | |||
312 | if (Inputs->size() != 1 || !Flags.exact_artifact_path) { | |||
313 | Printf("ERROR: -cleanse_crash should be given one input file and" | |||
314 | " -exact_artifact_path\n"); | |||
315 | exit(1); | |||
316 | } | |||
317 | std::string InputFilePath = Inputs->at(0); | |||
318 | std::string OutputFilePath = Flags.exact_artifact_path; | |||
319 | Command Cmd(Args); | |||
320 | Cmd.removeFlag("cleanse_crash"); | |||
321 | ||||
322 | assert(Cmd.hasArgument(InputFilePath))((Cmd.hasArgument(InputFilePath)) ? static_cast<void> ( 0) : __assert_fail ("Cmd.hasArgument(InputFilePath)", "/build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp" , 322, __PRETTY_FUNCTION__)); | |||
323 | Cmd.removeArgument(InputFilePath); | |||
324 | ||||
325 | auto LogFilePath = TempPath(".txt"); | |||
326 | auto TmpFilePath = TempPath(".repro"); | |||
327 | Cmd.addArgument(TmpFilePath); | |||
328 | Cmd.setOutputFile(LogFilePath); | |||
329 | Cmd.combineOutAndErr(); | |||
330 | ||||
331 | std::string CurrentFilePath = InputFilePath; | |||
332 | auto U = FileToVector(CurrentFilePath); | |||
333 | size_t Size = U.size(); | |||
334 | ||||
335 | const Vector<uint8_t> ReplacementBytes = {' ', 0xff}; | |||
336 | for (int NumAttempts = 0; NumAttempts < 5; NumAttempts++) { | |||
337 | bool Changed = false; | |||
338 | for (size_t Idx = 0; Idx < Size; Idx++) { | |||
339 | Printf("CLEANSE[%d]: Trying to replace byte %zd of %zd\n", NumAttempts, | |||
340 | Idx, Size); | |||
341 | uint8_t OriginalByte = U[Idx]; | |||
342 | if (ReplacementBytes.end() != std::find(ReplacementBytes.begin(), | |||
343 | ReplacementBytes.end(), | |||
344 | OriginalByte)) | |||
345 | continue; | |||
346 | for (auto NewByte : ReplacementBytes) { | |||
347 | U[Idx] = NewByte; | |||
348 | WriteToFile(U, TmpFilePath); | |||
349 | auto ExitCode = ExecuteCommand(Cmd); | |||
350 | RemoveFile(TmpFilePath); | |||
351 | if (!ExitCode) { | |||
352 | U[Idx] = OriginalByte; | |||
353 | } else { | |||
354 | Changed = true; | |||
355 | Printf("CLEANSE: Replaced byte %zd with 0x%x\n", Idx, NewByte); | |||
356 | WriteToFile(U, OutputFilePath); | |||
357 | break; | |||
358 | } | |||
359 | } | |||
360 | } | |||
361 | if (!Changed) break; | |||
362 | } | |||
363 | RemoveFile(LogFilePath); | |||
364 | return 0; | |||
365 | } | |||
366 | ||||
367 | int MinimizeCrashInput(const Vector<std::string> &Args, | |||
368 | const FuzzingOptions &Options) { | |||
369 | if (Inputs->size() != 1) { | |||
370 | Printf("ERROR: -minimize_crash should be given one input file\n"); | |||
371 | exit(1); | |||
372 | } | |||
373 | std::string InputFilePath = Inputs->at(0); | |||
374 | Command BaseCmd(Args); | |||
375 | BaseCmd.removeFlag("minimize_crash"); | |||
376 | BaseCmd.removeFlag("exact_artifact_path"); | |||
377 | assert(BaseCmd.hasArgument(InputFilePath))((BaseCmd.hasArgument(InputFilePath)) ? static_cast<void> (0) : __assert_fail ("BaseCmd.hasArgument(InputFilePath)", "/build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp" , 377, __PRETTY_FUNCTION__)); | |||
378 | BaseCmd.removeArgument(InputFilePath); | |||
379 | if (Flags.runs <= 0 && Flags.max_total_time == 0) { | |||
380 | Printf("INFO: you need to specify -runs=N or " | |||
381 | "-max_total_time=N with -minimize_crash=1\n" | |||
382 | "INFO: defaulting to -max_total_time=600\n"); | |||
383 | BaseCmd.addFlag("max_total_time", "600"); | |||
384 | } | |||
385 | ||||
386 | auto LogFilePath = TempPath(".txt"); | |||
387 | BaseCmd.setOutputFile(LogFilePath); | |||
388 | BaseCmd.combineOutAndErr(); | |||
389 | ||||
390 | std::string CurrentFilePath = InputFilePath; | |||
391 | while (true) { | |||
392 | Unit U = FileToVector(CurrentFilePath); | |||
393 | Printf("CRASH_MIN: minimizing crash input: '%s' (%zd bytes)\n", | |||
394 | CurrentFilePath.c_str(), U.size()); | |||
395 | ||||
396 | Command Cmd(BaseCmd); | |||
397 | Cmd.addArgument(CurrentFilePath); | |||
398 | ||||
399 | std::string CommandLine = Cmd.toString(); | |||
400 | Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str()); | |||
401 | int ExitCode = ExecuteCommand(Cmd); | |||
402 | if (ExitCode == 0) { | |||
403 | Printf("ERROR: the input %s did not crash\n", CurrentFilePath.c_str()); | |||
404 | exit(1); | |||
405 | } | |||
406 | Printf("CRASH_MIN: '%s' (%zd bytes) caused a crash. Will try to minimize " | |||
407 | "it further\n", | |||
408 | CurrentFilePath.c_str(), U.size()); | |||
409 | auto DedupToken1 = GetDedupTokenFromFile(LogFilePath); | |||
410 | if (!DedupToken1.empty()) | |||
411 | Printf("CRASH_MIN: DedupToken1: %s\n", DedupToken1.c_str()); | |||
412 | ||||
413 | std::string ArtifactPath = | |||
414 | Flags.exact_artifact_path | |||
415 | ? Flags.exact_artifact_path | |||
416 | : Options.ArtifactPrefix + "minimized-from-" + Hash(U); | |||
417 | Cmd.addFlag("minimize_crash_internal_step", "1"); | |||
418 | Cmd.addFlag("exact_artifact_path", ArtifactPath); | |||
419 | CommandLine = Cmd.toString(); | |||
420 | Printf("CRASH_MIN: executing: %s\n", CommandLine.c_str()); | |||
421 | ExitCode = ExecuteCommand(Cmd); | |||
422 | CopyFileToErr(LogFilePath); | |||
423 | if (ExitCode == 0) { | |||
424 | if (Flags.exact_artifact_path) { | |||
425 | CurrentFilePath = Flags.exact_artifact_path; | |||
426 | WriteToFile(U, CurrentFilePath); | |||
427 | } | |||
428 | Printf("CRASH_MIN: failed to minimize beyond %s (%d bytes), exiting\n", | |||
429 | CurrentFilePath.c_str(), U.size()); | |||
430 | break; | |||
431 | } | |||
432 | auto DedupToken2 = GetDedupTokenFromFile(LogFilePath); | |||
433 | if (!DedupToken2.empty()) | |||
434 | Printf("CRASH_MIN: DedupToken2: %s\n", DedupToken2.c_str()); | |||
435 | ||||
436 | if (DedupToken1 != DedupToken2) { | |||
437 | if (Flags.exact_artifact_path) { | |||
438 | CurrentFilePath = Flags.exact_artifact_path; | |||
439 | WriteToFile(U, CurrentFilePath); | |||
440 | } | |||
441 | Printf("CRASH_MIN: mismatch in dedup tokens" | |||
442 | " (looks like a different bug). Won't minimize further\n"); | |||
443 | break; | |||
444 | } | |||
445 | ||||
446 | CurrentFilePath = ArtifactPath; | |||
447 | Printf("*********************************\n"); | |||
448 | } | |||
449 | RemoveFile(LogFilePath); | |||
450 | return 0; | |||
451 | } | |||
452 | ||||
453 | int MinimizeCrashInputInternalStep(Fuzzer *F, InputCorpus *Corpus) { | |||
454 | assert(Inputs->size() == 1)((Inputs->size() == 1) ? static_cast<void> (0) : __assert_fail ("Inputs->size() == 1", "/build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp" , 454, __PRETTY_FUNCTION__)); | |||
455 | std::string InputFilePath = Inputs->at(0); | |||
456 | Unit U = FileToVector(InputFilePath); | |||
457 | Printf("INFO: Starting MinimizeCrashInputInternalStep: %zd\n", U.size()); | |||
458 | if (U.size() < 2) { | |||
459 | Printf("INFO: The input is small enough, exiting\n"); | |||
460 | exit(0); | |||
461 | } | |||
462 | F->SetMaxInputLen(U.size()); | |||
463 | F->SetMaxMutationLen(U.size() - 1); | |||
464 | F->MinimizeCrashLoop(U); | |||
465 | Printf("INFO: Done MinimizeCrashInputInternalStep, no crashes found\n"); | |||
466 | exit(0); | |||
467 | return 0; | |||
468 | } | |||
469 | ||||
470 | void Merge(Fuzzer *F, FuzzingOptions &Options, const Vector<std::string> &Args, | |||
471 | const Vector<std::string> &Corpora, const char *CFPathOrNull) { | |||
472 | if (Corpora.size() < 2) { | |||
473 | Printf("INFO: Merge requires two or more corpus dirs\n"); | |||
474 | exit(0); | |||
475 | } | |||
476 | ||||
477 | Vector<SizedFile> OldCorpus, NewCorpus; | |||
478 | GetSizedFilesFromDir(Corpora[0], &OldCorpus); | |||
479 | for (size_t i = 1; i < Corpora.size(); i++) | |||
480 | GetSizedFilesFromDir(Corpora[i], &NewCorpus); | |||
481 | std::sort(OldCorpus.begin(), OldCorpus.end()); | |||
482 | std::sort(NewCorpus.begin(), NewCorpus.end()); | |||
483 | ||||
484 | std::string CFPath = CFPathOrNull ? CFPathOrNull : TempPath(".txt"); | |||
485 | Vector<std::string> NewFiles; | |||
486 | Set<uint32_t> NewFeatures, NewCov; | |||
487 | CrashResistantMerge(Args, OldCorpus, NewCorpus, &NewFiles, {}, &NewFeatures, | |||
488 | {}, &NewCov, CFPath, true); | |||
489 | for (auto &Path : NewFiles) | |||
490 | F->WriteToOutputCorpus(FileToVector(Path, Options.MaxLen)); | |||
491 | // We are done, delete the control file if it was a temporary one. | |||
492 | if (!Flags.merge_control_file) | |||
493 | RemoveFile(CFPath); | |||
494 | ||||
495 | exit(0); | |||
496 | } | |||
497 | ||||
498 | int AnalyzeDictionary(Fuzzer *F, const Vector<Unit>& Dict, | |||
499 | UnitVector& Corpus) { | |||
500 | Printf("Started dictionary minimization (up to %d tests)\n", | |||
501 | Dict.size() * Corpus.size() * 2); | |||
502 | ||||
503 | // Scores and usage count for each dictionary unit. | |||
504 | Vector<int> Scores(Dict.size()); | |||
505 | Vector<int> Usages(Dict.size()); | |||
506 | ||||
507 | Vector<size_t> InitialFeatures; | |||
508 | Vector<size_t> ModifiedFeatures; | |||
509 | for (auto &C : Corpus) { | |||
510 | // Get coverage for the testcase without modifications. | |||
511 | F->ExecuteCallback(C.data(), C.size()); | |||
512 | InitialFeatures.clear(); | |||
513 | TPC.CollectFeatures([&](size_t Feature) { | |||
514 | InitialFeatures.push_back(Feature); | |||
515 | }); | |||
516 | ||||
517 | for (size_t i = 0; i < Dict.size(); ++i) { | |||
518 | Vector<uint8_t> Data = C; | |||
519 | auto StartPos = std::search(Data.begin(), Data.end(), | |||
520 | Dict[i].begin(), Dict[i].end()); | |||
521 | // Skip dictionary unit, if the testcase does not contain it. | |||
522 | if (StartPos == Data.end()) | |||
523 | continue; | |||
524 | ||||
525 | ++Usages[i]; | |||
526 | while (StartPos != Data.end()) { | |||
527 | // Replace all occurrences of dictionary unit in the testcase. | |||
528 | auto EndPos = StartPos + Dict[i].size(); | |||
529 | for (auto It = StartPos; It != EndPos; ++It) | |||
530 | *It ^= 0xFF; | |||
531 | ||||
532 | StartPos = std::search(EndPos, Data.end(), | |||
533 | Dict[i].begin(), Dict[i].end()); | |||
534 | } | |||
535 | ||||
536 | // Get coverage for testcase with masked occurrences of dictionary unit. | |||
537 | F->ExecuteCallback(Data.data(), Data.size()); | |||
538 | ModifiedFeatures.clear(); | |||
539 | TPC.CollectFeatures([&](size_t Feature) { | |||
540 | ModifiedFeatures.push_back(Feature); | |||
541 | }); | |||
542 | ||||
543 | if (InitialFeatures == ModifiedFeatures) | |||
544 | --Scores[i]; | |||
545 | else | |||
546 | Scores[i] += 2; | |||
547 | } | |||
548 | } | |||
549 | ||||
550 | Printf("###### Useless dictionary elements. ######\n"); | |||
551 | for (size_t i = 0; i < Dict.size(); ++i) { | |||
552 | // Dictionary units with positive score are treated as useful ones. | |||
553 | if (Scores[i] > 0) | |||
554 | continue; | |||
555 | ||||
556 | Printf("\""); | |||
557 | PrintASCII(Dict[i].data(), Dict[i].size(), "\""); | |||
558 | Printf(" # Score: %d, Used: %d\n", Scores[i], Usages[i]); | |||
559 | } | |||
560 | Printf("###### End of useless dictionary elements. ######\n"); | |||
561 | return 0; | |||
562 | } | |||
563 | ||||
564 | Vector<std::string> ParseSeedInuts(const char *seed_inputs) { | |||
565 | // Parse -seed_inputs=file1,file2,... or -seed_inputs=@seed_inputs_file | |||
566 | Vector<std::string> Files; | |||
567 | if (!seed_inputs) return Files; | |||
568 | std::string SeedInputs; | |||
569 | if (Flags.seed_inputs[0] == '@') | |||
570 | SeedInputs = FileToString(Flags.seed_inputs + 1); // File contains list. | |||
571 | else | |||
572 | SeedInputs = Flags.seed_inputs; // seed_inputs contains the list. | |||
573 | if (SeedInputs.empty()) { | |||
574 | Printf("seed_inputs is empty or @file does not exist.\n"); | |||
575 | exit(1); | |||
576 | } | |||
577 | // Parse SeedInputs. | |||
578 | size_t comma_pos = 0; | |||
579 | while ((comma_pos = SeedInputs.find_last_of(',')) != std::string::npos) { | |||
580 | Files.push_back(SeedInputs.substr(comma_pos + 1)); | |||
581 | SeedInputs = SeedInputs.substr(0, comma_pos); | |||
582 | } | |||
583 | Files.push_back(SeedInputs); | |||
584 | return Files; | |||
585 | } | |||
586 | ||||
587 | static Vector<SizedFile> ReadCorpora(const Vector<std::string> &CorpusDirs, | |||
588 | const Vector<std::string> &ExtraSeedFiles) { | |||
589 | Vector<SizedFile> SizedFiles; | |||
590 | size_t LastNumFiles = 0; | |||
591 | for (auto &Dir : CorpusDirs) { | |||
592 | GetSizedFilesFromDir(Dir, &SizedFiles); | |||
593 | Printf("INFO: % 8zd files found in %s\n", SizedFiles.size() - LastNumFiles, | |||
594 | Dir.c_str()); | |||
595 | LastNumFiles = SizedFiles.size(); | |||
596 | } | |||
597 | for (auto &File : ExtraSeedFiles) | |||
598 | if (auto Size = FileSize(File)) | |||
599 | SizedFiles.push_back({File, Size}); | |||
600 | return SizedFiles; | |||
601 | } | |||
602 | ||||
603 | int FuzzerDriver(int *argc, char ***argv, UserCallback Callback) { | |||
604 | using namespace fuzzer; | |||
605 | assert(argc && argv && "Argument pointers cannot be nullptr")((argc && argv && "Argument pointers cannot be nullptr" ) ? static_cast<void> (0) : __assert_fail ("argc && argv && \"Argument pointers cannot be nullptr\"" , "/build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp" , 605, __PRETTY_FUNCTION__)); | |||
| ||||
606 | std::string Argv0((*argv)[0]); | |||
607 | EF = new ExternalFunctions(); | |||
608 | if (EF->LLVMFuzzerInitialize) | |||
609 | EF->LLVMFuzzerInitialize(argc, argv); | |||
610 | if (EF->__msan_scoped_disable_interceptor_checks) | |||
611 | EF->__msan_scoped_disable_interceptor_checks(); | |||
612 | const Vector<std::string> Args(*argv, *argv + *argc); | |||
613 | assert(!Args.empty())((!Args.empty()) ? static_cast<void> (0) : __assert_fail ("!Args.empty()", "/build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp" , 613, __PRETTY_FUNCTION__)); | |||
614 | ProgName = new std::string(Args[0]); | |||
615 | if (Argv0 != *ProgName) { | |||
616 | Printf("ERROR: argv[0] has been modified in LLVMFuzzerInitialize\n"); | |||
617 | exit(1); | |||
618 | } | |||
619 | ParseFlags(Args); | |||
620 | if (Flags.help) { | |||
621 | PrintHelp(); | |||
622 | return 0; | |||
623 | } | |||
624 | ||||
625 | if (Flags.close_fd_mask & 2) | |||
626 | DupAndCloseStderr(); | |||
627 | if (Flags.close_fd_mask & 1) | |||
628 | CloseStdout(); | |||
629 | ||||
630 | if (Flags.jobs > 0 && Flags.workers == 0) { | |||
631 | Flags.workers = std::min(NumberOfCpuCores() / 2, Flags.jobs); | |||
632 | if (Flags.workers > 1) | |||
633 | Printf("Running %u workers\n", Flags.workers); | |||
634 | } | |||
635 | ||||
636 | if (Flags.workers > 0 && Flags.jobs > 0) | |||
637 | return RunInMultipleProcesses(Args, Flags.workers, Flags.jobs); | |||
638 | ||||
639 | FuzzingOptions Options; | |||
640 | Options.Verbosity = Flags.verbosity; | |||
641 | Options.MaxLen = Flags.max_len; | |||
642 | Options.LenControl = Flags.len_control; | |||
643 | Options.UnitTimeoutSec = Flags.timeout; | |||
644 | Options.ErrorExitCode = Flags.error_exitcode; | |||
645 | Options.TimeoutExitCode = Flags.timeout_exitcode; | |||
646 | Options.IgnoreTimeouts = Flags.ignore_timeouts; | |||
647 | Options.IgnoreOOMs = Flags.ignore_ooms; | |||
648 | Options.IgnoreCrashes = Flags.ignore_crashes; | |||
649 | Options.MaxTotalTimeSec = Flags.max_total_time; | |||
650 | Options.DoCrossOver = Flags.cross_over; | |||
651 | Options.MutateDepth = Flags.mutate_depth; | |||
652 | Options.ReduceDepth = Flags.reduce_depth; | |||
653 | Options.UseCounters = Flags.use_counters; | |||
654 | Options.UseMemmem = Flags.use_memmem; | |||
655 | Options.UseCmp = Flags.use_cmp; | |||
656 | Options.UseValueProfile = Flags.use_value_profile; | |||
657 | Options.Shrink = Flags.shrink; | |||
658 | Options.ReduceInputs = Flags.reduce_inputs; | |||
659 | Options.ShuffleAtStartUp = Flags.shuffle; | |||
660 | Options.PreferSmall = Flags.prefer_small; | |||
661 | Options.ReloadIntervalSec = Flags.reload; | |||
662 | Options.OnlyASCII = Flags.only_ascii; | |||
663 | Options.DetectLeaks = Flags.detect_leaks; | |||
664 | Options.PurgeAllocatorIntervalSec = Flags.purge_allocator_interval; | |||
665 | Options.TraceMalloc = Flags.trace_malloc; | |||
666 | Options.RssLimitMb = Flags.rss_limit_mb; | |||
667 | Options.MallocLimitMb = Flags.malloc_limit_mb; | |||
668 | if (!Options.MallocLimitMb) | |||
669 | Options.MallocLimitMb = Options.RssLimitMb; | |||
670 | if (Flags.runs >= 0) | |||
671 | Options.MaxNumberOfRuns = Flags.runs; | |||
672 | if (!Inputs->empty() && !Flags.minimize_crash_internal_step) | |||
673 | Options.OutputCorpus = (*Inputs)[0]; | |||
674 | Options.ReportSlowUnits = Flags.report_slow_units; | |||
675 | if (Flags.artifact_prefix) | |||
676 | Options.ArtifactPrefix = Flags.artifact_prefix; | |||
677 | if (Flags.exact_artifact_path) | |||
678 | Options.ExactArtifactPath = Flags.exact_artifact_path; | |||
679 | Vector<Unit> Dictionary; | |||
680 | if (Flags.dict) | |||
681 | if (!ParseDictionaryFile(FileToString(Flags.dict), &Dictionary)) | |||
682 | return 1; | |||
683 | if (Flags.verbosity > 0 && !Dictionary.empty()) | |||
684 | Printf("Dictionary: %zd entries\n", Dictionary.size()); | |||
685 | bool RunIndividualFiles = AllInputsAreFiles(); | |||
686 | Options.SaveArtifacts = | |||
687 | !RunIndividualFiles || Flags.minimize_crash_internal_step; | |||
688 | Options.PrintNewCovPcs = Flags.print_pcs; | |||
689 | Options.PrintNewCovFuncs = Flags.print_funcs; | |||
690 | Options.PrintFinalStats = Flags.print_final_stats; | |||
691 | Options.PrintCorpusStats = Flags.print_corpus_stats; | |||
692 | Options.PrintCoverage = Flags.print_coverage; | |||
693 | if (Flags.exit_on_src_pos) | |||
694 | Options.ExitOnSrcPos = Flags.exit_on_src_pos; | |||
695 | if (Flags.exit_on_item) | |||
696 | Options.ExitOnItem = Flags.exit_on_item; | |||
697 | if (Flags.focus_function) | |||
698 | Options.FocusFunction = Flags.focus_function; | |||
699 | if (Flags.data_flow_trace) | |||
700 | Options.DataFlowTrace = Flags.data_flow_trace; | |||
701 | if (Flags.features_dir) | |||
702 | Options.FeaturesDir = Flags.features_dir; | |||
703 | if (Flags.collect_data_flow) | |||
704 | Options.CollectDataFlow = Flags.collect_data_flow; | |||
705 | Options.LazyCounters = Flags.lazy_counters; | |||
706 | ||||
707 | unsigned Seed = Flags.seed; | |||
708 | // Initialize Seed. | |||
709 | if (Seed == 0) | |||
710 | Seed = | |||
711 | std::chrono::system_clock::now().time_since_epoch().count() + GetPid(); | |||
712 | if (Flags.verbosity) | |||
713 | Printf("INFO: Seed: %u\n", Seed); | |||
714 | ||||
715 | if (Flags.collect_data_flow && !Flags.fork && !Flags.merge) { | |||
716 | if (RunIndividualFiles) | |||
717 | return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, | |||
718 | ReadCorpora({}, *Inputs)); | |||
719 | else | |||
720 | return CollectDataFlow(Flags.collect_data_flow, Flags.data_flow_trace, | |||
721 | ReadCorpora(*Inputs, {})); | |||
722 | } | |||
723 | ||||
724 | Random Rand(Seed); | |||
725 | auto *MD = new MutationDispatcher(Rand, Options); | |||
726 | auto *Corpus = new InputCorpus(Options.OutputCorpus); | |||
727 | auto *F = new Fuzzer(Callback, *Corpus, *MD, Options); | |||
728 | ||||
729 | for (auto &U: Dictionary) | |||
730 | if (U.size() <= Word::GetMaxSize()) | |||
731 | MD->AddWordToManualDictionary(Word(U.data(), U.size())); | |||
732 | ||||
733 | StartRssThread(F, Flags.rss_limit_mb); | |||
734 | ||||
735 | Options.HandleAbrt = Flags.handle_abrt; | |||
736 | Options.HandleBus = Flags.handle_bus; | |||
737 | Options.HandleFpe = Flags.handle_fpe; | |||
738 | Options.HandleIll = Flags.handle_ill; | |||
739 | Options.HandleInt = Flags.handle_int; | |||
740 | Options.HandleSegv = Flags.handle_segv; | |||
741 | Options.HandleTerm = Flags.handle_term; | |||
742 | Options.HandleXfsz = Flags.handle_xfsz; | |||
743 | Options.HandleUsr1 = Flags.handle_usr1; | |||
744 | Options.HandleUsr2 = Flags.handle_usr2; | |||
745 | SetSignalHandler(Options); | |||
746 | ||||
747 | std::atexit(Fuzzer::StaticExitCallback); | |||
748 | ||||
749 | if (Flags.minimize_crash) | |||
750 | return MinimizeCrashInput(Args, Options); | |||
| ||||
751 | ||||
752 | if (Flags.minimize_crash_internal_step) | |||
753 | return MinimizeCrashInputInternalStep(F, Corpus); | |||
754 | ||||
755 | if (Flags.cleanse_crash) | |||
756 | return CleanseCrashInput(Args, Options); | |||
757 | ||||
758 | if (RunIndividualFiles) { | |||
759 | Options.SaveArtifacts = false; | |||
760 | int Runs = std::max(1, Flags.runs); | |||
761 | Printf("%s: Running %zd inputs %d time(s) each.\n", ProgName->c_str(), | |||
762 | Inputs->size(), Runs); | |||
763 | for (auto &Path : *Inputs) { | |||
764 | auto StartTime = system_clock::now(); | |||
765 | Printf("Running: %s\n", Path.c_str()); | |||
766 | for (int Iter = 0; Iter < Runs; Iter++) | |||
767 | RunOneTest(F, Path.c_str(), Options.MaxLen); | |||
768 | auto StopTime = system_clock::now(); | |||
769 | auto MS = duration_cast<milliseconds>(StopTime - StartTime).count(); | |||
770 | Printf("Executed %s in %zd ms\n", Path.c_str(), (long)MS); | |||
771 | } | |||
772 | Printf("***\n" | |||
773 | "*** NOTE: fuzzing was not performed, you have only\n" | |||
774 | "*** executed the target code on a fixed set of inputs.\n" | |||
775 | "***\n"); | |||
776 | F->PrintFinalStats(); | |||
777 | exit(0); | |||
778 | } | |||
779 | ||||
780 | if (Flags.fork) | |||
781 | FuzzWithFork(F->GetMD().GetRand(), Options, Args, *Inputs, Flags.fork); | |||
782 | ||||
783 | if (Flags.merge) | |||
784 | Merge(F, Options, Args, *Inputs, Flags.merge_control_file); | |||
785 | ||||
786 | if (Flags.merge_inner) { | |||
787 | const size_t kDefaultMaxMergeLen = 1 << 20; | |||
788 | if (Options.MaxLen == 0) | |||
789 | F->SetMaxInputLen(kDefaultMaxMergeLen); | |||
790 | assert(Flags.merge_control_file)((Flags.merge_control_file) ? static_cast<void> (0) : __assert_fail ("Flags.merge_control_file", "/build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp" , 790, __PRETTY_FUNCTION__)); | |||
791 | F->CrashResistantMergeInternalStep(Flags.merge_control_file); | |||
792 | exit(0); | |||
793 | } | |||
794 | ||||
795 | if (Flags.analyze_dict) { | |||
796 | size_t MaxLen = INT_MAX2147483647; // Large max length. | |||
797 | UnitVector InitialCorpus; | |||
798 | for (auto &Inp : *Inputs) { | |||
799 | Printf("Loading corpus dir: %s\n", Inp.c_str()); | |||
800 | ReadDirToVectorOfUnits(Inp.c_str(), &InitialCorpus, nullptr, | |||
801 | MaxLen, /*ExitOnError=*/false); | |||
802 | } | |||
803 | ||||
804 | if (Dictionary.empty() || Inputs->empty()) { | |||
805 | Printf("ERROR: can't analyze dict without dict and corpus provided\n"); | |||
806 | return 1; | |||
807 | } | |||
808 | if (AnalyzeDictionary(F, Dictionary, InitialCorpus)) { | |||
809 | Printf("Dictionary analysis failed\n"); | |||
810 | exit(1); | |||
811 | } | |||
812 | Printf("Dictionary analysis succeeded\n"); | |||
813 | exit(0); | |||
814 | } | |||
815 | ||||
816 | auto CorporaFiles = ReadCorpora(*Inputs, ParseSeedInuts(Flags.seed_inputs)); | |||
817 | F->Loop(CorporaFiles); | |||
818 | ||||
819 | if (Flags.verbosity) | |||
820 | Printf("Done %zd runs in %zd second(s)\n", F->getTotalNumberOfRuns(), | |||
821 | F->secondsSinceProcessStartUp()); | |||
822 | F->PrintFinalStats(); | |||
823 | ||||
824 | exit(0); // Don't let F destroy itself. | |||
825 | } | |||
826 | ||||
827 | // Storage for global ExternalFunctions object. | |||
828 | ExternalFunctions *EF = nullptr; | |||
829 | ||||
830 | } // namespace fuzzer |