diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d1881b..708f8c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -237,3 +237,4 @@ jobs: nimble install -y --depsOnly nimble test nimble build_dcli + nimble build_fuzzers diff --git a/eth.nimble b/eth.nimble index 50e1f42..85d801a 100644 --- a/eth.nimble +++ b/eth.nimble @@ -90,3 +90,16 @@ task test_discv5_full, "Run discovery v5 and its dependencies tests": task build_dcli, "Build dcli": buildBinary("eth/p2p/discoveryv5/dcli") + +import os, strutils + +task build_fuzzers, "Build fuzzer test cases": + # This file is there to be able to quickly build the fuzzer test cases in + # order to avoid bit rot (e.g. for CI). Not for actual fuzzing. + # TODO: Building fuzzer test case one by one will make it take a bit longer, + # but we cannot import them in one Nim file due to the usage of + # `exportc: "AFLmain"` in the fuzzing test template for Windows: + # https://github.com/status-im/nim-testutils/blob/master/testutils/fuzzing.nim#L100 + for file in walkDirRec("tests/fuzzing/"): + if file.endsWith("nim"): + buildBinary(file) diff --git a/eth/p2p/blockchain_utils.nim b/eth/p2p/blockchain_utils.nim index 0caf9e3..064de96 100644 --- a/eth/p2p/blockchain_utils.nim +++ b/eth/p2p/blockchain_utils.nim @@ -6,7 +6,7 @@ # at your option. This file may not be copied, modified, or distributed except according to those terms. import - eth/common/[eth_types, state_accessors] + ../common/[eth_types, state_accessors] # TODO: Perhaps we can move this to eth-common diff --git a/tests/fuzzing/discovery/fuzz.nim b/tests/fuzzing/discovery/fuzz.nim index 35b8a11..8d816ed 100644 --- a/tests/fuzzing/discovery/fuzz.nim +++ b/tests/fuzzing/discovery/fuzz.nim @@ -1,11 +1,18 @@ import - testutils/fuzzing, chronicles, - eth/p2p/[discovery, enode], eth/[keys, rlp], + testutils/fuzzing, chronicles, nimcrypto/keccak, + ../../../eth/p2p/[discovery, enode], ../../../eth/[keys, rlp], ../../p2p/p2p_test_helper const DefaultListeningPort = 30303 var targetNode: DiscoveryProtocol +proc packData(payload: openArray[byte], pk: PrivateKey): seq[byte] = + let + payloadSeq = @payload + signature = @(pk.sign(payload).toRaw()) + msgHash = keccak256.digest(signature & payloadSeq) + result = @(msgHash.data) & signature & payloadSeq + init: # Set up a discovery node, this is the node we target when fuzzing var diff --git a/tests/fuzzing/discovery/generate.nim b/tests/fuzzing/discovery/generate.nim index f0c1911..c51bc72 100644 --- a/tests/fuzzing/discovery/generate.nim +++ b/tests/fuzzing/discovery/generate.nim @@ -1,17 +1,13 @@ import - chronos, times, stew/byteutils, stint, chronicles, streams, nimcrypto, os, - strformat, strutils, eth/p2p/[discovery, kademlia], eth/[keys, rlp], - ../../p2p/p2p_test_helper + std/[times, os, strformat, strutils], + chronos, stew/byteutils, stint, chronicles, nimcrypto, + ../../../eth/p2p/[discovery, kademlia], ../../../eth/[keys, rlp], + ../../p2p/p2p_test_helper, + ../fuzzing_helpers template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0] const inputsDir = &"{sourceDir}{DirSep}generated-input{DirSep}" -proc toFile(data: Bytes, fn: string) = - var s = newFileStream(fn, fmWrite) - for x in data: - s.write(x) - s.close() - const EXPIRATION = 3600 * 24 * 365 * 10 proc expiration(): uint32 = uint32(epochTime() + EXPIRATION) @@ -32,7 +28,7 @@ proc generate() = # valid data for a Pong packet block: - let token = keccak256.digest(@[0]) + let token = keccak256.digest(@[byte 0]) let payload = rlp.encode((toAddr, token , expiration())) let encodedData = @[2.byte] & payload debug "Pong", data=byteutils.toHex(encodedData) @@ -42,9 +38,9 @@ proc generate() = # valid data for a FindNode packet block: var data: array[64, byte] - data[32 .. ^1] = peerKey.toPublicKey().tryGet().toNodeId().toByteArrayBE() + data[32 .. ^1] = peerKey.toPublicKey().toNodeId().toByteArrayBE() let payload = rlp.encode((data, expiration())) - let encodedData = @[3.byte] & payload.toSeq() + let encodedData = @[3.byte] & @payload debug "FindNode", data=byteutils.toHex(encodedData) encodedData.toFile(inputsDir & "findnode") @@ -62,11 +58,11 @@ proc generate() = type Neighbour = tuple[ip: IpAddress, udpPort, tcpPort: Port, pk: PublicKey] var nodes = newSeqOfCap[Neighbour](2) - nodes.add((n1Addr.ip, n1Addr.udpPort, n1Addr.tcpPort, n1Key.toPublicKey().tryGet())) - nodes.add((n2Addr.ip, n2Addr.udpPort, n2Addr.tcpPort, n2Key.toPublicKey().tryGet())) + nodes.add((n1Addr.ip, n1Addr.udpPort, n1Addr.tcpPort, n1Key.toPublicKey())) + nodes.add((n2Addr.ip, n2Addr.udpPort, n2Addr.tcpPort, n2Key.toPublicKey())) let payload = rlp.encode((nodes, expiration())) - let encodedData = @[4.byte] & payload.toSeq() + let encodedData = @[4.byte] & @payload debug "Neighbours", data=byteutils.toHex(encodedData) encodedData.toFile(inputsDir & "neighbours") diff --git a/tests/fuzzing/discoveryv5/fuzz_decode_message.nim b/tests/fuzzing/discoveryv5/fuzz_decode_message.nim index 5f8de42..e9ca520 100644 --- a/tests/fuzzing/discoveryv5/fuzz_decode_message.nim +++ b/tests/fuzzing/discoveryv5/fuzz_decode_message.nim @@ -1,6 +1,6 @@ import - testutils/fuzzing, stew/byteutils, - eth/rlp, eth/p2p/discoveryv5/[encoding, messages] + testutils/fuzzing, + ../../../eth/rlp, ../../../eth/p2p/discoveryv5/[encoding, messages] test: block: diff --git a/tests/fuzzing/discoveryv5/fuzz_decode_packet.nim b/tests/fuzzing/discoveryv5/fuzz_decode_packet.nim index 4be0be7..c320f4c 100644 --- a/tests/fuzzing/discoveryv5/fuzz_decode_packet.nim +++ b/tests/fuzzing/discoveryv5/fuzz_decode_packet.nim @@ -1,6 +1,6 @@ import - testutils/fuzzing, stew/shims/net, stew/byteutils, - eth/p2p/discoveryv5/[encoding, enr, sessions, node] + testutils/fuzzing, stew/shims/net, + ../../../eth/p2p/discoveryv5/[encoding, enr, sessions, node] init: const diff --git a/tests/fuzzing/enr/fuzz_enr.nim b/tests/fuzzing/enr/fuzz_enr.nim index e17a844..2c56d9f 100644 --- a/tests/fuzzing/enr/fuzz_enr.nim +++ b/tests/fuzzing/enr/fuzz_enr.nim @@ -1,6 +1,6 @@ import testutils/fuzzing, stew/byteutils, - eth/rlp, eth/p2p/discoveryv5/enr + ../../../eth/rlp, ../../../eth/p2p/discoveryv5/enr test: block: diff --git a/tests/fuzzing/enr/generate.nim b/tests/fuzzing/enr/generate.nim index f39b4d5..c2c3131 100644 --- a/tests/fuzzing/enr/generate.nim +++ b/tests/fuzzing/enr/generate.nim @@ -1,7 +1,7 @@ import std/[os, strutils, options], stew/shims/net, - eth/keys, eth/p2p/discoveryv5/enr, + ../../../eth/keys, ../../../eth/p2p/discoveryv5/enr, ../fuzzing_helpers template sourceDir: string = currentSourcePath.rsplit(DirSep, 1)[0] @@ -12,7 +12,7 @@ proc generate() = rng = newRng() privKey = PrivateKey.random(rng[]) ip = some(ValidIpAddress.init("127.0.0.1")) - port = Port(20301) + port = some(Port(20301)) block: let record = enr.Record.init(1, privKey, ip, port, port)[] diff --git a/tests/fuzzing/fuzzing_helpers.nim b/tests/fuzzing/fuzzing_helpers.nim index 1ba2700..dc9d8bd 100644 --- a/tests/fuzzing/fuzzing_helpers.nim +++ b/tests/fuzzing/fuzzing_helpers.nim @@ -1,5 +1,5 @@ import - std/streams + std/streams proc toFile*(data: seq[byte], fn: string) = var s = newFileStream(fn, fmWrite) diff --git a/tests/fuzzing/rlp/rlp_decode.nim b/tests/fuzzing/rlp/rlp_decode.nim index 5b7d39e..83561b6 100644 --- a/tests/fuzzing/rlp/rlp_decode.nim +++ b/tests/fuzzing/rlp/rlp_decode.nim @@ -1,6 +1,6 @@ import testutils/fuzzing, chronicles, - eth/rlp + ../../../eth/rlp type TestEnum = enum diff --git a/tests/fuzzing/rlp/rlp_inspect.nim b/tests/fuzzing/rlp/rlp_inspect.nim index 64c9657..fb2f266 100644 --- a/tests/fuzzing/rlp/rlp_inspect.nim +++ b/tests/fuzzing/rlp/rlp_inspect.nim @@ -1,6 +1,6 @@ import testutils/fuzzing, chronicles, - eth/rlp + ../../../eth/rlp test: try: diff --git a/tests/fuzzing/rlpx/thunk.nim b/tests/fuzzing/rlpx/thunk.nim index d418ce4..c050acd 100644 --- a/tests/fuzzing/rlpx/thunk.nim +++ b/tests/fuzzing/rlpx/thunk.nim @@ -1,8 +1,8 @@ import testutils/fuzzing, chronos, - eth/p2p, eth/p2p/rlpx, eth/p2p/private/p2p_types, - eth/p2p/rlpx_protocols/[whisper_protocol, eth_protocol], - ../p2p/p2p_test_helper + ../../../eth/p2p, ../../../eth/p2p/rlpx, ../../../eth/p2p/private/p2p_types, + ../../../eth/p2p/rlpx_protocols/[whisper_protocol, eth_protocol], + ../../p2p/p2p_test_helper var node1: EthereumNode diff --git a/tests/fuzzing/whisper/encode_decode.nim b/tests/fuzzing/whisper/encode_decode.nim index cc8004a..2a056d6 100644 --- a/tests/fuzzing/whisper/encode_decode.nim +++ b/tests/fuzzing/whisper/encode_decode.nim @@ -1,13 +1,15 @@ import - options, sequtils, chronicles, - eth/p2p/rlpx_protocols/whisper_protocol as whisper, - ../fuzztest + std/[options, sequtils], + chronicles, testutils/fuzzing, + ../../../eth/p2p/rlpx_protocols/whisper_protocol as whisper, + ../../../eth/keys test: let + rng = newRng() data = @payload.distribute(2) whisperPayload = Payload(payload: data[0], padding: some(data[1])) - encoded = whisper.encode(whisperPayload) + encoded = whisper.encode(rng[], whisperPayload) decoded = whisper.decode(encoded.get())