2019-10-01 10:00:20 +00:00
|
|
|
import streams, posix, strutils, chronicles, macros, stew/ranges/ptr_arith
|
2019-09-03 13:45:26 +00:00
|
|
|
|
2019-09-25 15:02:43 +00:00
|
|
|
template fuzz(body) =
|
2019-09-03 13:45:26 +00:00
|
|
|
# For code we want to fuzz, SIGSEGV is needed on unwanted exceptions.
|
|
|
|
# However, this is only needed when fuzzing with afl.
|
2019-10-11 12:46:54 +00:00
|
|
|
when defined(afl):
|
2019-09-03 13:45:26 +00:00
|
|
|
try:
|
|
|
|
body
|
|
|
|
except Exception as e:
|
2019-12-02 15:31:10 +00:00
|
|
|
error "Fuzzer input created exception", exception=e.name, trace=e.repr,
|
|
|
|
msg=e.msg
|
2019-09-03 13:45:26 +00:00
|
|
|
discard kill(getpid(), SIGSEGV)
|
|
|
|
else:
|
|
|
|
body
|
|
|
|
|
2019-10-08 14:23:57 +00:00
|
|
|
proc readStdin(): seq[byte] =
|
2019-09-03 13:45:26 +00:00
|
|
|
# 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
|
2019-09-25 15:02:43 +00:00
|
|
|
result = cast[seq[byte]](input)
|
|
|
|
|
|
|
|
proc NimMain() {.importc: "NimMain".}
|
|
|
|
|
2019-10-01 16:11:28 +00:00
|
|
|
# The default init, gets redefined when init template is used.
|
|
|
|
template initImpl(): untyped =
|
2019-10-11 12:46:54 +00:00
|
|
|
when not defined(libFuzzer):
|
2019-10-01 16:11:28 +00:00
|
|
|
discard
|
|
|
|
else:
|
|
|
|
proc fuzzerInit(): cint {.exportc: "LLVMFuzzerInitialize".} =
|
|
|
|
NimMain()
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
template init*(body: untyped) =
|
2019-10-08 14:23:57 +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.
|
|
|
|
##
|
|
|
|
## For libFuzzer 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.
|
2019-10-11 12:46:54 +00:00
|
|
|
when not defined(libFuzzer):
|
2019-10-01 16:11:28 +00:00
|
|
|
template initImpl(): untyped = fuzz: `body`
|
|
|
|
else:
|
|
|
|
template initImpl() =
|
|
|
|
proc fuzzerInit(): cint {.exportc: "LLVMFuzzerInitialize".} =
|
|
|
|
NimMain()
|
|
|
|
|
|
|
|
`body`
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
2019-09-25 15:16:35 +00:00
|
|
|
template test*(body: untyped): untyped =
|
2019-10-08 14:23:57 +00:00
|
|
|
## 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.
|
2019-10-01 16:11:28 +00:00
|
|
|
mixin initImpl
|
|
|
|
initImpl()
|
2019-10-11 12:46:54 +00:00
|
|
|
when not defined(libFuzzer):
|
2019-09-25 15:16:35 +00:00
|
|
|
var payload {.inject.} = readStdin()
|
2019-09-25 15:02:43 +00:00
|
|
|
|
2019-09-25 15:16:35 +00:00
|
|
|
fuzz: `body`
|
2019-09-25 15:02:43 +00:00
|
|
|
else:
|
2019-09-25 15:16:35 +00:00
|
|
|
proc fuzzerCall(data: ptr byte, len: csize):
|
|
|
|
cint {.exportc: "LLVMFuzzerTestOneInput".} =
|
2019-10-01 10:00:20 +00:00
|
|
|
template payload(): auto =
|
|
|
|
makeOpenArray(data, len)
|
2019-09-25 15:16:35 +00:00
|
|
|
|
|
|
|
`body`
|
2019-10-08 14:23:57 +00:00
|
|
|
|
|
|
|
when defined(clangfast):
|
|
|
|
## 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?
|
2019-10-09 19:23:22 +00:00
|
|
|
proc aflLoopImpl(count: cuint): cint {.importc: "__AFL_LOOP", noDecl.}
|
|
|
|
template aflLoop*(body: untyped): untyped =
|
|
|
|
while aflLoopImpl(1000) != 0:
|
|
|
|
`body`
|
2019-10-08 14:23:57 +00:00
|
|
|
else:
|
|
|
|
proc aflInit*() = discard
|
2019-10-09 19:23:22 +00:00
|
|
|
template aflLoop*(body: untyped): untyped = `body`
|