@@ -50,6 +50,10 @@ Options:
5050 --storagePath=path : Path at which to store output files (crashes, corpus, etc.) to.
5151 --resume : If storage path exists, import the programs from the corpus/ subdirectory
5252 --overwrite : If storage path exists, delete all data in it and start a fresh fuzzing session
53+ --staticCorpus : In this mode, we will just mutate the existing corpus and look for crashes.
54+ No new samples are added to the corpus, regardless of their coverage.
55+ This can be used to find different manifestations of bugs and
56+ also to try and reproduce a flaky crash or turn it into a deterministic one.
5357 --exportStatistics : If enabled, fuzzing statistics will be collected and saved to disk in regular intervals.
5458 Requires --storagePath.
5559 --statisticsExportInterval=n : Interval in minutes for saving fuzzing statistics to disk (default: 10).
@@ -135,6 +139,7 @@ let minimizationLimit = args.double(for: "--minimizationLimit") ?? 0.0
135139let storagePath = args [ " --storagePath " ]
136140var resume = args. has ( " --resume " )
137141let overwrite = args. has ( " --overwrite " )
142+ let staticCorpus = args. has ( " --staticCorpus " )
138143let exportStatistics = args. has ( " --exportStatistics " )
139144let statisticsExportInterval = args. uint ( for: " --statisticsExportInterval " ) ?? 10
140145let corpusImportAllPath = args [ " --importCorpusAll " ]
@@ -185,10 +190,18 @@ if corpusImportAllPath != nil && corpusName == "markov" {
185190 configError ( " Markov corpus is not compatible with --importCorpusAll " )
186191}
187192
193+ if staticCorpus && !( resume || corpusImportAllPath != nil || corpusImportCovOnlyPath != nil || corpusImportMergePath != nil ) {
194+ configError ( " Static corpus requires either --resume or one of the corpus import modes " )
195+ }
196+
188197if ( resume || overwrite) && storagePath == nil {
189198 configError ( " --resume and --overwrite require --storagePath " )
190199}
191200
201+ if corpusName == " markov " && staticCorpus {
202+ configError ( " Markov corpus is not compatible with --staticCorpus " )
203+ }
204+
192205if let path = storagePath {
193206 let directory = ( try ? FileManager . default. contentsOfDirectory ( atPath: path) ) ?? [ ]
194207 if !directory. isEmpty && !resume && !overwrite {
@@ -394,7 +407,8 @@ let config = Configuration(timeout: UInt32(timeout),
394407 isFuzzing: !dontFuzz,
395408 minimizationLimit: minimizationLimit,
396409 enableDiagnostics: diagnostics,
397- enableInspection: inspect)
410+ enableInspection: inspect,
411+ staticCorpus: staticCorpus)
398412
399413let fuzzer = makeFuzzer ( for: profile, with: config)
400414
@@ -464,31 +478,6 @@ fuzzer.sync {
464478 fuzzer. runStartupTests ( )
465479}
466480
467- // Add thread worker instances if requested
468- //
469- // This happens here, before any corpus is imported, so that any imported programs are
470- // forwarded to the ThreadWorkers automatically when they are deemed interesting.
471- //
472- // This must *not* happen on the main fuzzer's queue since workers perform synchronous
473- // operations on the master's dispatch queue.
474- var instances = [ fuzzer]
475- for _ in 1 ..< numJobs {
476- let worker = makeFuzzer ( for: profile, with: config)
477- instances. append ( worker)
478- let g = DispatchGroup ( )
479-
480- g. enter ( )
481- worker. sync {
482- worker. addModule ( Statistics ( ) )
483- worker. addModule ( ThreadWorker ( forMaster: fuzzer) )
484- worker. registerEventListener ( for: worker. events. Initialized) { g. leave ( ) }
485- worker. initialize ( )
486- }
487-
488- // Wait for the worker to be fully initialized
489- g. wait ( )
490- }
491-
492481// Import a corpus if requested and start the main fuzzer instance.
493482fuzzer. sync {
494483 func loadCorpus( from dirPath: String ) -> [ Program ] {
@@ -554,6 +543,28 @@ fuzzer.sync {
554543 }
555544}
556545
546+ // Add thread worker instances if requested
547+ //
548+ // This must *not* happen on the main fuzzer's queue since workers perform synchronous
549+ // operations on the master's dispatch queue.
550+ var instances = [ fuzzer]
551+ for _ in 1 ..< numJobs {
552+ let worker = makeFuzzer ( for: profile, with: config)
553+ instances. append ( worker)
554+ let g = DispatchGroup ( )
555+
556+ g. enter ( )
557+ worker. sync {
558+ worker. addModule ( Statistics ( ) )
559+ worker. addModule ( ThreadWorker ( forMaster: fuzzer) )
560+ worker. registerEventListener ( for: worker. events. Initialized) { g. leave ( ) }
561+ worker. initialize ( )
562+ }
563+
564+ // Wait for the worker to be fully initialized
565+ g. wait ( )
566+ }
567+
557568// Install signal handlers to terminate the fuzzer gracefully.
558569var signalSources : [ DispatchSourceSignal ] = [ ]
559570for sig in [ SIGINT, SIGTERM] {
0 commit comments