diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index 4686eba8..244f846d 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -470,7 +470,7 @@ func defaultCallSequenceGeneratorConfigFunc(fuzzer *Fuzzer, valueSet *valuegener func defaultShrinkingValueMutatorFunc(fuzzer *Fuzzer, valueSet *valuegeneration.ValueSet, randomProvider *rand.Rand) (valuegeneration.ValueMutator, error) { // Create the shrinking value mutator for the worker. shrinkingValueMutatorConfig := &valuegeneration.ShrinkingValueMutatorConfig{ - ShrinkValueProbability: 0.1, + ShrinkValueProbability: 1, } shrinkingValueMutator := valuegeneration.NewShrinkingValueMutator(shrinkingValueMutatorConfig, valueSet, randomProvider) return shrinkingValueMutator, nil diff --git a/fuzzing/fuzzer_worker.go b/fuzzing/fuzzer_worker.go index 27848a23..a18b052d 100644 --- a/fuzzing/fuzzer_worker.go +++ b/fuzzing/fuzzer_worker.go @@ -388,6 +388,20 @@ func (fw *FuzzerWorker) testShrunkenCallSequence(possibleShrunkSequence calls.Ca return validShrunkSequence, nil } +func (fw *FuzzerWorker) shrinkParam(callSequence *calls.CallSequence) { + i := fw.randomProvider.Intn(len(*callSequence)) + abiValuesMsgData := (*callSequence)[i].Call.DataAbiValues + for j := 0; j < len(abiValuesMsgData.InputValues); j++ { + mutatedInput, _ := valuegeneration.MutateAbiValue(fw.sequenceGenerator.config.ValueGenerator, fw.shrinkingValueMutator, &abiValuesMsgData.Method.Inputs[j].Type, abiValuesMsgData.InputValues[j]) + abiValuesMsgData.InputValues[j] = mutatedInput + } +} + +func (fw *FuzzerWorker) shorten(callSequence *calls.CallSequence) { + i := fw.randomProvider.Intn(len(*callSequence)) + *callSequence = append((*callSequence)[:i], (*callSequence)[i+1:]...) +} + // shrinkCallSequence takes a provided call sequence and attempts to shrink it by looking for redundant // calls which can be removed, and values which can be minimized, while continuing to satisfy the provided shrink // verifier. @@ -398,7 +412,7 @@ func (fw *FuzzerWorker) shrinkCallSequence(callSequence calls.CallSequence, shri optimizedSequence := callSequence // First try to remove any calls we can. We go from start to end to avoid index shifting. - for i := 0; i < len(optimizedSequence); { + for i := 0; i < 5000; i++ /* TODO add shrink limit config */ { // If our fuzzer context is done, exit out immediately without results. if utils.CheckContextDone(fw.fuzzer.ctx) { return nil, nil @@ -409,55 +423,23 @@ func (fw *FuzzerWorker) shrinkCallSequence(callSequence calls.CallSequence, shri if err != nil { return nil, err } - possibleShrunkSequence = append(possibleShrunkSequence[:i], possibleShrunkSequence[i+1:]...) + coinToss := fw.randomProvider.Int() % 2 + if coinToss == 0 || len(possibleShrunkSequence) == 1 { + fw.shrinkParam(&possibleShrunkSequence) + } else { + fw.shorten(&possibleShrunkSequence) + } // Test the shrunken sequence. validShrunkSequence, err := fw.testShrunkenCallSequence(possibleShrunkSequence, shrinkRequest) if err != nil { return nil, err } - // If this current sequence satisfied our conditions, set it as our optimized sequence. if validShrunkSequence { optimizedSequence = possibleShrunkSequence - } else { - // We didn't remove an item at this index, so we'll iterate to the next one. - i++ } - } - // Next try to shrink our values of every transaction a given number of rounds. - for i := 0; i < len(optimizedSequence); i++ { - for optimizationRound := 0; optimizationRound < 200; optimizationRound++ { - // If our fuzzer context is done, exit out immediately without results. - if utils.CheckContextDone(fw.fuzzer.ctx) { - return nil, nil - } - - // Clone the optimized sequence. - possibleShrunkSequence, _ := optimizedSequence.Clone() - - // Loop for each argument in the currently indexed call to mutate it. - abiValuesMsgData := possibleShrunkSequence[i].Call.DataAbiValues - for j := 0; j < len(abiValuesMsgData.InputValues); j++ { - mutatedInput, err := valuegeneration.MutateAbiValue(fw.sequenceGenerator.config.ValueGenerator, fw.shrinkingValueMutator, &abiValuesMsgData.Method.Inputs[j].Type, abiValuesMsgData.InputValues[j]) - if err != nil { - return nil, fmt.Errorf("error when shrinking call sequence input argument: %v", err) - } - abiValuesMsgData.InputValues[j] = mutatedInput - } - - // Test the shrunken sequence. - validShrunkSequence, err := fw.testShrunkenCallSequence(possibleShrunkSequence, shrinkRequest) - if err != nil { - return nil, err - } - - // If this current sequence satisfied our conditions, set it as our optimized sequence. - if validShrunkSequence { - optimizedSequence = possibleShrunkSequence - } - } } // If the shrink request wanted the sequence recorded in the corpus, do so now. diff --git a/fuzzing/valuegeneration/mutator_shrinking.go b/fuzzing/valuegeneration/mutator_shrinking.go index 20b7ff49..75df8546 100644 --- a/fuzzing/valuegeneration/mutator_shrinking.go +++ b/fuzzing/valuegeneration/mutator_shrinking.go @@ -1,10 +1,11 @@ package valuegeneration import ( - "github.com/crytic/medusa/utils" - "github.com/ethereum/go-ethereum/common" "math/big" "math/rand" + + "github.com/crytic/medusa/utils" + "github.com/ethereum/go-ethereum/common" ) // ShrinkingValueMutator represents a ValueMutator used to shrink function inputs and call arguments. @@ -96,7 +97,9 @@ func (g *ShrinkingValueMutator) MutateBytes(b []byte) []byte { randomGeneratorDecision := g.randomProvider.Float32() if randomGeneratorDecision < g.config.ShrinkValueProbability { // Mutate the data for our desired number of rounds + input := bytesShrinkingMethods[g.randomProvider.Intn(len(bytesShrinkingMethods))](g, b) + return input } return b @@ -182,6 +185,10 @@ var stringShrinkingMethods = []func(*ShrinkingValueMutator, string) string{ i := g.randomProvider.Intn(len(s)) return s[:i] + s[i+1:] }, + // ??? + func(g *ShrinkingValueMutator, s string) string { + return "" + }, } // MutateString takes a string input and returns a mutated value based off the input.