Add an example fuzzing test

This commit is contained in:
Zahary Karadjov 2020-05-20 15:46:35 +03:00 committed by zah
parent 4c80f523c2
commit b435f1a729
7 changed files with 161 additions and 28 deletions

View File

@ -61,6 +61,41 @@ To keep track of upstream AMCL:
- Test
- Commit
### Executing the test suite
We recomment working within the nimbus build environment described here:
https://github.com/status-im/nim-beacon-chain/
To execute the test suite, just navigate to the root of this repo and execute:
```
nimble test
```
> Please note that within the nimbus build environment, the repository will
be located in `nim-beacon-chain/vendor/nim-blscurve`.
### Executing the fuzzing tests
Before you start, please make sure that the regular test suite executes
successfully (see the instructions above). To start a particular fuzzing
test, navigate to the root of this repo and execute:
```
nim tests/fuzzing/run_fuzzing_test.nims <test-name>
```
You can specify the fuzzing engine being used by passing an additional
`--fuzzer` parameter. The currently supported engines are `libFuzzer`
(used by default) and `afl`.
All fuzzing tests are located in `tests/fuzzing` and use the following
naming convention:
```
fuzz_<test-name>.nim
```
## License
Licensed and distributed under either of

View File

@ -12,28 +12,19 @@
import
# Standard library
json, strutils, os, streams, unittest,
# Third party
yaml,
json, strutils, os, unittest,
# Status libraries
stew/byteutils,
# Public API
../blscurve
../blscurve,
# Test helpers
./test_locator
const ETH2_DIR = currentSourcePath.rsplit(DirSep, 1)[0] / "eth2.0_v0.10.1_vectors"
type InOut = enum
Input
Output
proc parseTest(file: string): JsonNode =
var yamlStream = openFileStream(file)
defer: yamlStream.close()
result = yamlStream.loadToJson()[0]
const SkippedTests = [
"small"/"fast_aggregate_verify_e6922a0d196d9869"/"data.yaml", # Buggy upstream vector: https://github.com/ethereum/eth2.0-specs/issues/1618
"small"/"fast_aggregate_verify_62bca7cd61880e26"/"data.yaml",
"small"/"fast_aggregate_verify_3b2b0141e95125f0"/"data.yaml",
]
template testGen(name, testJson, body: untyped): untyped =
template testGen*(name, testJson, body: untyped): untyped =
## Generates a test proc
## with identifier "test_name"
## The test file is availaible as JsonNode under the
@ -41,14 +32,9 @@ template testGen(name, testJson, body: untyped): untyped =
proc `test _ name`() =
var count = 0 # Need to fail if walkDir doesn't return anything
var skipped = 0
const testDir = ETH2_DIR / astToStr(name)
for file in walkDirRec(testDir, relative = true):
if file in SkippedTests:
echo "[WARNING] Skipping - ", file
inc skipped
continue
for dir, file in walkTests(astToStr(name), skipped):
echo " ", astToStr(name), " test: ", file
let testJson = parseTest(testDir / file)
let testJson = parseTest(dir / file)
body
@ -58,10 +44,6 @@ template testGen(name, testJson, body: untyped): untyped =
if skipped > 0:
echo "[Warning]: ", skipped, " tests skipped."
type InOut = enum
Input
Output
proc getFrom(T: typedesc, test: JsonNode, inout: static InOut): T =
when inout == Output:
when T is bool:

View File

@ -0,0 +1,34 @@
import
stew/byteutils,
../test_locator,
fuzzing_assumptions
var skipped = 0
let corpusDir = getAppDir() / "corpus"
removeDir corpusDir
template getInputBytes(test: JsonNode, fieldName: string): seq[byte] =
test["input"][fieldName].getStr.hexToSeqByte
var inputIdx = 0
template nextInput: string =
inc inputIdx
"input" & $inputIdx
let verifyCorpusDir = corpusDir / "verify"
createDir verifyCorpusDir
for dir, test in walkTests("verify", skipped):
let t = parseTest(dir / test)
let
message = t.getInputBytes "message"
pubKey = t.getInputBytes "pubkey"
signature = t.getInputBytes "signature"
doAssert pubKey.len == fuzzing_assumptions.pubkeyLen and
signature.len == fuzzing_assumptions.signatureLen
writeFile(verifyCorpusDir / nextInput(), message & pubkey & signature)

View File

@ -0,0 +1,23 @@
import
testutils/fuzzing, stew/byteutils,
../../blscurve, fuzzing_assumptions
test:
block:
if payload.len < pubkeyLen + signatureLen:
break
let
signatureStart = payload.len - signatureLen
pubkeyStart = signatureStart - pubkeyLen
var sig: Signature
if not sig.fromBytes(payload[signatureStart ..< (signatureStart + signatureLen)]):
break
var pubKey: PublicKey
if not pubKey.fromBytes(payload[pubkeyStart ..< (pubkeyStart + pubkeyLen)]):
break
discard pubKey.verify(payload[0 ..< pubkeyStart], sig)

View File

@ -0,0 +1,4 @@
const
pubkeyLen* = 48
signatureLen* = 96

View File

@ -0,0 +1,22 @@
import strformat
import os except paramCount, paramStr, fileExists # these are also defined in the system module
import confutils, testutils/fuzzing_engines
cli do (testName {.argument.}: string,
fuzzer = libFuzzer):
let
fuzzingDir = thisDir()
fuzzingFile = fuzzingDir / "fuzz_" & addFileExt(testName, "nim")
corpusDir = fuzzingDir / "corpus" / testName
if not fileExists(fuzzingFile):
echo testName, " is not a recognized fuzzing test"
quit 1
let
collectCorpusNim = fuzzingDir / "collect_corpus.nim"
fuzzNims = fuzzingDir / ".." / ".." / ".." / "nim-testutils" / "testutils" / "fuzzing" / "fuzz.nims"
exec &"""nim c -r "{collectCorpusNim}""""
exec &"""nim "{fuzzNims}" {fuzzer} "{fuzzingFile}" "{corpusDir}" """

33
tests/test_locator.nim Normal file
View File

@ -0,0 +1,33 @@
import
# Standard library
json, strutils, os, streams,
# Third party
yaml
export
os, json
const ETH2_DIR = currentSourcePath.rsplit(DirSep, 1)[0] / "eth2.0_v0.10.1_vectors"
proc parseTest*(file: string): JsonNode =
var yamlStream = openFileStream(file)
defer: yamlStream.close()
result = yamlStream.loadToJson()[0]
const SkippedTests = [
"small"/"fast_aggregate_verify_e6922a0d196d9869"/"data.yaml", # Buggy upstream vector: https://github.com/ethereum/eth2.0-specs/issues/1618
"small"/"fast_aggregate_verify_62bca7cd61880e26"/"data.yaml",
"small"/"fast_aggregate_verify_3b2b0141e95125f0"/"data.yaml",
]
iterator walkTests*(category: string, skipped: var int): (string, string) =
let testDir = ETH2_DIR / category
for file in walkDirRec(testDir, relative = true):
if file in SkippedTests:
echo "[WARNING] Skipping - ", file
inc skipped
continue
yield (testDir, file)