Skip to content

Commit

Permalink
EOF Differential Layout Fuzzer (hyperledger#7488)
Browse files Browse the repository at this point in the history
Differential EOF Layout Fuzzer guided by Besu's layout parser.

Signed-off-by: Danno Ferrin <[email protected]>
  • Loading branch information
shemnon authored Aug 21, 2024
1 parent 0ec335f commit 1598e6b
Show file tree
Hide file tree
Showing 23 changed files with 1,351 additions and 26 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ allprojects {
url 'https://splunk.jfrog.io/splunk/ext-releases-local'
content { includeGroupByRegex('com\\.splunk\\..*') }
}
maven {
url 'https://gitlab.com/api/v4/projects/19871573/packages/maven'
content { includeGroupByRegex('com\\.gitlab\\.javafuzz(\\..*)?') }
}

mavenCentral()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
Expand Down Expand Up @@ -175,12 +173,8 @@ public String considerCode(final String hexCode) {
((CodeV1) code).getEofLayout().containerMode().get())) {
return "err: code is valid initcode. Runtime code expected";
} else {
return "OK "
+ IntStream.range(0, code.getCodeSectionCount())
.mapToObj(code::getCodeSection)
.map(cs -> code.getBytes().slice(cs.getEntryPoint(), cs.getLength()))
.map(Bytes::toUnprefixedHexString)
.collect(Collectors.joining(","));
return "OK %d/%d/%d"
.formatted(code.getCodeSectionCount(), code.getSubcontainerCount(), code.getDataSize());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ void testSingleValidViaInput() {
EvmToolCommand parentCommand = new EvmToolCommand(bais, new PrintWriter(baos, true, UTF_8));
final CodeValidateSubCommand codeValidateSubCommand = new CodeValidateSubCommand(parentCommand);
codeValidateSubCommand.run();
assertThat(baos.toString(UTF_8)).contains("OK 00\n");
assertThat(baos.toString(UTF_8)).contains("OK 1/0/0\n");
}

@Test
Expand All @@ -70,9 +70,9 @@ void testMultipleViaInput() {
assertThat(baos.toString(UTF_8))
.contains(
"""
OK 00
OK 1/0/0
err: layout - EOF header byte 1 incorrect
OK 5f5ff3
OK 1/0/0
""");
}

Expand All @@ -85,7 +85,7 @@ void testSingleValidViaCli() {
final CommandLine cmd = new CommandLine(codeValidateSubCommand);
cmd.parseArgs(CODE_STOP_ONLY);
codeValidateSubCommand.run();
assertThat(baos.toString(UTF_8)).contains("OK 00\n");
assertThat(baos.toString(UTF_8)).contains("OK 1/0/0\n");
}

@Test
Expand All @@ -112,9 +112,9 @@ void testMultipleViaCli() {
assertThat(baos.toString(UTF_8))
.contains(
"""
OK 00
OK 1/0/0
err: layout - EOF header byte 1 incorrect
OK 5f5ff3
OK 1/0/0
""");
}

Expand All @@ -127,7 +127,7 @@ void testCliEclipsesInput() {
final CommandLine cmd = new CommandLine(codeValidateSubCommand);
cmd.parseArgs(CODE_RETURN_ONLY);
codeValidateSubCommand.run();
assertThat(baos.toString(UTF_8)).contains("OK 5f5ff3\n");
assertThat(baos.toString(UTF_8)).contains("OK 1/0/0\n");
}

@Test
Expand All @@ -139,7 +139,7 @@ void testInteriorCommentsSkipped() {
final CommandLine cmd = new CommandLine(codeValidateSubCommand);
cmd.parseArgs(CODE_INTERIOR_COMMENTS);
codeValidateSubCommand.run();
assertThat(baos.toString(UTF_8)).contains("OK 59595959e300015000,f8e4\n");
assertThat(baos.toString(UTF_8)).contains("OK 2/0/0\n");
}

@Test
Expand All @@ -153,9 +153,9 @@ void testBlankLinesAndCommentsSkipped() {
assertThat(baos.toString(UTF_8))
.isEqualTo(
"""
OK 00
OK 1/0/0
err: layout - EOF header byte 1 incorrect
OK 5f5ff3
OK 1/0/0
""");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"cli": [
"code-validate"
],
"stdin": "0xef0001010004020001000b0300010014040004000080000436600060ff6000ec005000ef000101000402000100010400000000800000feda7ac0de",
"stdout": "OK 1/1/4\n"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
"code-validate"
],
"stdin": "ef00010100040200010001040000000080000000",
"stdout": "OK 00\n"
"stdout": "OK 1/0/0\n"
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"stdin": "",
"stdout": [
{"pc":0,"section":0,"op":227,"immediate":"0x0002","gas":"0x2540be400","gasCost":"0x5","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"CALLF"},
{"pc":0,"section":2,"op":229,"immediate":"0x0002","gas":"0x2540be3fb","gasCost":"0x5","memSize":0,"stack":[],"depth":1,"fdepth":1,"refund":0,"opName":"JUMPF"},
{"pc":0,"section":1,"op":228,"gas":"0x2540be3f6","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"fdepth":1,"refund":0,"opName":"RETF"},
{"pc":0,"section":2,"op":229,"immediate":"0x0002","gas":"0x2540be3fb","gasCost":"0x5","memSize":0,"stack":[],"depth":1,"functionDepth":1,"refund":0,"opName":"JUMPF"},
{"pc":0,"section":1,"op":228,"gas":"0x2540be3f6","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"functionDepth":1,"refund":0,"opName":"RETF"},
{"pc":3,"section":0,"op":97,"immediate":"0x2015","gas":"0x2540be3f3","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"},
{"pc":6,"section":0,"op":96,"immediate":"0x01","gas":"0x2540be3f0","gasCost":"0x3","memSize":0,"stack":["0x2015"],"depth":1,"refund":0,"opName":"PUSH1"},
{"pc":8,"section":0,"op":85,"gas":"0x2540be3ed","gasCost":"0x5654","memSize":0,"stack":["0x2015","0x1"],"depth":1,"refund":0,"opName":"SSTORE"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,11 @@ public static void executeTest(final GeneralStateTestCaseEipSpec spec) {
.blobGasPricePerGas(blockHeader.getExcessBlobGas().orElse(BlobGas.ZERO));
final TransactionProcessingResult result =
processor.processTransaction(
worldStateUpdater,
worldStateUpdater,
blockHeader,
transaction,
blockHeader.getCoinbase(),
new CachingBlockHashLookup(blockHeader, blockchain),
new CachingBlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.processingBlock(),
blobGasPrice);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public void tracePostExecution(
}
sb.append("\"depth\":").append(depth).append(",");
if (subdepth >= 1) {
sb.append("\"fdepth\":").append(subdepth).append(",");
sb.append("\"functionDepth\":").append(subdepth).append(",");
}
sb.append("\"refund\":").append(messageFrame.getGasRefund()).append(",");
sb.append("\"opName\":\"").append(currentOp.getName()).append("\"");
Expand Down
21 changes: 21 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,19 @@
<sha256 value="74da05b3ca50a8158101b7e12fbfbf902e011340f14bf31c1776cb51f96147f3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.gitlab.javafuzz" name="core" version="1.26">
<artifact name="core-1.26.jar">
<sha256 value="c6c2a7a67fac12db6dd495181082b2cc3fa8fd30399287854119054dde58ba92" origin="Generated by Gradle"/>
</artifact>
<artifact name="core-1.26.pom">
<sha256 value="e218318c0edfea8c7f7030cbd2ffe9c7db206de39b16147d8a8a2a801515efd6" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.gitlab.javafuzz" name="javafuzz" version="1.26">
<artifact name="javafuzz-1.26.pom">
<sha256 value="c5f521d9795c2bc11293ab08fbc563d453349b398b4fc5afe1388644abc392bf" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.google" name="google" version="5">
<artifact name="google-5.pom">
<sha256 value="e09d345e73ca3fbca7f3e05f30deb74e9d39dd6b79a93fee8c511f23417b6828" origin="Generated by Gradle"/>
Expand Down Expand Up @@ -5372,6 +5385,14 @@
<sha256 value="74958acdde148f30bfa31ffc0858b62f71f63ccc2ceb4d8a8c67d7f428d43a2d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.mockito" name="mockito-inline" version="4.0.0">
<artifact name="mockito-inline-4.0.0.jar">
<sha256 value="ee52e1c299a632184fba274a9370993e09140429f5e516e6c5570fd6574b297f" origin="Generated by Gradle"/>
</artifact>
<artifact name="mockito-inline-4.0.0.pom">
<sha256 value="7ba6e072c76d24d3be8e9c9929c1115a69fa9c71d53d90865cb34cca6ccefb05" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.mockito" name="mockito-junit-jupiter" version="5.8.0">
<artifact name="mockito-junit-jupiter-5.8.0.jar">
<sha256 value="9f6ccc29654335b92ac20e800eb44949772031711185bed6a44a5f8bd56e476b" origin="Generated by Gradle"/>
Expand Down
11 changes: 9 additions & 2 deletions gradle/versions.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ dependencyManagement {

dependency 'org.hyperledger.besu:besu-errorprone-checks:1.0.0'

dependency 'com.gitlab.javafuzz:core:1.26'

dependency 'com.google.guava:guava:33.0.0-jre'

dependency 'com.graphql-java:graphql-java:21.5'
Expand Down Expand Up @@ -153,8 +155,6 @@ dependencyManagement {
}

dependency 'org.fusesource.jansi:jansi:2.4.1'
dependency 'org.openjdk.jol:jol-core:0.17'
dependency 'tech.pegasys:jc-kzg-4844:1.0.0'

dependencySet(group: 'org.hyperledger.besu', version: '0.9.4') {
entry 'arithmetic'
Expand All @@ -173,6 +173,9 @@ dependencyManagement {

dependency 'org.java-websocket:Java-WebSocket:1.5.5'

dependency 'org.jacoco:org.jacoco.agent:0.8.11'
dependency 'org.jacoco:org.jacoco.core:0.8.11'

dependency 'org.jetbrains.kotlin:kotlin-stdlib:1.9.22'

dependencySet(group: 'org.junit.jupiter', version: '5.10.1') {
Expand All @@ -182,6 +185,8 @@ dependencyManagement {
entry 'junit-jupiter-params'
}

dependency 'org.openjdk.jol:jol-core:0.17'

dependency 'org.junit.platform:junit-platform-runner:1.9.2'

dependency 'org.junit.vintage:junit-vintage-engine:5.10.1'
Expand Down Expand Up @@ -232,6 +237,8 @@ dependencyManagement {

dependency 'org.apache.maven:maven-artifact:3.9.6'

dependency 'tech.pegasys:jc-kzg-4844:1.0.0'

dependency 'tech.pegasys.discovery:discovery:22.12.0'
}
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,6 @@ include 'privacy-contracts'
include 'services:kvstore'
include 'services:pipeline'
include 'services:tasks'
include 'testfuzz'
include 'testutil'
include 'util'
29 changes: 29 additions & 0 deletions testfuzz/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# BesuFuzz

BesuFuzz is where all the besu guided fuzzing tools live.

## eof-container

Performs differential fuzzing between Ethereum clients based on
the [txparse eofparse](https://github.com/holiman/txparse/blob/main/README.md#eof-parser-eofparse)
format. Note that only the inital `OK` and `err` values are used to determine if
there is a difference.

### Prototypical CLI Usage:

```shell
BesuFuzz eof-container \
--tests-dir=~/git/ethereum/tests/EOFTests \
--client=evm1=evmone-eofparse \
--client=revm=revme bytecode
```

### Prototypical Gradle usage:

```shell
./gradlew fuzzEvmone fuzzReth
```

There are pre-written Gradle targets for `fuzzEthereumJS`, `fuzzEvmone`,
`fuzzGeth`, `fuzzNethermind`, and `fuzzReth`. Besu is always a fuzzing target.
The `fuzzAll` target will fuzz all clients.
Loading

0 comments on commit 1598e6b

Please sign in to comment.