@@ -3,11 +3,11 @@ package fuzzing
33import (
44 "fmt"
55 "math/big"
6+ "math/rand"
67
78 "github.com/crytic/medusa/fuzzing/calls"
89 "github.com/crytic/medusa/fuzzing/contracts"
910 "github.com/crytic/medusa/fuzzing/valuegeneration"
10- "github.com/crytic/medusa/utils"
1111 "github.com/crytic/medusa/utils/randomutils"
1212)
1313
@@ -186,6 +186,18 @@ func NewCallSequenceGenerator(worker *FuzzerWorker, config *CallSequenceGenerato
186186 },
187187 new (big.Int ).SetUint64 (config .RandomMutatedInterleaveAtRandomWeight ),
188188 ),
189+ randomutils .NewWeightedRandomChoice (CallSequenceGeneratorMutationStrategy {
190+ CallSequenceGeneratorFunc : callSeqSwapRandomElement ,
191+ PrefetchModifyCallFunc : nil ,
192+ },
193+ new (big.Int ).SetUint64 (config .RandomMutatedInterleaveAtRandomWeight ),
194+ ),
195+ randomutils .NewWeightedRandomChoice (CallSequenceGeneratorMutationStrategy {
196+ CallSequenceGeneratorFunc : callSeqDeleteRandomElement ,
197+ PrefetchModifyCallFunc : nil ,
198+ },
199+ new (big.Int ).SetUint64 (config .RandomMutatedInterleaveAtRandomWeight ),
200+ ),
189201 )
190202
191203 return generator
@@ -352,6 +364,24 @@ func (g *CallSequenceGenerator) generateNewElement() (*calls.CallSequenceElement
352364 return calls .NewCallSequenceElement (selectedMethod .Contract , msg , blockNumberDelay , blockTimestampDelay ), nil
353365}
354366
367+ func callSeqSwapRandomElement (sequenceGenerator * CallSequenceGenerator , sequence calls.CallSequence ) error {
368+ // Swap the element
369+ swappedSequence := swapRandList (sequence )
370+
371+ copy (sequence , swappedSequence )
372+
373+ return nil
374+ }
375+
376+ func callSeqDeleteRandomElement (sequenceGenerator * CallSequenceGenerator , sequence calls.CallSequence ) error {
377+ // Delete the element
378+ deletedSequence := deleteRandList (sequence )
379+
380+ copy (sequence , deletedSequence )
381+
382+ return nil
383+ }
384+
355385// callSeqGenFuncCorpusHead is a CallSequenceGeneratorFunc which prepares a CallSequenceGenerator to generate a sequence
356386// whose head is based off of an existing corpus call sequence.
357387// Returns an error if one occurs.
@@ -362,9 +392,10 @@ func callSeqGenFuncCorpusHead(sequenceGenerator *CallSequenceGenerator, sequence
362392 return fmt .Errorf ("could not obtain corpus call sequence for head mutation: %v" , err )
363393 }
364394
365- // Determine the length of the slice to be copied in the head.
366- maxLength := utils .Min (len (sequence ), len (corpusSequence ))
367- copy (sequence , corpusSequence [:maxLength ])
395+ // Append the new calls to the end of the corpus sequence
396+ spliced := append (corpusSequence , sequence ... )
397+
398+ copy (sequence , spliced )
368399
369400 return nil
370401}
@@ -379,10 +410,10 @@ func callSeqGenFuncCorpusTail(sequenceGenerator *CallSequenceGenerator, sequence
379410 return fmt .Errorf ("could not obtain corpus call sequence for tail mutation: %v" , err )
380411 }
381412
382- // Determine a random position to slice the call sequence.
383- maxLength := utils . Min ( len ( sequence ), len ( corpusSequence ) )
384- targetLength := sequenceGenerator . worker . randomProvider . Intn ( maxLength ) + 1
385- copy (sequence [ len ( sequence ) - targetLength :], corpusSequence [ len ( corpusSequence ) - targetLength :] )
413+ // Prepend the new calls to the start of the corpus sequence
414+ spliced := append ( sequence , corpusSequence ... )
415+
416+ copy (sequence , spliced )
386417
387418 return nil
388419}
@@ -394,19 +425,12 @@ func callSeqGenFuncExpansion(sequenceGenerator *CallSequenceGenerator, sequence
394425
395426 // Get item to expand
396427 randIndex := sequenceGenerator .worker .randomProvider .Intn (len (sequence ))
397- duplicatedElement := sequence [randIndex ]
398-
399- // Perform N rounds of expansion
400- for i := 0 ; i < rounds ; i ++ {
401- randIndex += i
402- if randIndex < len (sequence ) {
403- // Insert
404- sequence = append (sequence [:randIndex ], append ([]* calls.CallSequenceElement {duplicatedElement }, sequence [randIndex :]... )... )
405- } else {
406- // Extend
407- sequence = append (sequence , duplicatedElement )
408- }
409- }
428+
429+ // Expand the sequence
430+ expandedSequence := expandAt (sequence , randIndex , rounds )
431+
432+ copy (sequence , expandedSequence )
433+
410434 return nil
411435}
412436
@@ -425,19 +449,10 @@ func callSeqGenFuncSpliceAtRandom(sequenceGenerator *CallSequenceGenerator, sequ
425449 return fmt .Errorf ("could not obtain tail corpus call sequence for splice-at-random corpus mutation: %v" , err )
426450 }
427451
428- // Determine a random position to slice off the head of the call sequence.
429- maxLength := utils .Min (len (sequence ), len (headSequence ))
430- headSequenceLength := sequenceGenerator .worker .randomProvider .Intn (maxLength ) + 1
431-
432- // Copy the head of the first corpus sequence to our destination sequence.
433- copy (sequence , headSequence [:headSequenceLength ])
434-
435- // Determine a random position to slice off the tail of the call sequence.
436- maxLength = utils .Min (len (sequence )- headSequenceLength , len (tailSequence ))
437- tailSequenceLength := sequenceGenerator .worker .randomProvider .Intn (maxLength + 1 )
452+ // Splice the two sequences
453+ splicedSequence := spliceAtRandom (headSequence , tailSequence )
438454
439- // Copy the tail of the second corpus sequence to our destination sequence (after the head sequence portion).
440- copy (sequence [headSequenceLength :], tailSequence [len (tailSequence )- tailSequenceLength :])
455+ copy (sequence , splicedSequence )
441456
442457 return nil
443458}
@@ -457,30 +472,11 @@ func callSeqGenFuncInterleaveAtRandom(sequenceGenerator *CallSequenceGenerator,
457472 return fmt .Errorf ("could not obtain second corpus call sequence for interleave-at-random corpus mutation: %v" , err )
458473 }
459474
460- // Determine how many transactions to take from the first sequence and slice it.
461- maxLength := utils .Min (len (sequence ), len (firstSequence ))
462- firstSequenceLength := sequenceGenerator .worker .randomProvider .Intn (maxLength ) + 1
463- firstSequence = firstSequence [:firstSequenceLength ]
464-
465- // Determine how many transactions to take from the second sequence and slice it.
466- maxLength = utils .Min (len (sequence )- firstSequenceLength , len (secondSequence ))
467- secondSequenceLength := sequenceGenerator .worker .randomProvider .Intn (maxLength + 1 )
468- secondSequence = secondSequence [:secondSequenceLength ]
469-
470- // Now that we have both sequences, and we know they will not exceed our destination sequence length, interleave
471- // them.
472- destIndex := 0
473- largestSequenceSize := utils .Max (firstSequenceLength , secondSequenceLength )
474- for i := 0 ; i < largestSequenceSize ; i ++ {
475- if i < len (firstSequence ) {
476- sequence [destIndex ] = firstSequence [i ]
477- destIndex ++
478- }
479- if i < len (secondSequence ) {
480- sequence [destIndex ] = secondSequence [i ]
481- destIndex ++
482- }
483- }
475+ // Interleave the two sequences
476+ interleavedSequence := interleaveLL (firstSequence , secondSequence )
477+
478+ copy (sequence , interleavedSequence )
479+
484480 return nil
485481}
486482
@@ -507,3 +503,104 @@ func prefetchModifyCallFuncMutate(sequenceGenerator *CallSequenceGenerator, elem
507503
508504 return nil
509505}
506+
507+ // replaceAt replaces the element at the given index with the provided value.
508+ func replaceAt [T any ](xs []T , i T , n int ) []T {
509+ return append (append (xs [:n ], i ), xs [n + 1 :]... )
510+ }
511+
512+ // expandAt expands the element at index k by t times.
513+ func expandAt [T any ](xs []T , k int , t int ) []T {
514+ if len (xs ) == 0 {
515+ return xs
516+ }
517+ return append (append (xs [:k ], repeat (xs [k ], t )... ), xs [k + 1 :]... )
518+ }
519+
520+ // repeat replicates an element t times.
521+ func repeat [T any ](v T , t int ) []T {
522+ res := make ([]T , t )
523+ for i := range res {
524+ res [i ] = v
525+ }
526+ return res
527+ }
528+
529+ // expandRandList expands a random element of the list by 1 to 32 times.
530+ func expandRandList [T any ](xs []T ) []T {
531+ l := len (xs )
532+ if l == 0 || l >= 32 {
533+ return xs
534+ }
535+ k := rand .Intn (l )
536+ t := rand .Intn (min (32 , l )) + 1
537+ return expandAt (xs , k , t )
538+ }
539+
540+ // deleteAt deletes the element at the given index.
541+ func deleteAt [T any ](xs []T , n int ) []T {
542+ return append (xs [:n ], xs [n + 1 :]... )
543+ }
544+
545+ // deleteRandList deletes a random element from the list.
546+ func deleteRandList [T any ](xs []T ) []T {
547+ if len (xs ) == 0 {
548+ return xs
549+ }
550+ k := rand .Intn (len (xs ))
551+ return deleteAt (xs , k )
552+ }
553+
554+ // swapAt swaps two elements in the list.
555+ func swapAt [T any ](xs []T , i , j int ) []T {
556+ xs [i ], xs [j ] = xs [j ], xs [i ]
557+ return xs
558+ }
559+
560+ // swapRandList swaps two random elements in the list.
561+ func swapRandList [T any ](xs []T ) []T {
562+ if len (xs ) == 0 {
563+ return xs
564+ }
565+ i , j := rand .Intn (len (xs )), rand .Intn (len (xs ))
566+ return swapAt (xs , min (i , j ), max (i , j ))
567+ }
568+
569+ // spliceAtRandom splices two lists at random positions.
570+ func spliceAtRandom [T any ](xs1 , xs2 []T ) []T {
571+ idx1 , idx2 := rand .Intn (len (xs1 )), rand .Intn (len (xs2 ))
572+ return append (xs1 [:idx1 ], xs2 [idx2 :]... )
573+ }
574+
575+ // interleaveAtRandom interleaves two lists at random positions.
576+ func interleaveAtRandom [T any ](xs1 , xs2 []T ) []T {
577+ idx1 , idx2 := rand .Intn (len (xs1 )), rand .Intn (len (xs2 ))
578+ return interleaveLL (xs1 [:idx1 ], xs2 [:idx2 ])
579+ }
580+
581+ // interleaveLL interleaves two lists.
582+ func interleaveLL [T any ](a , b []T ) []T {
583+ if len (a ) == 0 {
584+ return b
585+ }
586+ if len (b ) == 0 {
587+ return a
588+ }
589+ return append ([]T {a [0 ], b [0 ]}, interleaveLL (a [1 :], b [1 :])... )
590+ }
591+
592+ // min returns the smaller of two integers.
593+ func min (a , b int ) int {
594+ if a < b {
595+ return a
596+ }
597+ return b
598+ }
599+
600+ // max returns the larger of two integers.
601+ func max (a , b int ) int {
602+ if a > b {
603+ return a
604+ }
605+ return b
606+ }
0 commit comments