2020-05-29 18:26:37 +00:00
|
|
|
import os, streams, strutils, chronicles, macros, stew/ranges/ptr_arith
|
2020-05-13 11:47:13 +00:00
|
|
|
|
2020-05-13 17:17:11 +00:00
|
|
|
when not defined(windows):
|
|
|
|
import posix
|
|
|
|
|
|
|
|
# if user forget to import chronicles
|
|
|
|
# they still can compile without mysterious
|
|
|
|
# error such as "undeclared identifier: 'activeChroniclesStream'"
|
|
|
|
export chronicles
|
|
|
|
|
|
|
|
proc suicide() =
|
2020-05-13 11:47:13 +00:00
|
|
|
# For code we want to fuzz, SIGSEGV is needed on unwanted exceptions.
|
|
|
|
# However, this is only needed when fuzzing with afl.
|
2020-05-13 17:17:11 +00:00
|
|
|
when not defined(windows):
|
|
|
|
discard kill(getpid(), SIGSEGV)
|
|
|
|
else:
|
|
|
|
discard
|
|
|
|
|
|
|
|
template fuzz(body) =
|
2020-06-11 13:50:26 +00:00
|
|
|
when defined(llvmFuzzer):
|
2020-05-29 18:26:37 +00:00
|
|
|
body
|
|
|
|
else:
|
2020-05-13 11:47:13 +00:00
|
|
|
try:
|
|
|
|
body
|
|
|
|
except Exception as e:
|
|
|
|
error "Fuzzer input created exception", exception=e.name, trace=e.repr,
|
|
|
|
msg=e.msg
|
2020-05-13 17:17:11 +00:00
|
|
|
suicide()
|
2020-05-13 11:47:13 +00:00
|
|
|
|
2020-06-11 13:50:26 +00:00
|
|
|
when not defined(llvmFuzzer):
|
2020-05-13 17:17:11 +00:00
|
|
|
proc readStdin(): seq[byte] =
|
2020-05-29 18:26:37 +00:00
|
|
|
let s = if paramCount() > 0: newFileStream(paramStr(1))
|
|
|
|
else: newFileStream(stdin)
|
2020-05-13 17:17:11 +00:00
|
|
|
if s.isNil:
|
2024-01-19 09:26:33 +00:00
|
|
|
chronicles.error "Error opening input stream"
|
2020-05-13 17:17:11 +00:00
|
|
|
suicide()
|
|
|
|
# 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
|
|
|
|
result = cast[seq[byte]](input)
|
2020-05-13 11:47:13 +00:00
|
|
|
|
|
|
|
proc NimMain() {.importc: "NimMain".}
|
|
|
|
|
|
|
|
# The default init, gets redefined when init template is used.
|
|
|
|
template initImpl(): untyped =
|
2020-06-11 13:50:26 +00:00
|
|
|
when defined(llvmFuzzer):
|
2020-05-13 11:47:13 +00:00
|
|
|
proc fuzzerInit(): cint {.exportc: "LLVMFuzzerInitialize".} =
|
|
|
|
NimMain()
|
|
|
|
|
|
|
|
return 0
|
2020-05-29 18:26:37 +00:00
|
|
|
else:
|
|
|
|
discard
|
2020-05-13 11:47:13 +00:00
|
|
|
|
2020-06-01 08:10:13 +00:00
|
|
|
template init*(body: untyped) {.dirty.} =
|
2020-05-13 11:47:13 +00:00
|
|
|
## Init block to do any initialisation for the fuzzing test.
|
|
|
|
##
|
|
|
|
## For AFL this is currently only cosmetic and will be run each time, before
|
|
|
|
## the test block.
|
|
|
|
##
|
2020-06-11 13:50:26 +00:00
|
|
|
## For LLVM fuzzers this will only be run once. So only put data which is
|
|
|
|
## stateless or make sure everything gets properply reset for each new run
|
|
|
|
## in the test block.
|
|
|
|
when defined(llvmFuzzer):
|
2020-06-01 08:10:13 +00:00
|
|
|
template initImpl() {.dirty.} =
|
2020-06-01 12:09:45 +00:00
|
|
|
bind NimMain
|
|
|
|
|
2020-05-13 11:47:13 +00:00
|
|
|
proc fuzzerInit(): cint {.exportc: "LLVMFuzzerInitialize".} =
|
|
|
|
NimMain()
|
|
|
|
|
2020-06-01 08:10:13 +00:00
|
|
|
body
|
2020-05-13 11:47:13 +00:00
|
|
|
|
|
|
|
return 0
|
2020-05-29 18:26:37 +00:00
|
|
|
else:
|
2020-06-01 08:10:13 +00:00
|
|
|
template initImpl(): untyped {.dirty.} =
|
|
|
|
bind fuzz
|
|
|
|
fuzz: body
|
2020-05-13 11:47:13 +00:00
|
|
|
|
|
|
|
template test*(body: untyped): untyped =
|
|
|
|
## Test block to do the actual test that will be fuzzed in a loop.
|
|
|
|
##
|
|
|
|
## Within this test block there is access to the payload OpenArray which
|
|
|
|
## contains the payload provided by the fuzzer.
|
|
|
|
mixin initImpl
|
|
|
|
initImpl()
|
2020-06-11 13:50:26 +00:00
|
|
|
when defined(llvmFuzzer):
|
2020-05-29 18:26:37 +00:00
|
|
|
proc fuzzerCall(data: ptr byte, len: csize):
|
|
|
|
cint {.exportc: "LLVMFuzzerTestOneInput".} =
|
|
|
|
template payload(): auto =
|
|
|
|
makeOpenArray(data, len)
|
|
|
|
|
2020-06-01 08:10:13 +00:00
|
|
|
body
|
2020-05-29 18:26:37 +00:00
|
|
|
else:
|
2020-05-13 17:17:11 +00:00
|
|
|
when not defined(windows):
|
|
|
|
var payload {.inject.} = readStdin()
|
|
|
|
|
2020-06-01 08:10:13 +00:00
|
|
|
fuzz: body
|
2020-05-13 17:17:11 +00:00
|
|
|
else:
|
|
|
|
proc fuzzerCall() {.exportc: "AFLmain", dynlib, cdecl.} =
|
|
|
|
var payload {.inject.} = readStdin()
|
2020-06-01 08:10:13 +00:00
|
|
|
fuzz: body
|
2020-05-13 11:47:13 +00:00
|
|
|
|
2020-05-13 17:17:11 +00:00
|
|
|
fuzzerCall()
|
2020-05-13 11:47:13 +00:00
|
|
|
|
2020-06-11 13:50:26 +00:00
|
|
|
when defined(clangfast) and not defined(llvmFuzzer):
|
2020-05-13 11:47:13 +00:00
|
|
|
## Can be used for deferred instrumentation.
|
|
|
|
## Should be placed on a suitable location in the code where the delayed
|
|
|
|
## cloning can take place (e.g. NOT after creation of threads)
|
|
|
|
proc aflInit*() {.importc: "__AFL_INIT", noDecl.}
|
|
|
|
## Can be used for persistent mode.
|
|
|
|
## Should be used as value for controlling a loop around a test case.
|
|
|
|
## Test case should be able to handle repeated inputs. No repeated fork() will
|
|
|
|
## be done.
|
|
|
|
# TODO: Lets use this in the test block when afl-clang-fast is used?
|
|
|
|
proc aflLoopImpl(count: cuint): cint {.importc: "__AFL_LOOP", noDecl.}
|
|
|
|
template aflLoop*(body: untyped): untyped =
|
|
|
|
while aflLoopImpl(1000) != 0:
|
|
|
|
`body`
|
|
|
|
else:
|
|
|
|
proc aflInit*() = discard
|
|
|
|
template aflLoop*(body: untyped): untyped = `body`
|