Skip to content

Commit

Permalink
feat: implementation of a power schedule based on genetic algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandreps1123 committed Apr 16, 2024
1 parent 3f4d80a commit 78314b8
Show file tree
Hide file tree
Showing 7 changed files with 273 additions and 45 deletions.
21 changes: 15 additions & 6 deletions cmd/dogefuzz/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type Env interface {
DirectedGreyboxFuzzer() interfaces.Fuzzer
GeneticAlgorithmFuzzer() interfaces.Fuzzer
PowerSchedule() interfaces.PowerSchedule
GeneticAlgorithmPowerSchedule() interfaces.GeneticAlgorithmPowerSchedule
}

type env struct {
Expand Down Expand Up @@ -129,12 +130,13 @@ type env struct {
transactionsCheckerJob interfaces.CronJob
transactionsTimeoutCheckerJob interfaces.CronJob

fuzzerLeader interfaces.FuzzerLeader
blackboxFuzzer interfaces.Fuzzer
greyboxFuzzer interfaces.Fuzzer
directedGreyboxFuzzer interfaces.Fuzzer
geneticAlgorithmFuzzer interfaces.Fuzzer
powerSchedule interfaces.PowerSchedule
fuzzerLeader interfaces.FuzzerLeader
blackboxFuzzer interfaces.Fuzzer
greyboxFuzzer interfaces.Fuzzer
directedGreyboxFuzzer interfaces.Fuzzer
geneticAlgorithmFuzzer interfaces.Fuzzer
powerSchedule interfaces.PowerSchedule
geneticAlgorithmPowerSchedule interfaces.GeneticAlgorithmPowerSchedule
}

func NewEnv(cfg *config.Config) *env {
Expand Down Expand Up @@ -493,6 +495,13 @@ func (e *env) PowerSchedule() interfaces.PowerSchedule {
return e.powerSchedule
}

func (e *env) GeneticAlgorithmPowerSchedule() interfaces.GeneticAlgorithmPowerSchedule {
if e.geneticAlgorithmPowerSchedule == nil {
e.geneticAlgorithmPowerSchedule = fuzz.NewGeneticAlgorithmPowerSchedule(e)
}
return e.geneticAlgorithmPowerSchedule
}

func initLogger() (*zap.Logger, error) {
// rawJSON := []byte(`{
// "level": "debug",
Expand Down
1 change: 1 addition & 0 deletions fuzz/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type env interface {
DirectedGreyboxFuzzer() interfaces.Fuzzer
GeneticAlgorithmFuzzer() interfaces.Fuzzer
PowerSchedule() interfaces.PowerSchedule
GeneticAlgorithmPowerSchedule() interfaces.GeneticAlgorithmPowerSchedule

TransactionService() interfaces.TransactionService
SolidityService() interfaces.SolidityService
Expand Down
221 changes: 221 additions & 0 deletions fuzz/ga_power_schedule.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
package fuzz

import (
"math"
"strings"

"github.com/dogefuzz/dogefuzz/config"
"github.com/dogefuzz/dogefuzz/pkg/common"
"github.com/dogefuzz/dogefuzz/pkg/interfaces"
"github.com/ethereum/go-ethereum/accounts/abi"
)

type geneticAlgorithmPowerSchedule struct {
cfg *config.Config
transactionService interfaces.TransactionService
solidityService interfaces.SolidityService
functionService interfaces.FunctionService
contractService interfaces.ContractService
}

func NewGeneticAlgorithmPowerSchedule(e env) *geneticAlgorithmPowerSchedule {
return &geneticAlgorithmPowerSchedule{
cfg: e.Config(),
transactionService: e.TransactionService(),
solidityService: e.SolidityService(),
functionService: e.FunctionService(),
contractService: e.ContractService(),
}
}

func (s *geneticAlgorithmPowerSchedule) RequestSeeds(functionId string, strategy common.PowerScheduleStrategy) ([][]interface{}, error) {
function, err := s.functionService.Get(functionId)
if err != nil {
return nil, err
}

contract, err := s.contractService.Get(function.ContractId)
if err != nil {
return nil, err
}

abiDefinition, err := abi.JSON(strings.NewReader(contract.AbiDefinition))
if err != nil {
return nil, err
}
method := abiDefinition.Methods[function.Name]

transactions, err := s.transactionService.FindDoneTransactionsByFunctionIdAndOrderByTimestamp(functionId, int64(s.cfg.FuzzerConfig.SeedsSize)*2)
if err != nil {
return nil, err
}

orderer := buildOrderer(strategy, contract)
orderer.OrderTransactions(transactions)

seeds := make([][]string, 0)
// it will take all seeds
for idx := 0; idx < len(transactions); idx++ {
seeds = append(seeds, transactions[idx].Inputs)
}

// select seeds to crossover
var selectedSeeds [][]string
for range seeds {
selectedSeeds = append(selectedSeeds, rouletteWheelSelection(seeds))
}

// do crossover
var crossoverSeeds [][]string
if len(selectedSeeds) > 2 {
for i := 0; i < s.cfg.FuzzerConfig.SeedsSize*2; i++ {
seed1, seed2 := crossover(chooseSeedsToCrossover(selectedSeeds))
crossoverSeeds = append(crossoverSeeds, seed1)
crossoverSeeds = append(crossoverSeeds, seed2)
}
}

deserializedSeeds, err := s.deserializeSeedsList(method, crossoverSeeds)
if err != nil {
return nil, err
}

if len(crossoverSeeds) < s.cfg.FuzzerConfig.SeedsSize {
deserializedSeeds, err = s.completeSeedsWithPreConfiguredSeeds(method, deserializedSeeds, s.cfg.FuzzerConfig.SeedsSize-len(crossoverSeeds))
if err != nil {
return nil, err
}
}

return deserializedSeeds, nil
}

func (s *geneticAlgorithmPowerSchedule) completeSeedsWithPreConfiguredSeeds(method abi.Method, seeds [][]interface{}, seedsAmountToBeAdded int) ([][]interface{}, error) {
result := make([][]interface{}, len(seeds)+seedsAmountToBeAdded)
copy(result, seeds)
for icr := 0; icr < int(seedsAmountToBeAdded); icr++ {
functionSeeds := make([]interface{}, len(method.Inputs))
for inputsIdx, input := range method.Inputs {
handler, err := s.solidityService.GetTypeHandlerWithContext(input.Type)
if err != nil {
return nil, err
}

err = handler.LoadSeedsAndChooseOneRandomly(s.cfg.FuzzerConfig.Seeds)
if err != nil {
return nil, err
}

functionSeeds[inputsIdx] = handler.GetValue()
}
result[icr+len(seeds)] = functionSeeds
}
return result, nil
}

func (s *geneticAlgorithmPowerSchedule) deserializeSeedsList(method abi.Method, seedsList [][]string) ([][]interface{}, error) {
result := make([][]interface{}, len(seedsList))
for seedsListIdx, seeds := range seedsList {
deserializedSeeds := make([]interface{}, len(seeds))
for inputsIdx, inputDefinition := range method.Inputs {
if len(seeds) <= inputsIdx {
return nil, ErrSeedsListInvalid
}

handler, err := s.solidityService.GetTypeHandlerWithContext(inputDefinition.Type)
if err != nil {
return nil, err
}

err = handler.Deserialize(seeds[inputsIdx])
if err != nil {
return nil, err
}
deserializedSeeds[inputsIdx] = handler.GetValue()
}
result[seedsListIdx] = deserializedSeeds
}
return result, nil
}

func rouletteWheelSelection(seedsList [][]string) []string {
rnd := common.RandomFloat64()
lengthList := len(seedsList)
var seedsSlice [][]string

if rnd >= 0 && rnd < FIRST_INTERVAL {
seedsSlice = seedsList[0:maxIndex(lengthList, FIRST_RANGE)]
} else if rnd >= FIRST_INTERVAL && rnd < SECOND_INTERVAL {
seedsSlice = seedsList[minIndex(lengthList, FIRST_RANGE):maxIndex(lengthList, SECOND_RANGE)]
} else {
// [minIndex:len(seedsList)]
seedsSlice = seedsList[minIndex(lengthList, SECOND_RANGE):]
}

return common.RandomChoice(seedsSlice)
}

func chooseSeedsToCrossover(seedsList [][]string) ([]string, []string) {
return common.RandomChoice(seedsList), common.RandomChoice(seedsList)
}

// metodos a serem testados podem ter mais de um parametro de entrada
// uma seed representa uma lista de paramentros de entrada
func crossover(seed1, seed2 []string) ([]string, []string) {
var crossoverSeed1 []string
var crossoverSeed2 []string

if len(seed1) == len(seed2) {
for i := range seed1 {
smallest := smallestSize(seed1[i], seed2[i])
// analisar a necessidade desta verificacao
if smallest == 0 {
crossoverSeed1 = seed1
crossoverSeed2 = seed2
// funcionando como esperado
} else if smallest == 1 {
var tempSeed1 string
var tempSeed2 string
for j := 0; j < smallest; j++ {
tempSeed1 = seed1[i][j:j+1] + seed2[i][j:j+1]
tempSeed2 = seed2[i][j:j+1] + seed1[i][j:j+1]
}
crossoverSeed1 = append(crossoverSeed1, tempSeed1)
crossoverSeed2 = append(crossoverSeed2, tempSeed2)
} else {
var tempSeed1 string
var tempSeed2 string
for j := 0; j < smallest; j++ {

if j%2 == 0 {
tempSeed1 += seed2[i][j : j+1]
tempSeed2 += seed1[i][j : j+1]
} else {
tempSeed1 += seed1[i][j : j+1]
tempSeed2 += seed2[i][j : j+1]
}
}
crossoverSeed1 = append(crossoverSeed1, tempSeed1)
crossoverSeed2 = append(crossoverSeed2, tempSeed2)
}
}
}

return crossoverSeed1, crossoverSeed2
}

func smallestSize(seed1, seed2 string) int {
if len(seed1) >= len(seed2) {
return len(seed2)
}

return len(seed1)
}

func maxIndex(lengthList int, factor float64) int {
return int(math.Ceil(float64(lengthList) * factor))
}

func minIndex(lengthList int, factor float64) int {
return int(math.Floor(float64(lengthList) * factor))
}
61 changes: 22 additions & 39 deletions fuzz/genetic_algorithm.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package fuzz

import (
"math/rand"
"strings"
"time"

"github.com/dogefuzz/dogefuzz/pkg/common"
"github.com/dogefuzz/dogefuzz/pkg/interfaces"
"github.com/ethereum/go-ethereum/accounts/abi"
)

const MUTATION_CHANCE float64 = 0.1

// consts to define what interval of the seeds slice will selected
const FIRST_INTERVAL float64 = 0.6
const SECOND_INTERVAL float64 = 0.8
Expand All @@ -19,18 +19,18 @@ const FIRST_RANGE float64 = 0.4
const SECOND_RANGE float64 = 0.7

type geneticAlgorithmFuzzer struct {
powerSchedule interfaces.PowerSchedule
solidityService interfaces.SolidityService
functionService interfaces.FunctionService
contractService interfaces.ContractService
geneticAlgorithmPowerSchedule interfaces.GeneticAlgorithmPowerSchedule
solidityService interfaces.SolidityService
functionService interfaces.FunctionService
contractService interfaces.ContractService
}

func NewGeneticAlgorithmFuzzer(e env) *geneticAlgorithmFuzzer {
return &geneticAlgorithmFuzzer{
powerSchedule: e.PowerSchedule(),
solidityService: e.SolidityService(),
functionService: e.FunctionService(),
contractService: e.ContractService(),
geneticAlgorithmPowerSchedule: e.GeneticAlgorithmPowerSchedule(),
solidityService: e.SolidityService(),
functionService: e.FunctionService(),
contractService: e.ContractService(),
}
}

Expand All @@ -51,49 +51,32 @@ func (f *geneticAlgorithmFuzzer) GenerateInput(functionId string) ([]interface{}
}
method := abiDefinition.Methods[function.Name]

seedsList, err := f.powerSchedule.RequestSeeds(functionId, common.DISTANCE_COVERAGE_BASED_STRATEGY)
// evaluate seeds - order a list by strategy
seedsList, err := f.geneticAlgorithmPowerSchedule.RequestSeeds(functionId, common.DISTANCE_COVERAGE_BASED_STRATEGY)
if err != nil {
return nil, err
}

var newSeedsList [][]interface{}
for range seedsList {
newSeedsList = append(newSeedsList, rouletteWheelSelection(seedsList))
}

chosenSeeds := common.RandomChoice(newSeedsList)
chosenSeeds := common.RandomChoice(seedsList)

inputs := make([]interface{}, len(method.Inputs))
for inputsIdx, inputDefinition := range method.Inputs {
rnd := common.RandomFloat64()

handler, err := f.solidityService.GetTypeHandlerWithContext(inputDefinition.Type)
if err != nil {
return nil, err
}
handler.SetValue(chosenSeeds[inputsIdx])
mutationFunction := common.RandomChoice(handler.GetMutators())
mutationFunction()
inputs[inputsIdx] = handler.GetValue()
}

return inputs, nil
}

func rouletteWheelSelection(seedsList [][]interface{}) []interface{} {
rand.Seed(time.Now().UnixNano())
rnd := rand.Float64()

if rnd >= 0 || rnd < FIRST_INTERVAL {
slice := seedsList[0:int(float64(len(seedsList))*FIRST_RANGE)]

return common.RandomChoice(slice)
} else if rnd >= FIRST_INTERVAL || rnd < SECOND_INTERVAL {
slice := seedsList[int(float64(len(seedsList))*FIRST_RANGE):int(float64(len(seedsList))*SECOND_RANGE)]
handler.SetValue(chosenSeeds[inputsIdx])

return common.RandomChoice(slice)
} else {
slice := seedsList[int(float64(len(seedsList))*SECOND_RANGE):]
if rnd >= 0 && rnd < MUTATION_CHANCE {
mutationFunction := common.RandomChoice(handler.GetMutators())
mutationFunction()
}

return common.RandomChoice(slice)
inputs[inputsIdx] = handler.GetValue()
}

return inputs, nil
}
1 change: 1 addition & 0 deletions listener/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ type Env interface {
DirectedGreyboxFuzzer() interfaces.Fuzzer
GeneticAlgorithmFuzzer() interfaces.Fuzzer
PowerSchedule() interfaces.PowerSchedule
GeneticAlgorithmPowerSchedule() interfaces.GeneticAlgorithmPowerSchedule
}
9 changes: 9 additions & 0 deletions pkg/common/random.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,12 @@ func RandomChoice[T any](slice []T) T {
rndIdx := rand.Intn(len(slice))
return slice[rndIdx]
}

func RandomFloat64() float64 {
rand.Seed(time.Now().UnixNano())
return rand.Float64()
}

func RandomInt(number int) int {
return rand.Intn(number)
}
Loading

0 comments on commit 78314b8

Please sign in to comment.