mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-08 20:47:59 -05:00
EOF Differential Layout Fuzzer (#7488)
Differential EOF Layout Fuzzer guided by Besu's layout parser. Signed-off-by: Danno Ferrin <danno@numisight.com>
This commit is contained in:
@@ -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()
|
||||
|
||||
|
||||
@@ -36,8 +36,6 @@ import java.io.InputStreamReader;
|
||||
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;
|
||||
@@ -175,12 +173,8 @@ public class CodeValidateSubCommand implements Runnable {
|
||||
((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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ class CodeValidationSubCommandTest {
|
||||
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
|
||||
@@ -70,9 +70,9 @@ class CodeValidationSubCommandTest {
|
||||
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
|
||||
""");
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ class CodeValidationSubCommandTest {
|
||||
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
|
||||
@@ -112,9 +112,9 @@ class CodeValidationSubCommandTest {
|
||||
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
|
||||
""");
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ class CodeValidationSubCommandTest {
|
||||
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
|
||||
@@ -139,7 +139,7 @@ class CodeValidationSubCommandTest {
|
||||
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
|
||||
@@ -153,9 +153,9 @@ class CodeValidationSubCommandTest {
|
||||
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
|
||||
""");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"cli": [
|
||||
"code-validate"
|
||||
],
|
||||
"stdin": "0xef0001010004020001000b0300010014040004000080000436600060ff6000ec005000ef000101000402000100010400000000800000feda7ac0de",
|
||||
"stdout": "OK 1/1/4\n"
|
||||
}
|
||||
@@ -3,5 +3,5 @@
|
||||
"code-validate"
|
||||
],
|
||||
"stdin": "ef00010100040200010001040000000080000000",
|
||||
"stdout": "OK 00\n"
|
||||
"stdout": "OK 1/0/0\n"
|
||||
}
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -151,11 +151,11 @@ public class GeneralStateReferenceTestTools {
|
||||
.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);
|
||||
|
||||
@@ -216,7 +216,7 @@ public class StandardJsonTracer implements OperationTracer {
|
||||
}
|
||||
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("\"");
|
||||
|
||||
@@ -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"/>
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
@@ -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') {
|
||||
@@ -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'
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,5 +68,6 @@ include 'privacy-contracts'
|
||||
include 'services:kvstore'
|
||||
include 'services:pipeline'
|
||||
include 'services:tasks'
|
||||
include 'testfuzz'
|
||||
include 'testutil'
|
||||
include 'util'
|
||||
|
||||
29
testfuzz/README.md
Normal file
29
testfuzz/README.md
Normal file
@@ -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.
|
||||
148
testfuzz/build.gradle
Normal file
148
testfuzz/build.gradle
Normal file
@@ -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);
|
||||
}
|
||||
239
testfuzz/src/main/java/org/hyperledger/besu/testfuzz/Fuzzer.java
Normal file
239
testfuzz/src/main/java/org/hyperledger/besu/testfuzz/Fuzzer.java
Normal file
@@ -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()};
|
||||
}
|
||||
}
|
||||
200
testfuzz/src/main/scripts/unixStartScript.txt
Normal file
200
testfuzz/src/main/scripts/unixStartScript.txt
Normal file
@@ -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" "\$@"
|
||||
91
testfuzz/src/main/scripts/windowsStartScript.txt
Normal file
91
testfuzz/src/main/scripts/windowsStartScript.txt
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user