From 1499037b01da86091743e5fa521eb3c00c03491f Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Fri, 26 Jul 2024 14:33:55 -0500 Subject: [PATCH 1/5] feat: try removing all reverting txs during shrinking --- fuzzing/fuzzer_worker.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/fuzzing/fuzzer_worker.go b/fuzzing/fuzzer_worker.go index 7ac958b2..72a3d757 100644 --- a/fuzzing/fuzzer_worker.go +++ b/fuzzing/fuzzer_worker.go @@ -13,6 +13,7 @@ import ( "github.com/crytic/medusa/utils" "github.com/crytic/medusa/utils/randomutils" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "golang.org/x/exp/maps" ) @@ -424,6 +425,31 @@ func (fw *FuzzerWorker) shrinkCallSequence(callSequence calls.CallSequence, shri return shrinkIteration >= shrinkLimit || utils.CheckContextDone(fw.fuzzer.ctx) } if shrinkLimit > 0 { + + // First, remove all reverting txs from the sequence. + var withoutReverts calls.CallSequence + for i := 0; i < len(optimizedSequence); i++ { + var err error + withoutReverts, err = optimizedSequence.Clone() + if err != nil { + return nil, err + } + lastCall := withoutReverts[i] + lastCallChainReference := lastCall.ChainReference + lastMessageResult := lastCallChainReference.Block.MessageResults[lastCallChainReference.TransactionIndex] + if lastMessageResult.Receipt.Status == types.ReceiptStatusFailed { + withoutReverts = append(withoutReverts[:i], withoutReverts[i+1:]...) + } + } + // Test the sequence with all reverts removed. + validShrunkSequence, err := fw.testShrunkenCallSequence(withoutReverts, shrinkRequest) + if err != nil { + return nil, err + } + if validShrunkSequence { + optimizedSequence = withoutReverts + } + // The first pass of shrinking is greedy towards trying to remove any unnecessary calls. // For each call in the sequence, the following removal strategies are used: // 1) Plain removal (lower block/time gap between surrounding blocks, maintain properties of max delay) From 2abb6190bc19f79131720d2809063cc9b5f2d82f Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Mon, 26 Aug 2024 12:58:48 -0500 Subject: [PATCH 2/5] feat: alternate shrinking and set prob. to 1 --- fuzzing/fuzzer.go | 2 +- fuzzing/fuzzer_worker.go | 75 ++++++++++++---------------------------- 2 files changed, 23 insertions(+), 54 deletions(-) diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index c28ac9d8..c95f6a09 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -585,7 +585,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, // TODO remove? } shrinkingValueMutator := valuegeneration.NewShrinkingValueMutator(shrinkingValueMutatorConfig, valueSet, randomProvider) return shrinkingValueMutator, nil diff --git a/fuzzing/fuzzer_worker.go b/fuzzing/fuzzer_worker.go index d0d8b0c7..383ab602 100644 --- a/fuzzing/fuzzer_worker.go +++ b/fuzzing/fuzzer_worker.go @@ -400,6 +400,22 @@ 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 + } + // Re-encode the message's calldata + (*callSequence)[i].Call.WithDataAbiValues(abiValuesMsgData) +} + +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. @@ -445,67 +461,20 @@ func (fw *FuzzerWorker) shrinkCallSequence(callSequence calls.CallSequence, shri optimizedSequence = withoutReverts } - // The first pass of shrinking is greedy towards trying to remove any unnecessary calls. - // For each call in the sequence, the following removal strategies are used: - // 1) Plain removal (lower block/time gap between surrounding blocks, maintain properties of max delay) - // 2) Add block/time delay to previous call (retain original block/time, possibly exceed max delays) - // At worst, this costs `2 * len(callSequence)` shrink iterations. fw.workerMetrics().shrinking = true - for removalStrategy := 0; removalStrategy < 2 && !shrinkingEnded(); removalStrategy++ { - for i := len(optimizedSequence) - 1; i >= 0 && !shrinkingEnded(); i-- { - // Recreate our current optimized sequence without the item at this index - possibleShrunkSequence, err := optimizedSequence.Clone() - removedCall := possibleShrunkSequence[i] - if err != nil { - return nil, err - } - possibleShrunkSequence = append(possibleShrunkSequence[:i], possibleShrunkSequence[i+1:]...) - - // Exercise the next removal strategy for this call. - if removalStrategy == 0 { - // Case 1: Plain removal. - } else if removalStrategy == 1 { - // Case 2: Add block/time delay to previous call. - if i > 0 { - possibleShrunkSequence[i-1].BlockNumberDelay += removedCall.BlockNumberDelay - possibleShrunkSequence[i-1].BlockTimestampDelay += removedCall.BlockTimestampDelay - } - } - - // Test the shrunken sequence. - validShrunkSequence, err := fw.testShrunkenCallSequence(possibleShrunkSequence, shrinkRequest) - shrinkIteration++ - if err != nil { - return nil, err - } - - // If the current sequence satisfied our conditions, set it as our optimized sequence. - if validShrunkSequence { - optimizedSequence = possibleShrunkSequence - } - } - } - - // The second pass of shrinking attempts to shrink values for each call in our call sequence. - // This is performed exhaustively in a round-robin fashion for each call, until the shrink limit is hit. for !shrinkingEnded() { for i := len(optimizedSequence) - 1; i >= 0 && !shrinkingEnded(); i-- { // 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 + // Alternate + coinToss := fw.randomProvider.Int() % 2 + if coinToss == 0 || len(possibleShrunkSequence) == 1 { + fw.shrinkParam(&possibleShrunkSequence) + } else { + fw.shorten(&possibleShrunkSequence) } - // Re-encode the message's calldata - possibleShrunkSequence[i].Call.WithDataAbiValues(abiValuesMsgData) - // Test the shrunken sequence. validShrunkSequence, err := fw.testShrunkenCallSequence(possibleShrunkSequence, shrinkRequest) shrinkIteration++ From 533b2fe3e61f27a3346e674ab12f3345bd33ded9 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Mon, 26 Aug 2024 13:05:03 -0500 Subject: [PATCH 3/5] fix loop condition --- fuzzing/fuzzer_worker.go | 45 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/fuzzing/fuzzer_worker.go b/fuzzing/fuzzer_worker.go index 2aba9251..3164f0a1 100644 --- a/fuzzing/fuzzer_worker.go +++ b/fuzzing/fuzzer_worker.go @@ -443,7 +443,7 @@ func (fw *FuzzerWorker) shrinkCallSequence(callSequence calls.CallSequence, shri // First, remove all reverting txs from the sequence. var withoutReverts calls.CallSequence - for i := 0; i < len(optimizedSequence); i++ { + for i := 0; i < len(optimizedSequence) && !shrinkingEnded(); i++ { var err error withoutReverts, err = optimizedSequence.Clone() if err != nil { @@ -468,30 +468,31 @@ func (fw *FuzzerWorker) shrinkCallSequence(callSequence calls.CallSequence, shri } for !shrinkingEnded() { - for i := len(optimizedSequence) - 1; i >= 0 && !shrinkingEnded(); i-- { - // Clone the optimized sequence. - possibleShrunkSequence, _ := optimizedSequence.Clone() - - // Alternate - 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) - shrinkIteration++ - if err != nil { - return nil, err - } + // Clone the optimized sequence. + possibleShrunkSequence, _ := optimizedSequence.Clone() - // If this current sequence satisfied our conditions, set it as our optimized sequence. - if validShrunkSequence { - optimizedSequence = possibleShrunkSequence - } + // Alternate + 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) + shrinkIteration++ + if err != nil { + return nil, err + } + + // If this current sequence satisfied our conditions, set it as our optimized sequence. + if validShrunkSequence { + optimizedSequence = possibleShrunkSequence } + + shrinkLimit-- } fw.workerMetrics().shrinking = false } From a0fa5ad8a4e9b4b8b3e0f5ec5f3f4f14b7e2d36d Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Tue, 27 Aug 2024 14:57:55 -0500 Subject: [PATCH 4/5] Update fuzzing/fuzzer_worker.go --- fuzzing/fuzzer_worker.go | 1 - 1 file changed, 1 deletion(-) diff --git a/fuzzing/fuzzer_worker.go b/fuzzing/fuzzer_worker.go index 3164f0a1..1bdf805a 100644 --- a/fuzzing/fuzzer_worker.go +++ b/fuzzing/fuzzer_worker.go @@ -455,7 +455,6 @@ func (fw *FuzzerWorker) shrinkCallSequence(callSequence calls.CallSequence, shri if lastMessageResult.Receipt.Status == types.ReceiptStatusFailed { withoutReverts = append(withoutReverts[:i], withoutReverts[i+1:]...) } - shrinkLimit-- } // Test the sequence with all reverts removed. validShrunkSequence, err := fw.testShrunkenCallSequence(withoutReverts, shrinkRequest) From 70797f1ec461c9a69e849a86323f079179d41765 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Sat, 31 Aug 2024 15:34:18 -0500 Subject: [PATCH 5/5] pass value --- fuzzing/fuzzer_worker.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fuzzing/fuzzer_worker.go b/fuzzing/fuzzer_worker.go index 1bdf805a..7fa09cbf 100644 --- a/fuzzing/fuzzer_worker.go +++ b/fuzzing/fuzzer_worker.go @@ -402,20 +402,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 +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 } // Re-encode the message's calldata - (*callSequence)[i].Call.WithDataAbiValues(abiValuesMsgData) + callSequence[i].Call.WithDataAbiValues(abiValuesMsgData) } -func (fw *FuzzerWorker) shorten(callSequence *calls.CallSequence) { - i := fw.randomProvider.Intn(len(*callSequence)) - *callSequence = append((*callSequence)[:i], (*callSequence)[i+1:]...) +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 @@ -474,9 +474,9 @@ func (fw *FuzzerWorker) shrinkCallSequence(callSequence calls.CallSequence, shri // Alternate coinToss := fw.randomProvider.Int() % 2 if coinToss == 0 || len(possibleShrunkSequence) == 1 { - fw.shrinkParam(&possibleShrunkSequence) + fw.shrinkParam(possibleShrunkSequence) } else { - fw.shorten(&possibleShrunkSequence) + fw.shorten(possibleShrunkSequence) } // Test the shrunken sequence.