mirror of https://github.com/hyperledger/besu
EOF Differential Layout Fuzzer (#7488)
Differential EOF Layout Fuzzer guided by Besu's layout parser. Signed-off-by: Danno Ferrin <danno@numisight.com>pull/7508/head
parent
0ec335fbf1
commit
1598e6be67
@ -0,0 +1,7 @@ |
|||||||
|
{ |
||||||
|
"cli": [ |
||||||
|
"code-validate" |
||||||
|
], |
||||||
|
"stdin": "0xef0001010004020001000b0300010014040004000080000436600060ff6000ec005000ef000101000402000100010400000000800000feda7ac0de", |
||||||
|
"stdout": "OK 1/1/4\n" |
||||||
|
} |
@ -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. |
@ -0,0 +1,148 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
apply plugin: 'application' |
||||||
|
apply plugin: 'java-library' |
||||||
|
apply plugin: 'jacoco' |
||||||
|
|
||||||
|
jar { |
||||||
|
archiveBaseName = 'besu-test-fuzz' |
||||||
|
manifest { |
||||||
|
attributes( |
||||||
|
'Specification-Title': archiveBaseName, |
||||||
|
'Specification-Version': project.version, |
||||||
|
'Implementation-Title': archiveBaseName, |
||||||
|
'Implementation-Version': calculateVersion() |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
implementation project(':besu') |
||||||
|
implementation project(':crypto:algorithms') |
||||||
|
implementation project(':datatypes') |
||||||
|
implementation project(':ethereum:referencetests') |
||||||
|
implementation project(':evm') |
||||||
|
implementation project(':util') |
||||||
|
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-databind' |
||||||
|
implementation 'com.gitlab.javafuzz:core' |
||||||
|
implementation 'info.picocli:picocli' |
||||||
|
implementation 'io.tmio:tuweni-bytes' |
||||||
|
implementation 'org.jacoco:org.jacoco.agent' |
||||||
|
implementation 'org.jacoco:org.jacoco.core' |
||||||
|
} |
||||||
|
|
||||||
|
application { |
||||||
|
applicationName = 'BesuFuzz' |
||||||
|
mainClass = 'org.hyperledger.besu.testfuzz.BesuFuzz' |
||||||
|
applicationDefaultJvmArgs = [ |
||||||
|
'-javaagent:$APP_HOME/lib/jacocoagent.jar' |
||||||
|
] |
||||||
|
} |
||||||
|
|
||||||
|
def corpusDir = "${buildDir}/generated/corpus" |
||||||
|
|
||||||
|
tasks.register("runFuzzer", JavaExec) { |
||||||
|
classpath = sourceSets.main.runtimeClasspath |
||||||
|
mainClass = 'org.hyperledger.besu.testfuzz.BesuFuzz' |
||||||
|
|
||||||
|
args = [ |
||||||
|
"eof-container", |
||||||
|
"--tests-dir=${projectDir}/../ethereum/referencetests/src/reference-test/external-resources/EOFTests", |
||||||
|
"--corpus-dir=${corpusDir}" |
||||||
|
] |
||||||
|
doFirst { |
||||||
|
mkdir corpusDir |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
tasks.register("fuzzEvmone") { |
||||||
|
doLast { |
||||||
|
runFuzzer.args += "--client=evm1=evmone-eofparse" |
||||||
|
} |
||||||
|
finalizedBy("runFuzzer") |
||||||
|
} |
||||||
|
|
||||||
|
tasks.register("fuzzEthereumJS") { |
||||||
|
doLast { |
||||||
|
runFuzzer.args += "--client=etjs=tsx ../../../ethereumjs/ethereumjs-monorepo/packages/evm/scripts/eofContainerValidator.ts" |
||||||
|
} |
||||||
|
finalizedBy("runFuzzer") |
||||||
|
} |
||||||
|
|
||||||
|
tasks.register("fuzzGeth") { |
||||||
|
doLast { |
||||||
|
runFuzzer.args += "--client=geth=eofdump eofparser" |
||||||
|
} |
||||||
|
finalizedBy("runFuzzer") |
||||||
|
} |
||||||
|
|
||||||
|
tasks.register("fuzzNethermind") { |
||||||
|
doLast { |
||||||
|
runFuzzer.args += "--client=neth=netheofparse -x" |
||||||
|
} |
||||||
|
finalizedBy("runFuzzer") |
||||||
|
} |
||||||
|
|
||||||
|
tasks.register("fuzzReth") { |
||||||
|
doLast { |
||||||
|
runFuzzer.args += "--client=revm=revme bytecode" |
||||||
|
} |
||||||
|
finalizedBy("runFuzzer") |
||||||
|
} |
||||||
|
|
||||||
|
tasks.register("fuzzAll") { |
||||||
|
dependsOn fuzzEvm1, fuzzEthJS, fuzzGeth, fuzzNethermind, fuzzReth |
||||||
|
} |
||||||
|
|
||||||
|
jacoco { |
||||||
|
applyTo run |
||||||
|
applyTo runFuzzer |
||||||
|
} |
||||||
|
|
||||||
|
// Copies jacoco into the lib directory |
||||||
|
tasks.register("copyJacoco", Copy) { |
||||||
|
// The jacocoagent.jar is embedded within the jar |
||||||
|
from zipTree(configurations.jacocoAgent.singleFile).filter { it.name == 'jacocoagent.jar' }.singleFile |
||||||
|
into layout.buildDirectory.dir("install/${application.applicationName}/lib") |
||||||
|
} |
||||||
|
|
||||||
|
installDist.finalizedBy copyJacoco |
||||||
|
|
||||||
|
startScripts { |
||||||
|
defaultJvmOpts = [ |
||||||
|
"-Dsecp256k1.randomize=false" |
||||||
|
] |
||||||
|
unixStartScriptGenerator.template = resources.text.fromFile("${projectDir}/src/main/scripts/unixStartScript.txt") |
||||||
|
windowsStartScriptGenerator.template = resources.text.fromFile("${projectDir}/src/main/scripts/windowsStartScript.txt") |
||||||
|
doLast { tweakStartScript(startScripts) } |
||||||
|
} |
||||||
|
|
||||||
|
static def tweakStartScript(createScriptTask) { |
||||||
|
def shortenWindowsClasspath = { line -> |
||||||
|
line.replaceAll(/^set CLASSPATH=.*$/, "set CLASSPATH=%APP_HOME%/lib/*") |
||||||
|
} |
||||||
|
|
||||||
|
createScriptTask.unixScript.text = createScriptTask.unixScript.text.replace('BESU_HOME', '\$APP_HOME') |
||||||
|
createScriptTask.windowsScript.text = createScriptTask.windowsScript.text.replace('BESU_HOME', '%~dp0..') |
||||||
|
|
||||||
|
// Prevent the error originating from the 8191 chars limit on Windows |
||||||
|
createScriptTask.windowsScript.text = |
||||||
|
createScriptTask.windowsScript |
||||||
|
.readLines() |
||||||
|
.collect(shortenWindowsClasspath) |
||||||
|
.join('\r\n') |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.testfuzz; |
||||||
|
|
||||||
|
import org.hyperledger.besu.util.LogConfigurator; |
||||||
|
|
||||||
|
/** The main entry point for the EVM (Ethereum Virtual Machine) tool. */ |
||||||
|
public final class BesuFuzz { |
||||||
|
|
||||||
|
/** Default constructor for the EvmTool class. */ |
||||||
|
public BesuFuzz() { |
||||||
|
// this is here only for Javadoc linting
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* The main entry point for the EVM (Ethereum Virtual Machine) tool. |
||||||
|
* |
||||||
|
* @param args The command line arguments. |
||||||
|
*/ |
||||||
|
public static void main(final String... args) { |
||||||
|
LogConfigurator.setLevel("", "DEBUG"); |
||||||
|
final BesuFuzzCommand besuFuzzCommand = new BesuFuzzCommand(); |
||||||
|
|
||||||
|
besuFuzzCommand.execute(args); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.testfuzz; |
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8; |
||||||
|
|
||||||
|
import org.hyperledger.besu.util.LogConfigurator; |
||||||
|
|
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.PrintWriter; |
||||||
|
|
||||||
|
import picocli.CommandLine; |
||||||
|
import picocli.CommandLine.Command; |
||||||
|
|
||||||
|
/** |
||||||
|
* This is the root command for the `BesuFuzz` command line tool. It is a collection of fuzzers that |
||||||
|
* are guided by Besu's implementations. |
||||||
|
*/ |
||||||
|
@Command( |
||||||
|
description = "Executes Besu based fuzz tests", |
||||||
|
abbreviateSynopsis = true, |
||||||
|
name = "evm", |
||||||
|
mixinStandardHelpOptions = true, |
||||||
|
versionProvider = VersionProvider.class, |
||||||
|
sortOptions = false, |
||||||
|
header = "Usage:", |
||||||
|
synopsisHeading = "%n", |
||||||
|
descriptionHeading = "%nDescription:%n%n", |
||||||
|
optionListHeading = "%nOptions:%n", |
||||||
|
footerHeading = "%n", |
||||||
|
footer = "Hyperledger Besu is licensed under the Apache License 2.0", |
||||||
|
subcommands = {EofContainerSubCommand.class}) |
||||||
|
@SuppressWarnings("java:S106") |
||||||
|
public class BesuFuzzCommand implements Runnable { |
||||||
|
|
||||||
|
PrintWriter out; |
||||||
|
InputStream in; |
||||||
|
|
||||||
|
/** Default Constructor */ |
||||||
|
BesuFuzzCommand() { |
||||||
|
// this method is here only for JavaDoc linting
|
||||||
|
} |
||||||
|
|
||||||
|
void execute(final String... args) { |
||||||
|
execute(System.in, new PrintWriter(System.out, true, UTF_8), args); |
||||||
|
} |
||||||
|
|
||||||
|
void execute(final InputStream input, final PrintWriter output, final String[] args) { |
||||||
|
final CommandLine commandLine = new CommandLine(this).setOut(output); |
||||||
|
out = output; |
||||||
|
in = input; |
||||||
|
|
||||||
|
// don't require exact case to match enum values
|
||||||
|
commandLine.setCaseInsensitiveEnumValuesAllowed(true); |
||||||
|
|
||||||
|
commandLine.setExecutionStrategy(new CommandLine.RunLast()); |
||||||
|
commandLine.execute(args); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
LogConfigurator.setLevel("", "OFF"); |
||||||
|
System.out.println("No default command, please select a subcommand"); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,258 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.testfuzz; |
||||||
|
|
||||||
|
import static org.hyperledger.besu.testfuzz.EofContainerSubCommand.COMMAND_NAME; |
||||||
|
|
||||||
|
import org.hyperledger.besu.datatypes.Address; |
||||||
|
import org.hyperledger.besu.ethereum.referencetests.EOFTestCaseSpec; |
||||||
|
import org.hyperledger.besu.evm.Code; |
||||||
|
import org.hyperledger.besu.evm.EVM; |
||||||
|
import org.hyperledger.besu.evm.MainnetEVMs; |
||||||
|
import org.hyperledger.besu.evm.code.CodeInvalid; |
||||||
|
import org.hyperledger.besu.evm.code.CodeV1; |
||||||
|
import org.hyperledger.besu.evm.code.EOFLayout; |
||||||
|
import org.hyperledger.besu.evm.code.EOFLayout.EOFContainerMode; |
||||||
|
import org.hyperledger.besu.evm.internal.EvmConfiguration; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.lang.reflect.InvocationTargetException; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.security.NoSuchAlgorithmException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Locale; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import com.fasterxml.jackson.core.JsonParser.Feature; |
||||||
|
import com.fasterxml.jackson.core.util.DefaultIndenter; |
||||||
|
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; |
||||||
|
import com.fasterxml.jackson.core.util.Separators; |
||||||
|
import com.fasterxml.jackson.core.util.Separators.Spacing; |
||||||
|
import com.fasterxml.jackson.databind.JavaType; |
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule; |
||||||
|
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
||||||
|
import com.gitlab.javafuzz.core.AbstractFuzzTarget; |
||||||
|
import org.apache.tuweni.bytes.Bytes; |
||||||
|
import picocli.CommandLine; |
||||||
|
import picocli.CommandLine.Option; |
||||||
|
|
||||||
|
/** Fuzzes the parsing and validation of an EOF container. */ |
||||||
|
@SuppressWarnings({"java:S106", "CallToPrintStackTrace"}) // we use lots the console, on purpose
|
||||||
|
@CommandLine.Command( |
||||||
|
name = COMMAND_NAME, |
||||||
|
description = "Fuzzes EOF container parsing and validation", |
||||||
|
mixinStandardHelpOptions = true, |
||||||
|
versionProvider = VersionProvider.class) |
||||||
|
public class EofContainerSubCommand extends AbstractFuzzTarget implements Runnable { |
||||||
|
|
||||||
|
static final String COMMAND_NAME = "eof-container"; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--corpus-dir"}, |
||||||
|
paramLabel = "<directory>", |
||||||
|
description = "Directory to store corpus files") |
||||||
|
private final Path corpusDir = Path.of("corpus"); |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--tests-dir"}, |
||||||
|
paramLabel = "<directory>", |
||||||
|
description = "Directory where EOF tests references file tree lives") |
||||||
|
private final Path testsDir = null; |
||||||
|
|
||||||
|
@Option( |
||||||
|
names = {"--client"}, |
||||||
|
paramLabel = "<directory>=<CLI>", |
||||||
|
description = "Add a client for differential fuzzing") |
||||||
|
private final Map<String, String> clients = new LinkedHashMap<>(); |
||||||
|
|
||||||
|
@CommandLine.ParentCommand private final BesuFuzzCommand parentCommand; |
||||||
|
|
||||||
|
static final ObjectMapper eofTestMapper = createObjectMapper(); |
||||||
|
static final JavaType javaType = |
||||||
|
eofTestMapper |
||||||
|
.getTypeFactory() |
||||||
|
.constructParametricType(Map.class, String.class, EOFTestCaseSpec.class); |
||||||
|
|
||||||
|
List<ExternalClient> externalClients = new ArrayList<>(); |
||||||
|
EVM evm = MainnetEVMs.pragueEOF(EvmConfiguration.DEFAULT); |
||||||
|
long validContainers; |
||||||
|
long totalContainers; |
||||||
|
|
||||||
|
/** |
||||||
|
* Default constructor for the EofContainerSubCommand class. This constructor initializes the |
||||||
|
* parentCommand to null. |
||||||
|
*/ |
||||||
|
public EofContainerSubCommand() { |
||||||
|
this(null); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new EofContainerSubCommand with the specified parent command. |
||||||
|
* |
||||||
|
* @param parentCommand The parent command for this subcommand. |
||||||
|
*/ |
||||||
|
public EofContainerSubCommand(final BesuFuzzCommand parentCommand) { |
||||||
|
this.parentCommand = parentCommand; |
||||||
|
} |
||||||
|
|
||||||
|
private static ObjectMapper createObjectMapper() { |
||||||
|
final ObjectMapper objectMapper = new ObjectMapper(); |
||||||
|
objectMapper.setDefaultPrettyPrinter( |
||||||
|
(new DefaultPrettyPrinter()) |
||||||
|
.withSeparators( |
||||||
|
Separators.createDefaultInstance().withObjectFieldValueSpacing(Spacing.BOTH)) |
||||||
|
.withObjectIndenter(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE.withIndent(" ")) |
||||||
|
.withArrayIndenter(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE.withIndent(" "))); |
||||||
|
objectMapper.disable(Feature.AUTO_CLOSE_SOURCE); |
||||||
|
SimpleModule serializers = new SimpleModule("Serializers"); |
||||||
|
serializers.addSerializer(Address.class, ToStringSerializer.instance); |
||||||
|
serializers.addSerializer(Bytes.class, ToStringSerializer.instance); |
||||||
|
objectMapper.registerModule(serializers); |
||||||
|
|
||||||
|
return objectMapper; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
// load test dir into corpus dir
|
||||||
|
if (testsDir != null) { |
||||||
|
File f = testsDir.toFile(); |
||||||
|
if (f.isDirectory()) { |
||||||
|
try (var files = Files.walk(f.toPath(), Integer.MAX_VALUE)) { |
||||||
|
files.forEach( |
||||||
|
ff -> { |
||||||
|
File file = ff.toFile(); |
||||||
|
if (file.isFile()) { |
||||||
|
extractFile(file, corpusDir.toFile()); |
||||||
|
} |
||||||
|
}); |
||||||
|
} catch (IOException e) { |
||||||
|
parentCommand.out.println("Exception walking " + f + ": " + e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
clients.forEach((k, v) -> externalClients.add(new StreamingClient(k, v.split(" ")))); |
||||||
|
System.out.println("Fuzzing client set: " + clients.keySet()); |
||||||
|
|
||||||
|
try { |
||||||
|
new Fuzzer(this, corpusDir.toString(), this::fuzzStats).start(); |
||||||
|
} catch (NoSuchAlgorithmException |
||||||
|
| ClassNotFoundException |
||||||
|
| InvocationTargetException |
||||||
|
| IllegalAccessException |
||||||
|
| NoSuchMethodException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void extractFile(final File f, final File initialCorpus) { |
||||||
|
final Map<String, EOFTestCaseSpec> eofTests; |
||||||
|
try { |
||||||
|
eofTests = eofTestMapper.readValue(f, javaType); |
||||||
|
} catch (IOException e) { |
||||||
|
// presume parse failed because it's a corpus file
|
||||||
|
return; |
||||||
|
} |
||||||
|
for (var entry : eofTests.entrySet()) { |
||||||
|
int index = 0; |
||||||
|
for (var vector : entry.getValue().getVector().entrySet()) { |
||||||
|
try (FileOutputStream fos = |
||||||
|
new FileOutputStream( |
||||||
|
new File( |
||||||
|
initialCorpus, |
||||||
|
f.toPath().getFileName() + "_" + (index++) + "_" + vector.getKey()))) { |
||||||
|
Bytes codeBytes = Bytes.fromHexString(vector.getValue().code()); |
||||||
|
evm.getCodeUncached(codeBytes); |
||||||
|
fos.write(codeBytes.toArrayUnsafe()); |
||||||
|
} catch (IOException e) { |
||||||
|
parentCommand.out.println("Invalid file " + f + ": " + e.getMessage()); |
||||||
|
e.printStackTrace(); |
||||||
|
System.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void fuzz(final byte[] bytes) { |
||||||
|
Bytes eofUnderTest = Bytes.wrap(bytes); |
||||||
|
String eofUnderTestHexString = eofUnderTest.toHexString(); |
||||||
|
Code code = evm.getCodeUncached(eofUnderTest); |
||||||
|
Map<String, String> results = new LinkedHashMap<>(); |
||||||
|
boolean mismatch = false; |
||||||
|
for (var client : externalClients) { |
||||||
|
String value = client.differentialFuzz(eofUnderTestHexString); |
||||||
|
results.put(client.getName(), value); |
||||||
|
if (value == null || value.startsWith("fail: ")) { |
||||||
|
mismatch = true; // if an external client fails, always report it as an error
|
||||||
|
} |
||||||
|
} |
||||||
|
boolean besuValid = false; |
||||||
|
String besuReason; |
||||||
|
if (!code.isValid()) { |
||||||
|
besuReason = ((CodeInvalid) code).getInvalidReason(); |
||||||
|
} else if (code.getEofVersion() != 1) { |
||||||
|
EOFLayout layout = EOFLayout.parseEOF(eofUnderTest); |
||||||
|
if (layout.isValid()) { |
||||||
|
besuReason = "Besu Parsing Error"; |
||||||
|
parentCommand.out.println(layout.version()); |
||||||
|
parentCommand.out.println(layout.invalidReason()); |
||||||
|
parentCommand.out.println(code.getEofVersion()); |
||||||
|
parentCommand.out.println(code.getClass().getName()); |
||||||
|
System.exit(1); |
||||||
|
mismatch = true; |
||||||
|
} else { |
||||||
|
besuReason = layout.invalidReason(); |
||||||
|
} |
||||||
|
} else if (EOFContainerMode.INITCODE.equals( |
||||||
|
((CodeV1) code).getEofLayout().containerMode().get())) { |
||||||
|
besuReason = "Code is initcode, not runtime"; |
||||||
|
} else { |
||||||
|
besuReason = "OK"; |
||||||
|
besuValid = true; |
||||||
|
} |
||||||
|
for (var entry : results.entrySet()) { |
||||||
|
mismatch = |
||||||
|
mismatch |
||||||
|
|| besuValid != entry.getValue().toUpperCase(Locale.getDefault()).startsWith("OK"); |
||||||
|
} |
||||||
|
if (mismatch) { |
||||||
|
parentCommand.out.println("besu: " + besuReason); |
||||||
|
for (var entry : results.entrySet()) { |
||||||
|
parentCommand.out.println(entry.getKey() + ": " + entry.getValue()); |
||||||
|
} |
||||||
|
parentCommand.out.println("code: " + eofUnderTest.toUnprefixedHexString()); |
||||||
|
parentCommand.out.println("size: " + eofUnderTest.size()); |
||||||
|
parentCommand.out.println(); |
||||||
|
} else { |
||||||
|
if (besuValid) { |
||||||
|
validContainers++; |
||||||
|
} |
||||||
|
totalContainers++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String fuzzStats() { |
||||||
|
return " / %5.2f%% valid %d/%d" |
||||||
|
.formatted((100.0 * validContainers) / totalContainers, validContainers, totalContainers); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.testfuzz; |
||||||
|
|
||||||
|
interface ExternalClient { |
||||||
|
|
||||||
|
String getName(); |
||||||
|
|
||||||
|
String differentialFuzz(String data); |
||||||
|
} |
@ -0,0 +1,239 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.testfuzz; |
||||||
|
|
||||||
|
import org.hyperledger.besu.crypto.Hash; |
||||||
|
import org.hyperledger.besu.crypto.MessageDigestFactory; |
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.lang.reflect.InvocationTargetException; |
||||||
|
import java.lang.reflect.Method; |
||||||
|
import java.math.BigInteger; |
||||||
|
import java.security.MessageDigest; |
||||||
|
import java.security.NoSuchAlgorithmException; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.function.Supplier; |
||||||
|
|
||||||
|
import com.gitlab.javafuzz.core.AbstractFuzzTarget; |
||||||
|
import com.gitlab.javafuzz.core.Corpus; |
||||||
|
import org.apache.tuweni.bytes.Bytes; |
||||||
|
import org.jacoco.core.data.ExecutionData; |
||||||
|
import org.jacoco.core.data.ExecutionDataReader; |
||||||
|
import org.jacoco.core.data.IExecutionDataVisitor; |
||||||
|
import org.jacoco.core.data.ISessionInfoVisitor; |
||||||
|
import org.jacoco.core.data.SessionInfo; |
||||||
|
|
||||||
|
/** Ported from javafuzz because JaCoCo APIs changed. */ |
||||||
|
@SuppressWarnings({"java:S106", "CallToPrintStackTrace"}) // we use lots the console, on purpose
|
||||||
|
public class Fuzzer { |
||||||
|
private final AbstractFuzzTarget target; |
||||||
|
private final Corpus corpus; |
||||||
|
private final Object agent; |
||||||
|
private final Method getExecutionDataMethod; |
||||||
|
private long executionsInSample; |
||||||
|
private long lastSampleTime; |
||||||
|
private long totalExecutions; |
||||||
|
private long totalCoverage; |
||||||
|
|
||||||
|
Supplier<String> fuzzStats; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create a new fuzzer |
||||||
|
* |
||||||
|
* @param target The target to fuzz |
||||||
|
* @param dirs the list of corpus dirs and files, comma separated. |
||||||
|
* @param fuzzStats additional fuzzing data from the client |
||||||
|
* @throws ClassNotFoundException If Jacoco RT is not found (because jacocoagent.jar is not |
||||||
|
* loaded) |
||||||
|
* @throws NoSuchMethodException If the wrong version of Jacoco is loaded |
||||||
|
* @throws InvocationTargetException If the wrong version of Jacoco is loaded |
||||||
|
* @throws IllegalAccessException If the wrong version of Jacoco is loaded |
||||||
|
* @throws NoSuchAlgorithmException If the SHA-256 crypto algo cannot be loaded. |
||||||
|
*/ |
||||||
|
public Fuzzer( |
||||||
|
final AbstractFuzzTarget target, final String dirs, final Supplier<String> fuzzStats) |
||||||
|
throws ClassNotFoundException, |
||||||
|
NoSuchMethodException, |
||||||
|
InvocationTargetException, |
||||||
|
IllegalAccessException, |
||||||
|
NoSuchAlgorithmException { |
||||||
|
this.target = target; |
||||||
|
this.corpus = new Corpus(dirs); |
||||||
|
this.fuzzStats = fuzzStats; |
||||||
|
Class<?> c = Class.forName("org.jacoco.agent.rt.RT"); |
||||||
|
Method getAgentMethod = c.getMethod("getAgent"); |
||||||
|
this.agent = getAgentMethod.invoke(null); |
||||||
|
this.getExecutionDataMethod = agent.getClass().getMethod("getExecutionData", boolean.class); |
||||||
|
fileNameForBuffer(new byte[0]); |
||||||
|
} |
||||||
|
|
||||||
|
void writeCrash(final byte[] buf) { |
||||||
|
Bytes hash = Hash.sha256(Bytes.wrap(buf)); |
||||||
|
String filepath = "crash-" + hash.toUnprefixedHexString(); |
||||||
|
try (FileOutputStream fos = new FileOutputStream(filepath)) { |
||||||
|
fos.write(buf); |
||||||
|
System.out.printf("crash was written to %s%n", filepath); |
||||||
|
} catch (IOException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void logStats(final String type) { |
||||||
|
long rss = |
||||||
|
(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024 / 1024; |
||||||
|
long endTime = System.currentTimeMillis(); |
||||||
|
long execs_per_second = -1; |
||||||
|
if ((endTime - this.lastSampleTime) != 0) { |
||||||
|
execs_per_second = (this.executionsInSample * 1000 / (endTime - this.lastSampleTime)); |
||||||
|
} |
||||||
|
this.lastSampleTime = endTime; |
||||||
|
this.executionsInSample = 0; |
||||||
|
|
||||||
|
System.out.printf( |
||||||
|
"#%d %s cov: %d corp: %d exec/s: %d rss: %d MB %s%n", |
||||||
|
this.totalExecutions, |
||||||
|
type, |
||||||
|
this.totalCoverage, |
||||||
|
this.corpus.getLength(), |
||||||
|
execs_per_second, |
||||||
|
rss, |
||||||
|
fuzzStats.get()); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Runs the fuzzer until the VM is shut down |
||||||
|
* |
||||||
|
* @throws InvocationTargetException if the wrong version of jacoco is loaded |
||||||
|
* @throws IllegalAccessException if the wrong version of jacoco is loaded |
||||||
|
* @throws NoSuchAlgorithmException if our favorite hash algo is not loaded |
||||||
|
*/ |
||||||
|
@SuppressWarnings("java:S2189") // the endless loop is on purpose
|
||||||
|
public void start() |
||||||
|
throws InvocationTargetException, IllegalAccessException, NoSuchAlgorithmException { |
||||||
|
System.out.printf("#0 READ units: %d%n", this.corpus.getLength()); |
||||||
|
this.totalCoverage = 0; |
||||||
|
this.totalExecutions = 0; |
||||||
|
this.executionsInSample = 0; |
||||||
|
this.lastSampleTime = System.currentTimeMillis(); |
||||||
|
|
||||||
|
Map<String, Integer> hitMap = new HashMap<>(); |
||||||
|
|
||||||
|
while (true) { |
||||||
|
byte[] buf = this.corpus.generateInput(); |
||||||
|
// The next version will run this in a different thread.
|
||||||
|
try { |
||||||
|
this.target.fuzz(buf); |
||||||
|
} catch (Exception e) { |
||||||
|
e.printStackTrace(System.out); |
||||||
|
this.writeCrash(buf); |
||||||
|
System.exit(1); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
this.totalExecutions++; |
||||||
|
this.executionsInSample++; |
||||||
|
|
||||||
|
long newCoverage = getHitCount(hitMap); |
||||||
|
if (newCoverage > this.totalCoverage) { |
||||||
|
this.totalCoverage = newCoverage; |
||||||
|
this.corpus.putBuffer(buf); |
||||||
|
this.logStats("NEW"); |
||||||
|
|
||||||
|
// If you want hex strings of new hits, uncomment the following.
|
||||||
|
// String filename = fileNameForBuffer(buf);
|
||||||
|
// try (var pw =
|
||||||
|
// new PrintWriter(
|
||||||
|
// new BufferedWriter(
|
||||||
|
// new OutputStreamWriter(new FileOutputStream(filename), UTF_8)))) {
|
||||||
|
// pw.println(Bytes.wrap(buf).toHexString());
|
||||||
|
// System.out.println(filename);
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// e.printStackTrace(System.out);
|
||||||
|
// }
|
||||||
|
} else if ((System.currentTimeMillis() - this.lastSampleTime) > 30000) { |
||||||
|
this.logStats("PULSE"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static String fileNameForBuffer(final byte[] buf) throws NoSuchAlgorithmException { |
||||||
|
MessageDigest md = MessageDigestFactory.create(MessageDigestFactory.SHA256_ALG); |
||||||
|
md.update(buf); |
||||||
|
byte[] digest = md.digest(); |
||||||
|
return String.format("./new-%064x.hex", new BigInteger(1, digest)); |
||||||
|
} |
||||||
|
|
||||||
|
private long getHitCount(final Map<String, Integer> hitMap) |
||||||
|
throws IllegalAccessException, InvocationTargetException { |
||||||
|
byte[] dumpData = (byte[]) this.getExecutionDataMethod.invoke(this.agent, false); |
||||||
|
ExecutionDataReader edr = new ExecutionDataReader(new ByteArrayInputStream(dumpData)); |
||||||
|
HitCounter hc = new HitCounter(hitMap); |
||||||
|
edr.setExecutionDataVisitor(hc); |
||||||
|
edr.setSessionInfoVisitor(hc); |
||||||
|
try { |
||||||
|
edr.read(); |
||||||
|
} catch (IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
this.writeCrash(dumpData); |
||||||
|
} |
||||||
|
|
||||||
|
return hc.getHits(); |
||||||
|
} |
||||||
|
|
||||||
|
static class HitCounter implements IExecutionDataVisitor, ISessionInfoVisitor { |
||||||
|
long hits = 0; |
||||||
|
Map<String, Integer> hitMap; |
||||||
|
|
||||||
|
public HitCounter(final Map<String, Integer> hitMap) { |
||||||
|
this.hitMap = hitMap; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void visitClassExecution(final ExecutionData executionData) { |
||||||
|
int hit = 0; |
||||||
|
for (boolean b : executionData.getProbes()) { |
||||||
|
if (executionData.getName().startsWith("org/hyperledger/besu/testfuzz/") |
||||||
|
|| executionData.getName().startsWith("org/bouncycastle/") |
||||||
|
|| executionData.getName().startsWith("com/gitlab/javafuzz/")) { |
||||||
|
continue; |
||||||
|
} |
||||||
|
if (b) { |
||||||
|
hit++; |
||||||
|
} |
||||||
|
} |
||||||
|
String name = executionData.getName(); |
||||||
|
if (hitMap.containsKey(name)) { |
||||||
|
if (hitMap.get(name) < hit) { |
||||||
|
hitMap.put(name, hit); |
||||||
|
} |
||||||
|
} else { |
||||||
|
hitMap.put(name, hit); |
||||||
|
} |
||||||
|
hits += hit; |
||||||
|
} |
||||||
|
|
||||||
|
public long getHits() { |
||||||
|
return hits; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void visitSessionInfo(final SessionInfo sessionInfo) { |
||||||
|
// nothing to do. Data parser requires a session listener.
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.testfuzz; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
import java.util.regex.Matcher; |
||||||
|
import java.util.regex.Pattern; |
||||||
|
|
||||||
|
@SuppressWarnings({"java:S106", "CallToPrintStackTrace"}) // we use lots the console, on purpose
|
||||||
|
class SingleQueryClient implements ExternalClient { |
||||||
|
final String name; |
||||||
|
String[] command; |
||||||
|
Pattern okRegexp; |
||||||
|
String okRegexpStr; |
||||||
|
int okGroup; |
||||||
|
Pattern failRegexp; |
||||||
|
int failGroup; |
||||||
|
String failRegexpStr; |
||||||
|
|
||||||
|
public SingleQueryClient( |
||||||
|
final String clientName, |
||||||
|
final String okRegexp, |
||||||
|
final int okGroup, |
||||||
|
final String errorRegexp, |
||||||
|
final int failGroup, |
||||||
|
final String... command) { |
||||||
|
this.name = clientName; |
||||||
|
this.okRegexp = Pattern.compile(okRegexp); |
||||||
|
this.okRegexpStr = okRegexp; |
||||||
|
this.okGroup = okGroup; |
||||||
|
this.failRegexp = Pattern.compile(errorRegexp); |
||||||
|
this.failGroup = failGroup; |
||||||
|
this.failRegexpStr = errorRegexp; |
||||||
|
this.command = command; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
@SuppressWarnings("java:S2142") |
||||||
|
public String differentialFuzz(final String data) { |
||||||
|
if (!data.startsWith("0xef")) { |
||||||
|
return "err: <harness> invalid_magic"; |
||||||
|
} |
||||||
|
try { |
||||||
|
List<String> localCommand = new ArrayList<>(command.length + 1); |
||||||
|
localCommand.addAll(Arrays.asList(command)); |
||||||
|
localCommand.add(data); |
||||||
|
Process p = new ProcessBuilder().command(localCommand).redirectErrorStream(true).start(); |
||||||
|
if (!p.waitFor(1, TimeUnit.SECONDS)) { |
||||||
|
System.out.println("Process Hang for " + name); |
||||||
|
return "fail: process took more than 1 sec " + p.pid(); |
||||||
|
} |
||||||
|
String s = new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8); |
||||||
|
Matcher m = okRegexp.matcher(s); |
||||||
|
if (m.find()) { |
||||||
|
return "OK " + m.group(okGroup); |
||||||
|
} |
||||||
|
m = failRegexp.matcher(s); |
||||||
|
if (m.find()) { |
||||||
|
return "err: " + m.group(failGroup); |
||||||
|
} |
||||||
|
return "fail: SingleClientQuery failed to get data"; |
||||||
|
} catch (InterruptedException | IOException e) { |
||||||
|
e.printStackTrace(); |
||||||
|
return "fail: " + e.getMessage(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.testfuzz; |
||||||
|
|
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.PrintWriter; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
class StreamingClient implements ExternalClient { |
||||||
|
final String name; |
||||||
|
final BufferedReader reader; |
||||||
|
final PrintWriter writer; |
||||||
|
|
||||||
|
public StreamingClient(final String clientName, final String... command) { |
||||||
|
try { |
||||||
|
Process p = new ProcessBuilder().redirectErrorStream(true).command(command).start(); |
||||||
|
this.name = clientName; |
||||||
|
this.reader = new BufferedReader(p.inputReader(StandardCharsets.UTF_8)); |
||||||
|
this.writer = new PrintWriter(p.getOutputStream(), true, StandardCharsets.UTF_8); |
||||||
|
} catch (IOException e) { |
||||||
|
throw new RuntimeException(e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() { |
||||||
|
return name; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String differentialFuzz(final String data) { |
||||||
|
try { |
||||||
|
writer.println(data); |
||||||
|
return reader.readLine(); |
||||||
|
} catch (IOException ioe) { |
||||||
|
throw new RuntimeException(ioe); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
/* |
||||||
|
* Copyright contributors to Hyperledger Besu. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||||
|
* the License. You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||||
|
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||||
|
* specific language governing permissions and limitations under the License. |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
package org.hyperledger.besu.testfuzz; |
||||||
|
|
||||||
|
import org.hyperledger.besu.BesuInfo; |
||||||
|
|
||||||
|
import picocli.CommandLine; |
||||||
|
|
||||||
|
/** |
||||||
|
* The VersionProvider class is responsible for providing the version of the Hyperledger Besu EVM |
||||||
|
* tool. It implements the IVersionProvider interface from the picocli library. |
||||||
|
* |
||||||
|
* <p>The getVersion method returns a string array containing the version of the Hyperledger Besu |
||||||
|
* EVM tool. |
||||||
|
*/ |
||||||
|
public class VersionProvider implements CommandLine.IVersionProvider { |
||||||
|
|
||||||
|
/** |
||||||
|
* Default constructor for the VersionProvider class. This constructor does not perform any |
||||||
|
* operations. |
||||||
|
*/ |
||||||
|
public VersionProvider() { |
||||||
|
// this constructor is here only for javadoc linting
|
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* This method returns the version of the Hyperledger Besu EVM tool. |
||||||
|
* |
||||||
|
* @return A string array containing the version of the Hyperledger Besu EVM tool. |
||||||
|
*/ |
||||||
|
@Override |
||||||
|
public String[] getVersion() { |
||||||
|
return new String[] {"Hyperledger Besu evm " + BesuInfo.shortVersion()}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,200 @@ |
|||||||
|
#!/usr/bin/env sh |
||||||
|
|
||||||
|
# |
||||||
|
# Copyright 2015 the original author or authors. |
||||||
|
# |
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
# you may not use this file except in compliance with the License. |
||||||
|
# You may obtain a copy of the License at |
||||||
|
# |
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
# |
||||||
|
# Unless required by applicable law or agreed to in writing, software |
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
# See the License for the specific language governing permissions and |
||||||
|
# limitations under the License. |
||||||
|
# |
||||||
|
|
||||||
|
############################################################################## |
||||||
|
## |
||||||
|
## ${applicationName} start up script for UN*X |
||||||
|
## |
||||||
|
############################################################################## |
||||||
|
|
||||||
|
# Attempt to set APP_HOME |
||||||
|
# Resolve links: \$0 may be a link |
||||||
|
PRG="\$0" |
||||||
|
# Need this for relative symlinks. |
||||||
|
while [ -h "\$PRG" ] ; do |
||||||
|
ls=`ls -ld "\$PRG"` |
||||||
|
link=`expr "\$ls" : '.*-> \\(.*\\)\$'` |
||||||
|
if expr "\$link" : '/.*' > /dev/null; then |
||||||
|
PRG="\$link" |
||||||
|
else |
||||||
|
PRG=`dirname "\$PRG"`"/\$link" |
||||||
|
fi |
||||||
|
done |
||||||
|
SAVED="`pwd`" |
||||||
|
cd "`dirname \"\$PRG\"`/${appHomeRelativePath}" >/dev/null |
||||||
|
APP_HOME="`pwd -P`" |
||||||
|
cd "\$SAVED" >/dev/null |
||||||
|
|
||||||
|
APP_NAME="${applicationName}" |
||||||
|
APP_BASE_NAME=`basename "\$0"` |
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. |
||||||
|
DEFAULT_JVM_OPTS=${defaultJvmOpts} |
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value. |
||||||
|
MAX_FD="maximum" |
||||||
|
|
||||||
|
warn () { |
||||||
|
echo "\$*" |
||||||
|
} |
||||||
|
|
||||||
|
die () { |
||||||
|
echo |
||||||
|
echo "\$*" |
||||||
|
echo |
||||||
|
exit 1 |
||||||
|
} |
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false'). |
||||||
|
cygwin=false |
||||||
|
msys=false |
||||||
|
darwin=false |
||||||
|
nonstop=false |
||||||
|
case "`uname`" in |
||||||
|
CYGWIN* ) |
||||||
|
cygwin=true |
||||||
|
;; |
||||||
|
Darwin* ) |
||||||
|
darwin=true |
||||||
|
;; |
||||||
|
MSYS* | MINGW* ) |
||||||
|
msys=true |
||||||
|
;; |
||||||
|
NONSTOP* ) |
||||||
|
nonstop=true |
||||||
|
;; |
||||||
|
esac |
||||||
|
|
||||||
|
CLASSPATH=$classpath |
||||||
|
<% if ( mainClassName.startsWith('--module ') ) { %>MODULE_PATH=$modulePath<% } %> |
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM. |
||||||
|
if [ -n "\$JAVA_HOME" ] ; then |
||||||
|
if [ -x "\$JAVA_HOME/jre/sh/java" ] ; then |
||||||
|
# IBM's JDK on AIX uses strange locations for the executables |
||||||
|
JAVACMD="\$JAVA_HOME/jre/sh/java" |
||||||
|
else |
||||||
|
JAVACMD="\$JAVA_HOME/bin/java" |
||||||
|
fi |
||||||
|
if [ ! -x "\$JAVACMD" ] ; then |
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: \$JAVA_HOME |
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the |
||||||
|
location of your Java installation." |
||||||
|
fi |
||||||
|
else |
||||||
|
JAVACMD="java" |
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the |
||||||
|
location of your Java installation." |
||||||
|
fi |
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can. |
||||||
|
if [ "\$cygwin" = "false" -a "\$darwin" = "false" -a "\$nonstop" = "false" ] ; then |
||||||
|
MAX_FD_LIMIT=`ulimit -H -n` |
||||||
|
if [ \$? -eq 0 ] ; then |
||||||
|
if [ "\$MAX_FD" = "maximum" -o "\$MAX_FD" = "max" ] ; then |
||||||
|
MAX_FD="\$MAX_FD_LIMIT" |
||||||
|
fi |
||||||
|
ulimit -n \$MAX_FD |
||||||
|
if [ \$? -ne 0 ] ; then |
||||||
|
warn "Could not set maximum file descriptor limit: \$MAX_FD" |
||||||
|
fi |
||||||
|
else |
||||||
|
warn "Could not query maximum file descriptor limit: \$MAX_FD_LIMIT" |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock |
||||||
|
if \$darwin; then |
||||||
|
GRADLE_OPTS="\$GRADLE_OPTS \\"-Xdock:name=\$APP_NAME\\" \\"-Xdock:icon=\$APP_HOME/media/gradle.icns\\"" |
||||||
|
fi |
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java |
||||||
|
if [ "\$cygwin" = "true" -o "\$msys" = "true" ] ; then |
||||||
|
APP_HOME=`cygpath --path --mixed "\$APP_HOME"` |
||||||
|
CLASSPATH=`cygpath --path --mixed "\$CLASSPATH"` |
||||||
|
<% if ( mainClassName.startsWith('--module ') ) { %> MODULE_PATH=`cygpath --path --mixed "\$MODULE_PATH"`<% } %> |
||||||
|
JAVACMD=`cygpath --unix "\$JAVACMD"` |
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath |
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` |
||||||
|
SEP="" |
||||||
|
for dir in \$ROOTDIRSRAW ; do |
||||||
|
ROOTDIRS="\$ROOTDIRS\$SEP\$dir" |
||||||
|
SEP="|" |
||||||
|
done |
||||||
|
OURCYGPATTERN="(^(\$ROOTDIRS))" |
||||||
|
# Add a user-defined pattern to the cygpath arguments |
||||||
|
if [ "\$GRADLE_CYGPATTERN" != "" ] ; then |
||||||
|
OURCYGPATTERN="\$OURCYGPATTERN|(\$GRADLE_CYGPATTERN)" |
||||||
|
fi |
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh |
||||||
|
i=0 |
||||||
|
for arg in "\$@" ; do |
||||||
|
CHECK=`echo "\$arg"|egrep -c "\$OURCYGPATTERN" -` |
||||||
|
CHECK2=`echo "\$arg"|egrep -c "^-"` ### Determine if an option |
||||||
|
|
||||||
|
if [ \$CHECK -ne 0 ] && [ \$CHECK2 -eq 0 ] ; then ### Added a condition |
||||||
|
eval `echo args\$i`=`cygpath --path --ignore --mixed "\$arg"` |
||||||
|
else |
||||||
|
eval `echo args\$i`="\"\$arg\"" |
||||||
|
fi |
||||||
|
i=`expr \$i + 1` |
||||||
|
done |
||||||
|
case \$i in |
||||||
|
0) set -- ;; |
||||||
|
1) set -- "\$args0" ;; |
||||||
|
2) set -- "\$args0" "\$args1" ;; |
||||||
|
3) set -- "\$args0" "\$args1" "\$args2" ;; |
||||||
|
4) set -- "\$args0" "\$args1" "\$args2" "\$args3" ;; |
||||||
|
5) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" ;; |
||||||
|
6) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" ;; |
||||||
|
7) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" ;; |
||||||
|
8) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" "\$args7" ;; |
||||||
|
9) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" "\$args7" "\$args8" ;; |
||||||
|
esac |
||||||
|
fi |
||||||
|
|
||||||
|
# Escape application args |
||||||
|
save () { |
||||||
|
for i do printf %s\\\\n "\$i" | sed "s/'/'\\\\\\\\''/g;1s/^/'/;\\\$s/\\\$/' \\\\\\\\/" ; done |
||||||
|
echo " " |
||||||
|
} |
||||||
|
APP_ARGS=`save "\$@"` |
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules |
||||||
|
eval set -- -javaagent:\$APP_HOME/lib/jacocoagent.jar \$DEFAULT_JVM_OPTS \$JAVA_OPTS \$${optsEnvironmentVar} <% if ( appNameSystemProperty ) { %>"\"-D${appNameSystemProperty}=\$APP_BASE_NAME\"" <% } %>-classpath "\"\$CLASSPATH\"" <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "\"\$MODULE_PATH\"" <% } %>${mainClassName} "\$APP_ARGS" |
||||||
|
|
||||||
|
unset BESU_USING_JEMALLOC |
||||||
|
if [ "\$darwin" = "false" -a "\$msys" = "false" ]; then |
||||||
|
# check if jemalloc is available |
||||||
|
TEST_JEMALLOC=\$(LD_PRELOAD=libjemalloc.so sh -c true 2>&1) |
||||||
|
|
||||||
|
# if jemalloc is available the output is empty, otherwise the output has an error line |
||||||
|
if [ -z "\$TEST_JEMALLOC" ]; then |
||||||
|
export LD_PRELOAD=libjemalloc.so |
||||||
|
export BESU_USING_JEMALLOC=true |
||||||
|
else |
||||||
|
# jemalloc not available, as fallback limit malloc to 2 arenas |
||||||
|
export MALLOC_ARENA_MAX=2 |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
exec "\$JAVACMD" "\$@" |
@ -0,0 +1,91 @@ |
|||||||
|
@rem |
||||||
|
@rem |
||||||
|
@rem Copyright 2015 the original author or authors. |
||||||
|
@rem |
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
@rem you may not use this file except in compliance with the License. |
||||||
|
@rem You may obtain a copy of the License at |
||||||
|
@rem |
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0 |
||||||
|
@rem |
||||||
|
@rem Unless required by applicable law or agreed to in writing, software |
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
@rem See the License for the specific language governing permissions and |
||||||
|
@rem limitations under the License. |
||||||
|
@rem |
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off |
||||||
|
@rem ########################################################################## |
||||||
|
@rem |
||||||
|
@rem ${applicationName} startup script for Windows |
||||||
|
@rem |
||||||
|
@rem ########################################################################## |
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell |
||||||
|
if "%OS%"=="Windows_NT" setlocal |
||||||
|
|
||||||
|
set DIRNAME=%~dp0 |
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.\ |
||||||
|
|
||||||
|
set APP_BASE_NAME=%~n0 |
||||||
|
set APP_HOME=%DIRNAME%${appHomeRelativePath} |
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter. |
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi |
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. |
||||||
|
set DEFAULT_JVM_OPTS=${defaultJvmOpts} |
||||||
|
|
||||||
|
@rem Find java.exe |
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome |
||||||
|
|
||||||
|
set JAVA_EXE=java.exe |
||||||
|
%JAVA_EXE% -version >NUL 2>&1 |
||||||
|
if "%ERRORLEVEL%" == "0" goto execute |
||||||
|
|
||||||
|
echo. |
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. |
||||||
|
echo. |
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the |
||||||
|
echo location of your Java installation. |
||||||
|
|
||||||
|
goto fail |
||||||
|
|
||||||
|
:findJavaFromJavaHome |
||||||
|
set JAVA_HOME=%JAVA_HOME:"=% |
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe |
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute |
||||||
|
|
||||||
|
echo. |
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% |
||||||
|
echo. |
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the |
||||||
|
echo location of your Java installation. |
||||||
|
|
||||||
|
goto fail |
||||||
|
|
||||||
|
:execute |
||||||
|
@rem Setup the command line |
||||||
|
|
||||||
|
set CLASSPATH=$classpath |
||||||
|
<% if ( mainClassName.startsWith('--module ') ) { %>set MODULE_PATH=$modulePath<% } %> |
||||||
|
|
||||||
|
@rem Execute ${applicationName} |
||||||
|
"%JAVA_EXE%" -javaagent:%APP_HOME%/lib/jacocoagent.jar %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -classpath "%CLASSPATH%" <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName} %* |
||||||
|
|
||||||
|
:end |
||||||
|
@rem End local scope for the variables with windows NT shell |
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd |
||||||
|
|
||||||
|
:fail |
||||||
|
rem Set variable ${exitEnvironmentVar} if you need the _script_ return code instead of |
||||||
|
rem the _cmd.exe /c_ return code! |
||||||
|
if not "" == "%${exitEnvironmentVar}%" exit 1 |
||||||
|
exit /b 1 |
||||||
|
|
||||||
|
:mainEnd |
||||||
|
if "%OS%"=="Windows_NT" endlocal |
||||||
|
|
||||||
|
:omega |
Loading…
Reference in new issue