@@ -28,6 +28,12 @@ import (
2828// Corpus describes an archive of fuzzer-generated artifacts used to further fuzzing efforts. These artifacts are
2929// reusable across fuzzer runs. Changes to the fuzzer/chain configuration or definitions within smart contracts
3030// may create incompatibilities with corpus items.
31+ // warmupSequence tracks a stored corpus entry and whether it should rejoin the mutation chooser after replay.
32+ type warmupSequence struct {
33+ sequence calls.CallSequence
34+ useInMutations bool
35+ }
36+
3137type Corpus struct {
3238 // storageDirectory describes the directory to save corpus callSequenceFiles within.
3339 storageDirectory string
@@ -45,7 +51,7 @@ type Corpus struct {
4551 // unexecutedCallSequences defines the callSequences which have not yet been executed by the fuzzer. As each item
4652 // is selected for execution by the fuzzer on startup, it is removed. This way, all call sequences loaded from disk
4753 // are executed to check for test failures.
48- unexecutedCallSequences []calls. CallSequence
54+ unexecutedCallSequences []warmupSequence
4955
5056 // mutationTargetSequenceChooser is a provider that allows for weighted random selection of callSequences. If a
5157 // call sequence was not found to be compatible with this run, it is not added to the chooser.
@@ -68,7 +74,7 @@ func NewCorpus(corpusDirectory string) (*Corpus, error) {
6874 coverageMaps : coverage .NewCoverageMaps (),
6975 callSequenceFiles : newCorpusDirectory [calls.CallSequence ]("" ),
7076 testResultSequenceFiles : newCorpusDirectory [calls.CallSequence ]("" ),
71- unexecutedCallSequences : make ([]calls. CallSequence , 0 ),
77+ unexecutedCallSequences : make ([]warmupSequence , 0 ),
7278 logger : logging .GlobalLogger .NewSubLogger ("module" , "corpus" ),
7379 }
7480
@@ -511,17 +517,23 @@ func (c *Corpus) CheckSequenceCoverageAndUpdate(callSequence calls.CallSequence,
511517// MarkSequenceValidated records that a call sequence loaded from disk has been successfully replayed by a worker.
512518// The sequence is cloned, stripped of runtime metadata, and registered with the mutation chooser so it can participate
513519// in future mutations.
514- func (c * Corpus ) MarkSequenceValidated (sequence calls.CallSequence , mutationChooserWeight * big.Int ) error {
520+ func (c * Corpus ) MarkSequenceValidated (sequence calls.CallSequence , mutationChooserWeight * big.Int , useInMutations bool ) error {
515521 if mutationChooserWeight == nil {
516522 mutationChooserWeight = big .NewInt (1 )
517523 }
518524
525+ c .callSequencesLock .Lock ()
526+ defer c .callSequencesLock .Unlock ()
527+
528+ if ! useInMutations {
529+ return nil
530+ }
531+
519532 clonedSequence , err := sequence .Clone ()
520533 if err != nil {
521534 return err
522535 }
523536
524- // Strip runtime-only references that should not persist in the corpus chooser.
525537 for _ , element := range clonedSequence {
526538 if element == nil {
527539 continue
@@ -530,9 +542,6 @@ func (c *Corpus) MarkSequenceValidated(sequence calls.CallSequence, mutationChoo
530542 element .ExecutionTrace = nil
531543 }
532544
533- c .callSequencesLock .Lock ()
534- defer c .callSequencesLock .Unlock ()
535-
536545 if c .mutationTargetSequenceChooser == nil {
537546 c .mutationTargetSequenceChooser = randomutils .NewWeightedRandomChooser [calls.CallSequence ]()
538547 }
@@ -599,12 +608,18 @@ func (c *Corpus) PrepareForWarmup(baseTestChain *chain.TestChain, contractDefini
599608 totalSequences := len (c .callSequenceFiles .files ) + len (c .testResultSequenceFiles .files )
600609
601610 c .callSequencesLock .Lock ()
602- c .unexecutedCallSequences = make ([]calls. CallSequence , 0 , totalSequences )
611+ c .unexecutedCallSequences = make ([]warmupSequence , 0 , totalSequences )
603612 for _ , sequenceFileData := range c .testResultSequenceFiles .files {
604- c .unexecutedCallSequences = append (c .unexecutedCallSequences , sequenceFileData .data )
613+ c .unexecutedCallSequences = append (c .unexecutedCallSequences , warmupSequence {
614+ sequence : sequenceFileData .data ,
615+ useInMutations : false ,
616+ })
605617 }
606618 for _ , sequenceFileData := range c .callSequenceFiles .files {
607- c .unexecutedCallSequences = append (c .unexecutedCallSequences , sequenceFileData .data )
619+ c .unexecutedCallSequences = append (c .unexecutedCallSequences , warmupSequence {
620+ sequence : sequenceFileData .data ,
621+ useInMutations : true ,
622+ })
608623 }
609624 activeSequences := len (c .unexecutedCallSequences )
610625 c .callSequencesLock .Unlock ()
@@ -617,11 +632,11 @@ func (c *Corpus) PrepareForWarmup(baseTestChain *chain.TestChain, contractDefini
617632// failures. If a call sequence is returned, it will not be returned by this method again.
618633// Returns a call sequence loaded from disk which has not yet been executed, to check for test failures. If all
619634// sequences in the corpus have been executed, this will return nil.
620- func (c * Corpus ) UnexecutedCallSequence () * calls.CallSequence {
635+ func (c * Corpus ) UnexecutedCallSequence () ( * calls.CallSequence , bool ) {
621636 // Prior to thread locking, if we have no un-executed call sequences, quit.
622637 // This is a speed optimization, as thread locking on a central component affects performance.
623638 if len (c .unexecutedCallSequences ) == 0 {
624- return nil
639+ return nil , false
625640 }
626641
627642 // Acquire a thread lock for the duration of this method.
@@ -631,15 +646,15 @@ func (c *Corpus) UnexecutedCallSequence() *calls.CallSequence {
631646 // Check that we have an item now that the thread is locked. This must be performed again as an item could've
632647 // been removed between time of check (the prior exit condition) and time of use (thread locked operations).
633648 if len (c .unexecutedCallSequences ) == 0 {
634- return nil
649+ return nil , false
635650 }
636651
637652 // Otherwise obtain the first item and remove it from the slice.
638653 firstSequence := c .unexecutedCallSequences [0 ]
639654 c .unexecutedCallSequences = c .unexecutedCallSequences [1 :]
640655
641656 // Return the first sequence
642- return & firstSequence
657+ return & firstSequence . sequence , firstSequence . useInMutations
643658}
644659
645660// Flush writes corpus changes to disk. Returns an error if one occurs.
0 commit comments