Skip to content

Instantly share code, notes, and snippets.

@planetis-m
Last active July 14, 2022 12:14
Show Gist options
  • Save planetis-m/a157bbfcea532770ec35bfcfeddcebc0 to your computer and use it in GitHub Desktop.
Save planetis-m/a157bbfcea532770ec35bfcfeddcebc0 to your computer and use it in GitHub Desktop.
import std/random
proc quitOrDebug() {.noreturn, importc: "abort", header: "<stdlib.h>", nodecl.}
type
Fruit = enum
Apple, Banana, Orange
Bar = object
b: bool
case kind: Fruit
of Banana, Orange:
bad: float
banana: int
of Apple: apple: int
a: float
proc randBool(r: var Rand; n = 2): bool {.inline.} =
# Return true with probability about 1-of-n.
r.rand(n-1) == 0
type
Unstructured = object
data: ptr UncheckedArray[byte]
pos, len: int
proc toUnstructured*(data: ptr UncheckedArray[byte]; len: int): Unstructured =
Unstructured(data: data, pos: 0, len: len)
proc setPosition(x: var Unstructured, pos: int) =
x.pos = clamp(pos, 0, x.len)
proc readData(x: var Unstructured, buffer: pointer, bufLen: int): int =
result = min(bufLen, x.len - x.pos)
if result > 0:
copyMem(buffer, addr x.data[x.pos], result)
inc(x.pos, result)
else:
result = 0
proc writeData(x: var Unstructured, buffer: pointer, bufLen: int) =
if bufLen <= 0:
return
if x.pos + bufLen > x.len:
quitOrDebug()
copyMem(addr(x.data[x.pos]), buffer, bufLen)
inc(x.pos, bufLen)
proc read[T](x: var Unstructured, result: var T) =
if readData(x, addr(result), sizeof(T)) != sizeof(T):
quitOrDebug()
proc readBool*(x: var Unstructured): bool =
read(x, result)
proc readInt64*(x: var Unstructured): int64 =
read(x, result)
proc readFloat64*(x: var Unstructured): float64 =
read(x, result)
proc write[T](x: var Unstructured, v: T) =
writeData(x, addr v, sizeof(v))
proc write(x: var Unstructured; o: Bar) =
for v in o.fields:
write(x, v)
proc initFromBin(dest: var Bar; x: var Unstructured) =
read(x, dest.b)
{.cast(uncheckedAssign).}:
read(x, dest.kind) # seems ok since dest must be uninitialized
case dest.kind
of Banana, Orange:
read(x, dest.bad)
read(x, dest.banana)
of Apple:
read(x, dest.apple)
read(x, dest.a)
proc byteSize(o: Bar): int =
result = 0
for v in o.fields: inc result, sizeof(v)
proc mutate*(data: ptr UncheckedArray[byte], len, maxLen: int): int {.
importc: "LLVMFuzzerMutate".}
template `+!`(p: pointer, s: int): untyped =
cast[pointer](cast[ByteAddress](p) +% s)
proc mutate[T: enum and Ordinal](v: T; sizeIncreaseHint: int, r: var Rand): T =
const count = high(T).ord - low(T).ord + 1
if count <= 1: low(T)
else: T((v.ord + 1 + rand(r, count - 1)) mod count)
proc mutateVal[T](v: T): T =
result = v
let size = mutate(cast[ptr UncheckedArray[byte]](addr result), sizeof(T), sizeof(T))
zeroMem(result.addr +! size, sizeof(T) - size)
proc mutate(v: int; a: int, b: var Rand): int {.inline.} =
mutateVal(v)
proc mutate(v: float; a: int, b: var Rand): float {.inline.} =
mutateVal(v)
proc mutate(v: bool; a: int, b: var Rand): bool {.inline.} =
not v
type
Mutation = enum
None,
Switch,
Mutate,
Copy
proc mutateImpl(dest: var Bar, source: Bar; sizeIncreaseHint: int, r: var Rand) =
case r.rand(None..Copy)
of None: discard
of Switch:
var kind: Fruit
kind = mutate(kind, sizeIncreaseHint, r)
{.cast(uncheckedAssign).}:
dest.kind = kind
of Mutate:
dest.b = mutate(dest.b, sizeIncreaseHint, r)
case dest.kind
of Orange, Banana:
dest.bad = mutate(dest.bad, sizeIncreaseHint, r)
dest.banana = mutate(dest.banana, sizeIncreaseHint, r)
of Apple:
dest.apple = mutate(dest.apple, sizeIncreaseHint, r)
dest.a = mutate(dest.a, sizeIncreaseHint, r)
of Copy:
if randBool(r, 10): dest.b = source.b
if dest.kind == source.kind:
case dest.kind
of Orange, Banana:
if randBool(r, 10): dest.bad = source.bad
if randBool(r, 10): dest.banana = source.banana
of Apple:
if randBool(r, 10): dest.apple = source.apple
if randBool(r, 10): dest.a = source.a
proc customMutator*(data: ptr UncheckedArray[byte], len, maxLen: int, seed: int64): int {.
exportc: "LLVMFuzzerCustomMutator".} =
var dest: Bar
if len >= dest.byteSize: # remove this
var u = toUnstructured(data, len)
initFromBin(dest, u)
let source = dest
var gen = initRand(seed)
mutateImpl(dest, source, maxLen - dest.byteSize, gen)
result = dest.byteSize
if result <= maxLen:
var writeStr = toUnstructured(data, maxLen)
writeStr.write(dest)
else:
result = len
proc customCrossOver(data1: ptr UncheckedArray[byte], len1: int,
data2: ptr UncheckedArray[byte], len2: int, res: ptr UncheckedArray[byte],
maxResLen: int, seed: int64): int {.
exportc: "LLVMFuzzerCustomCrossOver".} =
var source: Bar
if len1 >= source.byteSize:
var u = toUnstructured(data1, len1)
initFromBin(source, u)
var dest: Bar
if len2 >= dest.byteSize:
var u = toUnstructured(data2, len2)
initFromBin(dest, u)
var gen = initRand(seed)
mutateImpl(dest, source, maxResLen - dest.byteSize, gen)
result = dest.byteSize
if result <= maxResLen:
var writeStr = toUnstructured(res, maxResLen)
writeStr.write(dest)
else:
result = 0
proc fuzzMe(s: Bar) =
if s.kind in {Banana, Orange} and s.banana == 0xdeadbeef and s.bad > 100:
echo "PANIC!"; quitOrDebug()
proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
{.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
exportc: "LLVMFuzzerTestOneInput", raises: [].} =
result = 0
var x: Bar
if len < x.byteSize: return
var u = toUnstructured(data, len)
initFromBin(x, u)
fuzzMe(x)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment