Skip to content

WIP: Update generation, mutation, and shrinking of ABI values #571

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
33 changes: 16 additions & 17 deletions fuzzing/fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,36 +581,35 @@ func chainSetupFromCompilations(fuzzer *Fuzzer, testChain *chain.TestChain) (*ex
func defaultCallSequenceGeneratorConfigFunc(fuzzer *Fuzzer, valueSet *valuegeneration.ValueSet, randomProvider *rand.Rand) (*CallSequenceGeneratorConfig, error) {
// Create the value generator and mutator for the worker.
mutationalGeneratorConfig := &valuegeneration.MutationalValueGeneratorConfig{
MinMutationRounds: 0,
MaxMutationRounds: 1,
GenerateRandomAddressBias: 0.05,
GenerateRandomIntegerBias: 0.5,
GenerateRandomStringBias: 0.05,
GenerateRandomBytesBias: 0.05,
MutateAddressProbability: 0.1,
MinMutationRounds: 0,
MaxMutationRounds: 1,
// Echidna: Generate a random ABI value 40% of the time
GenerateRandomAddressBias: 0.4,
GenerateRandomIntegerBias: 0.4,
GenerateRandomStringBias: 0.4,
GenerateRandomBytesBias: 0.4,
MutateAddressProbability: 0.0,
MutateArrayStructureProbability: 0.1,
MutateBoolProbability: 0.1,
MutateBoolProbability: 1.0,
MutateBytesProbability: 0.1,
MutateBytesGenerateNewBias: 0.45,
MutateFixedBytesProbability: 0.1,
MutateStringProbability: 0.1,
MutateStringGenerateNewBias: 0.7,
MutateIntegerProbability: 0.1,
MutateIntegerGenerateNewBias: 0.5,
RandomValueGeneratorConfig: &valuegeneration.RandomValueGeneratorConfig{
GenerateRandomArrayMinSize: 0,
GenerateRandomArrayMaxSize: 100,
GenerateRandomBytesMinSize: 0,
GenerateRandomBytesMaxSize: 100,
GenerateRandomStringMinSize: 0,
GenerateRandomStringMaxSize: 100,
GenerateRandomArrayMinSize: 1,
GenerateRandomArrayMaxSize: 32,
GenerateRandomBytesMinSize: 1,
GenerateRandomBytesMaxSize: 32,
GenerateRandomStringMinSize: 1,
GenerateRandomStringMaxSize: 32,
},
}
mutationalGenerator := valuegeneration.NewMutationalValueGenerator(mutationalGeneratorConfig, valueSet, randomProvider)

// Create a sequence generator config which uses the created value generator.
sequenceGenConfig := &CallSequenceGeneratorConfig{
NewSequenceProbability: 0.3,
MutateCallSequenceElementProbability: 0.1,
RandomUnmodifiedCorpusHeadWeight: 800,
RandomUnmodifiedCorpusTailWeight: 100,
RandomUnmodifiedSpliceAtRandomWeight: 200,
Expand Down
31 changes: 24 additions & 7 deletions fuzzing/fuzzer_worker_sequence_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ type CallSequenceGeneratorConfig struct {
// sequence rather than mutating one from the corpus.
NewSequenceProbability float32

// MutateCallSequenceElementProbability defines the probability that the CallSequenceGenerator should mutate a call
// sequence element.
MutateCallSequenceElementProbability float32

// RandomUnmodifiedCorpusHeadWeight defines the weight that the CallSequenceGenerator should use the call sequence
// generation strategy of taking the head of a corpus sequence (without mutations) and append newly generated calls
// to the end of it.
Expand Down Expand Up @@ -467,15 +471,28 @@ func prefetchModifyCallFuncMutate(sequenceGenerator *CallSequenceGenerator, elem
return nil
}

// Loop for each input value and mutate it
// If our bias directs us to it, do not mutate the element at all
randomGeneratorDecision := sequenceGenerator.worker.randomProvider.Float32()
if randomGeneratorDecision > sequenceGenerator.config.MutateCallSequenceElementProbability {
return nil
}

// If this element has no input values, exit early.
if len(element.Call.DataAbiValues.InputValues) == 0 {
return nil
}

// Choose which input value to mutate
idx := sequenceGenerator.worker.randomProvider.Intn(len(element.Call.DataAbiValues.InputValues))

// Mutate selected input value and replace the value
abiValuesMsgData := element.Call.DataAbiValues
for i := 0; i < len(abiValuesMsgData.InputValues); i++ {
mutatedInput, err := valuegeneration.MutateAbiValue(sequenceGenerator.config.ValueGenerator, sequenceGenerator.config.ValueMutator, &abiValuesMsgData.Method.Inputs[i].Type, abiValuesMsgData.InputValues[i])
if err != nil {
return fmt.Errorf("error when mutating call sequence input argument: %v", err)
}
abiValuesMsgData.InputValues[i] = mutatedInput
mutatedInput, err := valuegeneration.MutateAbiValue(sequenceGenerator.config.ValueGenerator, sequenceGenerator.config.ValueMutator, &abiValuesMsgData.Method.Inputs[idx].Type, abiValuesMsgData.InputValues[idx])
if err != nil {
return fmt.Errorf("error when mutating call sequence input argument: %v", err)
}
abiValuesMsgData.InputValues[idx] = mutatedInput

// Re-encode the message's calldata
element.Call.WithDataAbiValues(abiValuesMsgData)

Expand Down
43 changes: 17 additions & 26 deletions fuzzing/valuegeneration/abi_values.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ package valuegeneration
import (
"encoding/hex"
"fmt"
"github.com/crytic/medusa/logging"
"github.com/crytic/medusa/utils"
"math/big"
"reflect"
"strconv"
"strings"

"github.com/crytic/medusa/logging"
"github.com/crytic/medusa/utils"

"github.com/crytic/medusa/utils/reflectionutils"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -211,41 +212,31 @@ func MutateAbiValue(generator ValueGenerator, mutator ValueMutator, inputType *a
// Note: We create a copy, as existing arrays may not be assignable.
array := reflectionutils.CopyReflectedType(reflect.ValueOf(value))

// Mutate our array structure first
mutatedValues := mutator.MutateArray(reflectionutils.GetReflectedArrayValues(array), true)
// Mutate our array structure
mutatedValues := mutator.MutateArray(reflectionutils.GetReflectedArrayValues(array), true, inputType.Elem)

// TODO: Make sure that we do not need to create a new copy of the array with some unit tests.

// Create a new array of the appropriate size
array = reflect.New(reflect.ArrayOf(array.Len(), array.Type().Elem())).Elem()
/*array = reflect.New(reflect.ArrayOf(array.Len(), array.Type().Elem())).Elem()

// Next mutate each element in the array.
// Next set each element in the new array.
for i := 0; i < array.Len(); i++ {
// Obtain the element's reflected value to access its getter/setters
reflectedElement := array.Index(i)

// If any item is nil, we generate a new element in its place instead. Otherwise, we mutate the existing value.
if mutatedValues[i] == nil {
generatedElement := GenerateAbiValue(generator, inputType.Elem)
reflectedElement.Set(reflect.ValueOf(generatedElement))
} else {
mutatedElement, err := MutateAbiValue(generator, mutator, inputType.Elem, mutatedValues[i])
if err != nil {
return nil, fmt.Errorf("could not mutate array input as the value generator encountered an error: %v", err)
}
reflectedElement.Set(reflect.ValueOf(mutatedElement))
}
}
array.Index(i).Set(reflect.ValueOf(mutatedValues[i]))
}*/

return array.Interface(), nil
return mutatedValues, nil
case abi.SliceTy:
// Dynamic sized arrays are represented as slices.
// Note: We create a copy, as existing slices may not be assignable.
slice := reflectionutils.CopyReflectedType(reflect.ValueOf(value))

// Mutate our slice structure first
mutatedValues := mutator.MutateArray(reflectionutils.GetReflectedArrayValues(slice), false)
mutatedValues := mutator.MutateArray(reflectionutils.GetReflectedArrayValues(slice), false, inputType.Elem)

// TODO: Same TODO as for arrays above.
// Create a new slice of the appropriate size
slice = reflect.MakeSlice(reflect.SliceOf(slice.Type().Elem()), len(mutatedValues), len(mutatedValues))
/*slice = reflect.MakeSlice(reflect.SliceOf(slice.Type().Elem()), len(mutatedValues), len(mutatedValues))

// Next mutate each element in the slice.
for i := 0; i < slice.Len(); i++ {
Expand All @@ -263,8 +254,8 @@ func MutateAbiValue(generator ValueGenerator, mutator ValueMutator, inputType *a
}
reflectedElement.Set(reflect.ValueOf(mutatedElement))
}
}
return slice.Interface(), nil
}*/
return mutatedValues, nil
case abi.TupleTy:
// Structs are used to represent tuples.
// Note: We create a copy, as existing tuples may not be assignable.
Expand Down
37 changes: 17 additions & 20 deletions fuzzing/valuegeneration/abi_values_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,27 +250,24 @@ func TestABIGenerationAndMutation(t *testing.T) {
mutationalGeneratorConfig := &MutationalValueGeneratorConfig{
MinMutationRounds: 0,
MaxMutationRounds: 1,
GenerateRandomAddressBias: 0.5,
GenerateRandomIntegerBias: 0.5,
GenerateRandomStringBias: 0.5,
GenerateRandomBytesBias: 0.5,
MutateAddressProbability: 0.8,
MutateArrayStructureProbability: 0.8,
MutateBoolProbability: 0.8,
MutateBytesProbability: 0.8,
MutateBytesGenerateNewBias: 0.45,
MutateFixedBytesProbability: 0.8,
MutateStringProbability: 0.8,
MutateStringGenerateNewBias: 0.7,
MutateIntegerProbability: 0.8,
MutateIntegerGenerateNewBias: 0.5,
GenerateRandomAddressBias: 0.4,
GenerateRandomIntegerBias: 0.4,
GenerateRandomStringBias: 0.4,
GenerateRandomBytesBias: 0.4,
MutateAddressProbability: 0.1,
MutateArrayStructureProbability: 0.1,
MutateBoolProbability: 0.1,
MutateBytesProbability: 0.1,
MutateFixedBytesProbability: 0.1,
MutateStringProbability: 0.1,
MutateIntegerProbability: 0.1,
RandomValueGeneratorConfig: &RandomValueGeneratorConfig{
GenerateRandomArrayMinSize: 0,
GenerateRandomArrayMaxSize: 100,
GenerateRandomBytesMinSize: 0,
GenerateRandomBytesMaxSize: 100,
GenerateRandomStringMinSize: 0,
GenerateRandomStringMaxSize: 100,
GenerateRandomArrayMinSize: 1,
GenerateRandomArrayMaxSize: 32,
GenerateRandomBytesMinSize: 1,
GenerateRandomBytesMaxSize: 32,
GenerateRandomStringMinSize: 1,
GenerateRandomStringMaxSize: 32,
},
}
mutationalGenerator := NewMutationalValueGenerator(mutationalGeneratorConfig, NewValueSet(), rand.New(rand.NewSource(time.Now().UnixNano())))
Expand Down
Loading
Loading