Coverage Report

Created: 2018-09-25 14:53

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