Allow for fuzzing with libFuzzer

This commit is contained in:
kdeme 2019-09-03 15:45:26 +02:00 committed by zah
parent 0f020d5df8
commit 70a892fff7
3 changed files with 76 additions and 54 deletions

View File

@ -1,55 +1,25 @@
import
streams, posix, sequtils, strutils, chronicles, chronos, stew/byteutils,
eth/p2p/[discovery, kademlia, enode], eth/[keys, rlp],
../../p2p/p2p_test_helper
template fuzz(body) =
# For code we want to fuzz.
try:
body
except:
let e = getCurrentException()
debug "Fuzzer input created exception", exception=e.name, trace=e.repr
discard kill(getpid(), SIGSEGV)
template noFuzz(body) =
# For code not in the scope of the test.
# Lets not have false negatives due to possible issues in this code.
try:
body
except:
let e = getCurrentException()
debug "Exception out of scope of the fuzzing target",
exception=e.name, trace=e.repr
return
chronicles, eth/p2p/[discovery, enode], eth/[keys, rlp],
../../p2p/p2p_test_helper, ../fuzz_helpers
const DefaultListeningPort = 30303
var targetNode: DiscoveryProtocol
proc fuzzTest() =
proc init() =
# Set up a discovery node, this is the node we target when fuzzing
var
targetNodeKey = initPrivateKey("a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")
targetNodeAddr = localAddress(DefaultListeningPort)
targetNode = newDiscoveryProtocol(targetNodeKey, targetNodeAddr, @[])
# Create the transport as else replies on the messages send will fail.
targetNode.open()
proc test(payload: seq[byte]) =
var
msg: seq[byte]
address: Address
targetNode: DiscoveryProtocol
noFuzz:
# Set up a discovery node, this is the node we target with fuzzing
let
targetNodeKey = initPrivateKey("a2b50376a79b1a8c8a3296485572bdfbf54708bb46d3c25d73d2723aaaf6a617")
targetNodeAddr = localAddress(DefaultListeningPort)
targetNode = newDiscoveryProtocol(targetNodeKey, targetNodeAddr, @[])
# Create the transport as else replies on the messages send will fail.
targetNode.open()
# Read input from stdin (fastest for AFL)
let s = newFileStream(stdin)
# We use binary files as with hex we can get lots of "not hex" failures
var input = s.readAll()
s.close()
# Remove newline if it is there
input.removeSuffix
# TODO: is there a better/faster way?
let payload = input.mapIt(it.byte)
fuzz:
# Sending raw payload is possible but won't find us much. We need a hash and
# a signature, and without it there is a big chance it will always result in
# "Wrong msg mac from" error.
@ -57,12 +27,38 @@ proc fuzzTest() =
msg = packData(payload, nodeKey)
address = localAddress(DefaultListeningPort + 1)
try:
targetNode.receive(address, msg)
# These errors are also catched in `processClient` in discovery.nim
# TODO: move them a layer down in discovery so we can do a cleaner test there?
except RlpError, DiscProtocolError:
debug "Receive failed", err = getCurrentExceptionMsg()
try:
targetNode.receive(address, msg)
# These errors are also catched in `processClient` in discovery.nim
# TODO: move them a layer down in discovery so we can do a cleaner test there?
except RlpError, DiscProtocolError:
debug "Receive failed", err = getCurrentExceptionMsg()
fuzz:
fuzzTest()
proc NimMain() {.importc: "NimMain".}
proc fuzzerInit(): cint {.exportc: "LLVMFuzzerInitialize".} =
NimMain()
init()
return 0
template `+`*[T](p: ptr T, off: int): ptr T =
cast[ptr type(p[])](cast[ByteAddress](p) +% off * sizeof(p[]))
proc fuzzerCall(data: ptr byte, len: csize): cint {.exportc: "LLVMFuzzerTestOneInput".} =
if len > 0:
var input: seq[byte]
# TODO: something better to get this data in the seq?
newSeq(input, len)
for i in 0..<len:
input[i] = (data + i)[]
test(input)
return 0
when defined(afl):
init()
test(readStdin())

View File

@ -7,8 +7,7 @@ if not dirExists("generated-input"):
if not fileExists("fuzz"):
# Requires afl-gcc to be installed
# TODO: add + test option for clang
exec "nim c --cc=gcc --gcc.exe=afl-gcc --gcc.linkerexe=afl-gcc fuzz"
exec "nim c -d:afl -d:noSignalHandler --cc=gcc --gcc.exe=afl-gcc --gcc.linkerexe=afl-gcc fuzz"
if dirExists("output"):
exec "afl-fuzz -i - -o output -M fuzzer01 -- ./fuzz"
else:

View File

@ -0,0 +1,27 @@
import streams, posix, sequtils, strutils, chronicles
template fuzz*(body) =
# For code we want to fuzz, SIGSEGV is needed on unwanted exceptions.
# However, this is only needed when fuzzing with afl.
when defined(afl):
try:
body
except Exception as e:
error "Fuzzer input created exception", exception=e.name, trace=e.repr, msg=e.msg
discard kill(getpid(), SIGSEGV)
else:
body
proc readStdin*(): seq[byte] =
# Read input from stdin (fastest for AFL)
let s = newFileStream(stdin)
if s.isNil:
error "Error opening stdin"
discard kill(getpid(), SIGSEGV)
# We use binary files as with hex we can get lots of "not hex" failures
var input = s.readAll()
s.close()
# Remove newline if it is there
input.removeSuffix
# TODO: is there a better/faster way?
result = input.mapIt(it.byte)