Initial commit

This commit is contained in:
Antonis Geralis 2022-08-25 23:18:38 +03:00
commit 020f294416
33 changed files with 1595 additions and 0 deletions

1
README.md Normal file
View File

@ -0,0 +1 @@

117
benchmarks/bench_graph.nim Normal file
View File

@ -0,0 +1,117 @@
# Compile with: nim c -d:danger -d:fuzzerStandalone bench_graph.nim
# Then run: perf record -e cycles:pp --call-graph dwarf ./bench_graph
include examples/fuzz_graph
const
corpus = [
"\xf1\x03\x00\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00\x00\x60\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x5D\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x3A\x00\x00\x00\x00",
"\xf1\x04\x00\x00\x00\x10\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x3B\x00\x00\x00\x00",
"\xf1\x05\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x41\x00\x00\x00\x00\x43\x00\x00\x00\x00\x7D\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\xC8\x00\x00\x00\x00\xAE\x00\x00\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x9C\x00\x00\x00\x00\x20\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x70\x00\x00\x00\x00\x2E\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x6F\x00\x00\x00\x00\x2C\x00\x00\x00\x00",
"\xf1\x05\x00\x00\x00\x03\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x2A\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x04\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x03\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x40\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x2C\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x04\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x2E\x00\x00\x00\x00\xFD\x00\x00\x00\x00\x40\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x34\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x92\x00\x00\x00\x00\x2C\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x20\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\x3F\x00\x00\x00\x00\x24\x00\x00\x00\x00\x40\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x2C\x00\x00\x00\x00",
"\xf1\x07\x00\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x40\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x08\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x8E\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x09\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x88\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x02\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x05\x00\x00\x00\x00\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x29\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x64\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\xA7\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x64\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x03\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\x2B\x00\x00\x00\x00\x60\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x64\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x9C\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x2E\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x04\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x2F\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\x2B\x00\x00\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x64\x01\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x04\x00\x00\x00\x2B\x00\x00\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x05\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\xC8\x00\x00\x00\x00",
"\xf1\x04\x00\x00\x00\x3D\x00\x00\x00\x00\x3F\x00\x00\x00\x00\x01\x00\x00\x00\x00\x03\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x10\x00\x00\x00\x00",
"\xf1\x06\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\xFD\x00\x00\x00\x00\x49\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x05\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x49\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x05\x00\x00\x00\x3F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x26\x00\x00\x00\x00\xFD\x00\x00\x00\x00\x40\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x5B\x00\x00\x00\x00",
"\xf1\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x64\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x80\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x64\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x78\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x04\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x49\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x13\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\xB2\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x2E\x00\x00\x00\x00\xFD\x00\x00\x00\x00\x40\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x64\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x9C\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\xB2\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x64\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\xE5\x00\x00\x00\x00",
"\xf1\x04\x00\x00\x00\x2B\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x01\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x02\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x02\x00\x00\x00\x00",
"\xf1\x04\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x2A\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x7B\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3D\x00\x00\x00\x00\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x41\x00\x00\x00\x00\x03\x00\x00\x00\x00\x8D\x00\x00\x00\x00\x7D\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x25\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x60\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x10\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\xFD\x00\x00\x00\x00",
"\xf1\x07\x00\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x80\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x40\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x4E\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\x04\x00\x00\x00\x00\x02\x00\x00\x00\x00\x01\x00\x00\x00\x00",
"\xf1\x05\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x2A\x01\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x01\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x40\x00\x00\x00\x00\x25\x00\x00\x00\x00\x2E\x00\x00\x00\x00\xF9\x00\x00\x00\x00\x40\x00\x00\x00\x00",
"\xf1\x05\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x80\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x40\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x80\x02\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x04\x00\x00\x00\x2D\x00\x00\x00\x00\x20\x00\x00\x00\x00\xFF\x00\x00\x00\x00\x01\x00\x00\x00\x00",
"\xf1\x06\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xE8\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\x25\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x2C\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x30\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\xC8\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x08\x00\x00\x00\x3F\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\xC8\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x64\x00\x00\x00\x00\x9C\x00\x00\x00\x00\xB2\x00\x00\x00\x00\x2E\x00\x00\x00\x00\x78\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x31\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x16\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x40\x00\x00\x00\x00",
"\xf1\x03\x00\x00\x00\x00\x02\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00",
"\xf1\x01\x00\x00\x00\x2D\x00\x00\x00\x00",
"\xf1\x02\x00\x00\x00\x13\x00\x00\x00\x00\x10\x00\x00\x00\x00"
]
proc benchFuzzTarget =
var buf = newString(4096)
for i in 1..1000:
for x in corpus.items:
buf.setLen(x.len)
copyMem(cstring(buf), cstring(x), x.len)
discard LLVMFuzzerCustomMutator(cast[ptr UncheckedArray[byte]](cstring(buf)), buf.len, 4096, 1600568261)
benchFuzzTarget()

View File

@ -0,0 +1 @@
--define: runFuzzTests

13
benchmarks/nim.cfg Normal file
View File

@ -0,0 +1,13 @@
--cc: clang
--define: useMalloc
@if not fuzzerStandalone:
--noMain: on
--define: noSignalHandler
--passC: "-fsanitize=fuzzer"
--passL: "-fsanitize=fuzzer"
@end
--passC: "-fsanitize=address,undefined"
--passL: "-fsanitize=address,undefined"
#--define: release
--debugger: native
--path: "../"

2
drchaos.nim Normal file
View File

@ -0,0 +1,2 @@
import drchaos/mutator
export defaultMutator

51
drchaos.nimble Normal file
View File

@ -0,0 +1,51 @@
mode = ScriptMode.Verbose
version = "0.1.0"
author = "Dr. Chaos Team"
description = "Library for structured fuzzing for Nim"
license = "MIT"
srcDir = "."
skipDirs = @["tests", "benchmarks", "examples"]
requires "nim >= 1.4.0"
proc buildBinary(name: string, srcDir = "./", params = "", lang = "c") =
if not dirExists "build":
mkDir "build"
# allow something like "nim nimbus --verbosity:0 --hints:off nimbus.nims"
var extra_params = params
when compiles(commandLineParams):
for param in commandLineParams:
extra_params &= " " & param
else:
for i in 2..<paramCount():
extra_params &= " " & paramStr(i)
exec "nim " & lang & " --out:build/" & name & " " & extra_params & " " & srcDir & name & ".nim"
proc test(name: string, srcDir = "tests/", lang = "c") =
buildBinary name, srcDir, "--mm:arc -d:danger"
withDir("build/"):
exec name & " -error_exitcode=0 -max_total_time=5 -runs=10000"
task testDrChaosExamples, "Build & run Dr. Chaos examples":
let examples = @["fuzz_graph", "fuzz_tree"]
for ex in examples:
test ex, "examples/"
task testDrChaos, "Build & run Dr. Chaos tests":
for filePath in listFiles("tests/"):
if filePath[^4..^1] == ".nim":
test filePath[len("tests/")..^5]
task testDrChaosTimed, "Build & run Dr. Chaos time limited tests":
for filePath in listFiles("tests/time_limited/"):
if filePath[^4..^1] == ".nim":
test filePath[len("tests/time_limited/")..^5], "tests/time_limited/"
#task test, "Run basic tests":
#testDrChaosTask()
task testAll, "Run all tests":
testDrChaosTask()
testDrChaosTimedTask()

401
drchaos/common.nim Normal file
View File

@ -0,0 +1,401 @@
# Procedures dealing with serialization from/to libFuzzer's input buffer. Since the custom
# mutator is in control of the process, there should be no errors. And if there are, they
# should be fatal and the code should be fixed. User may also run the fuzzer without any
# sanitizer, which means that errors should always be detected.
import std/[options, tables, sets, macros]
from typetraits import supportsCopyMem, distinctBase
template getFieldValue(mFunc, tmpSym, fieldSym) =
mFunc(tmpSym.fieldSym)
template getKindValue(mFunc, tmpSym, kindSym) =
var kindTmp = tmpSym.kindSym
mFunc(kindTmp)
{.cast(uncheckedAssign).}:
tmpSym.kindSym = kindTmp
proc foldObjectBody(tmpSym, typeNode, mFunc: NimNode): NimNode =
case typeNode.kind
of nnkEmpty:
result = newNimNode(nnkNone)
of nnkRecList:
result = newStmtList()
for it in typeNode:
let x = foldObjectBody(tmpSym, it, mFunc)
if x.kind != nnkNone: result.add x
of nnkIdentDefs:
expectLen(typeNode, 3)
let fieldSym = typeNode[0]
result = getAst(getFieldValue(mFunc, tmpSym, fieldSym))
of nnkRecCase:
let kindSym = typeNode[0][0]
result = newStmtList(getAst(getKindValue(mFunc, tmpSym, kindSym)))
let inner = nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym))
for i in 1..<typeNode.len:
let x = foldObjectBody(tmpSym, typeNode[i], mFunc)
if x.kind != nnkNone: inner.add x
result.add inner
of nnkOfBranch, nnkElse:
result = copyNimNode(typeNode)
for i in 0..typeNode.len-2:
result.add copyNimTree(typeNode[i])
let inner = newNimNode(nnkStmtListExpr)
let x = foldObjectBody(tmpSym, typeNode[^1], mFunc)
if x.kind != nnkNone: inner.add x
result.add inner
of nnkObjectTy:
expectKind(typeNode[0], nnkEmpty)
expectKind(typeNode[1], {nnkEmpty, nnkOfInherit})
result = newNimNode(nnkNone)
if typeNode[1].kind == nnkOfInherit:
let base = typeNode[1][0]
var impl = getTypeImpl(base)
while impl.kind in {nnkRefTy, nnkPtrTy}:
impl = getTypeImpl(impl[0])
result = foldObjectBody(tmpSym, impl, mFunc)
let body = typeNode[2]
let x = foldObjectBody(tmpSym, body, mFunc)
if result.kind != nnkNone:
if x.kind != nnkNone:
for i in 0..<result.len: x.add(result[i])
result = x
else: result = x
else:
error("unhandled kind: " & $typeNode.kind, typeNode)
macro assignObjectImpl*(output, mFunc: typed): untyped =
## This macro is used for safely mutating object fields with `mFunc`.
## For case discriminators it makes a temporary and assigns it inside a
## cast uncheckedAssign section. This ensures a =destroy call is generated.
let typeSym = getTypeInst(output)
result = newStmtList()
let x = foldObjectBody(output, typeSym.getTypeImpl, mFunc)
if x.kind != nnkNone: result.add x
type
EncodingDefect = object of Defect
DecodingDefect = object of Defect
proc raiseEncoding() {.noinline, noreturn.} =
raise newException(EncodingDefect, "Can't write bytes to buffer.")
proc raiseDecoding() {.noinline, noreturn.} =
raise newException(DecodingDefect, "Can't read bytes from buffer.")
proc equals*(a, b: openArray[byte]): bool =
if a.len != b.len:
result = false
else: result = equalMem(addr a, addr b, a.len)
proc byteSize*(x: string): int {.inline.}
proc byteSize*[S, T](x: array[S, T]): int {.inline.}
proc byteSize*[T](x: seq[T]): int {.inline.}
proc byteSize*[T](o: SomeSet[T]): int {.inline.}
proc byteSize*[K, V](o: (Table[K, V]|OrderedTable[K, V])): int {.inline.}
proc byteSize*[T](o: ref T): int {.inline.}
proc byteSize*[T](o: Option[T]): int {.inline.}
proc byteSize*[T: tuple](o: T): int {.inline.}
proc byteSize*[T: object](o: T): int {.inline.}
proc byteSize*[T: distinct](x: T): int {.inline.}
proc byteSize*(x: bool): int {.inline.} = sizeof(x)
proc byteSize*(x: char): int {.inline.} = sizeof(x)
proc byteSize*[T: SomeNumber](x: T): int {.inline.} = sizeof(x)
proc byteSize*[T: enum](x: T): int {.inline.} = sizeof(x)
proc byteSize*[T](x: set[T]): int {.inline.} = sizeof(x)
proc byteSize*(x: string): int = sizeof(int32) + x.len
proc byteSize*[S, T](x: array[S, T]): int =
when supportsCopyMem(T):
result = sizeof(x)
else:
result = 0
for elem in x.items: result.inc byteSize(elem)
proc byteSize*[T](x: seq[T]): int =
when supportsCopyMem(T):
result = sizeof(int32) + x.len * sizeof(T)
else:
result = sizeof(int32)
for elem in x.items: result.inc byteSize(elem)
proc byteSize*[T](o: SomeSet[T]): int =
result = sizeof(int32)
for elem in o.items: result.inc byteSize(elem)
proc byteSize*[K, V](o: (Table[K, V]|OrderedTable[K, V])): int =
result = sizeof(int32)
for k, v in o.pairs:
result.inc byteSize(k)
result.inc byteSize(v)
proc byteSize*[T](o: ref T): int =
result = sizeof(bool)
if o != nil: result.inc byteSize(o[])
proc byteSize*[T](o: Option[T]): int =
result = sizeof(bool)
if isSome(o): result.inc byteSize(get(o))
proc byteSize*[T: tuple](o: T): int =
when supportsCopyMem(T):
result = sizeof(o)
else:
result = 0
for v in o.fields: result.inc byteSize(v)
proc byteSize*[T: object](o: T): int =
when supportsCopyMem(T):
result = sizeof(o)
else:
result = 0
for v in o.fields: result.inc byteSize(v)
proc byteSize*[T: distinct](x: T): int = byteSize(x.distinctBase)
proc writeData*(data: var openArray[byte], pos: var int, buffer: pointer, bufLen: int) =
if bufLen <= 0:
return
if pos + bufLen > data.len:
raiseEncoding()
else:
copyMem(data[pos].addr, buffer, bufLen)
inc(pos, bufLen)
proc write*[T](data: var openArray[byte], pos: var int, input: T) =
writeData(data, pos, input.unsafeAddr, sizeof(input))
proc readData*(data: openArray[byte], pos: var int, buffer: pointer, bufLen: int): int =
result = min(bufLen, data.len - pos)
if result > 0:
copyMem(buffer, data[pos].addr, result)
inc(pos, result)
else:
result = 0
proc read*[T](data: openArray[byte], pos: var int, output: var T) =
if readData(data, pos, output.addr, sizeof(output)) != sizeof(output):
raiseDecoding()
proc readChar*(data: openArray[byte], pos: var int): char {.inline.} =
read(data, pos, result)
proc readBool*(data: openArray[byte], pos: var int): bool {.inline.} =
read(data, pos, result)
proc readInt8*(data: openArray[byte], pos: var int): int8 {.inline.} =
read(data, pos, result)
proc readInt16*(data: openArray[byte], pos: var int): int16 {.inline.} =
read(data, pos, result)
proc readInt32*(data: openArray[byte], pos: var int): int32 {.inline.} =
read(data, pos, result)
proc readInt64*(data: openArray[byte], pos: var int): int64 {.inline.} =
read(data, pos, result)
proc readUint8*(data: openArray[byte], pos: var int): uint8 {.inline.} =
read(data, pos, result)
proc readUint16*(data: openArray[byte], pos: var int): uint16 {.inline.} =
read(data, pos, result)
proc readUint32*(data: openArray[byte], pos: var int): uint32 {.inline.} =
read(data, pos, result)
proc readUint64*(data: openArray[byte], pos: var int): uint64 {.inline.} =
read(data, pos, result)
proc readFloat32*(data: openArray[byte], pos: var int): float32 {.inline.} =
read(data, pos, result)
proc readFloat64*(data: openArray[byte], pos: var int): float64 {.inline.} =
read(data, pos, result)
proc fromData*(data: openArray[byte]; pos: var int; output: var string)
proc fromData*[S, T](data: openArray[byte]; pos: var int; output: var array[S, T])
proc fromData*[T](data: openArray[byte]; pos: var int; output: var seq[T])
proc fromData*[T](data: openArray[byte]; pos: var int; output: var SomeSet[T])
proc fromData*[K, V](data: openArray[byte]; pos: var int; output: var (Table[K, V]|OrderedTable[K, V]))
proc fromData*[T](data: openArray[byte]; pos: var int; output: var ref T)
proc fromData*[T](data: openArray[byte]; pos: var int; output: var Option[T])
proc fromData*[T: tuple](data: openArray[byte]; pos: var int; output: var T)
proc fromData*[T: object](data: openArray[byte]; pos: var int; output: var T) {.nodestroy.}
proc fromData*[T: distinct](data: openArray[byte]; pos: var int; output: var T) {.inline.}
proc toData*(data: var openArray[byte]; pos: var int; input: string)
proc toData*[S, T](data: var openArray[byte]; pos: var int; input: array[S, T])
proc toData*[T](data: var openArray[byte]; pos: var int; input: seq[T])
proc toData*[T](data: var openArray[byte]; pos: var int; input: SomeSet[T])
proc toData*[K, V](data: var openArray[byte]; pos: var int; input: (Table[K, V]|OrderedTable[K, V]))
proc toData*[T](data: var openArray[byte]; pos: var int; input: ref T)
proc toData*[T](data: var openArray[byte]; pos: var int; input: Option[T])
proc toData*[T: tuple](data: var openArray[byte]; pos: var int; input: T)
proc toData*[T: object](data: var openArray[byte]; pos: var int; input: T)
proc toData*[T: distinct](data: var openArray[byte]; pos: var int; input: T) {.inline.}
proc toData*(data: var openArray[byte]; pos: var int; input: bool) =
write(data, pos, input)
proc fromData*(data: openArray[byte]; pos: var int; output: var bool) =
read(data, pos, output)
proc toData*(data: var openArray[byte]; pos: var int; input: char) =
write(data, pos, input)
proc fromData*(data: openArray[byte]; pos: var int; output: var char) =
read(data, pos, output)
proc toData*[T: SomeNumber](data: var openArray[byte]; pos: var int; input: T) =
write(data, pos, input)
proc fromData*[T: SomeNumber](data: openArray[byte]; pos: var int; output: var T) =
read(data, pos, output)
proc toData*[T: enum](data: var openArray[byte]; pos: var int; input: T) =
write(data, pos, input)
proc fromData*[T: enum](data: openArray[byte]; pos: var int; output: var T) =
read(data, pos, output)
proc toData*[T](data: var openArray[byte]; pos: var int; input: set[T]) =
write(data, pos, input)
proc fromData*[T](data: openArray[byte]; pos: var int; output: var set[T]) =
read(data, pos, output)
proc toData*(data: var openArray[byte]; pos: var int; input: string) =
write(data, pos, int32(input.len))
writeData(data, pos, cstring(input), input.len)
proc fromData*(data: openArray[byte]; pos: var int; output: var string) =
let len = readInt32(data, pos).int
output.setLen(len)
if readData(data, pos, cstring(output), len) != len:
raiseDecoding()
proc toData*[S, T](data: var openArray[byte]; pos: var int; input: array[S, T]) =
when supportsCopyMem(T):
writeData(data, pos, input.unsafeAddr, sizeof(input))
else:
for elem in input.items:
toData(data, pos, elem)
proc fromData*[S, T](data: openArray[byte]; pos: var int; output: var array[S, T]) =
when supportsCopyMem(T):
if readData(data, pos, output.addr, sizeof(output)) != sizeof(output):
raiseDecoding()
else:
for i in low(output)..high(output):
fromData(data, pos, output[i])
proc toData*[T](data: var openArray[byte]; pos: var int; input: seq[T]) =
write(data, pos, int32(input.len))
when supportsCopyMem(T):
if input.len > 0:
writeData(data, pos, input[0].unsafeAddr, input.len * sizeof(T))
else:
for elem in input.items:
toData(data, pos, elem)
proc fromData*[T](data: openArray[byte]; pos: var int; output: var seq[T]) =
let len = readInt32(data, pos).int
output.setLen(len)
when supportsCopyMem(T):
if len > 0:
let bLen = len * sizeof(T)
if readData(data, pos, output[0].addr, bLen) != bLen:
raiseDecoding()
else:
for i in 0..<len:
fromData(data, pos, output[i])
proc toData*[T](data: var openArray[byte]; pos: var int; input: SomeSet[T]) =
write(data, pos, int32(input.len))
for elem in input.items:
toData(data, pos, elem)
proc fromData*[T](data: openArray[byte]; pos: var int; output: var SomeSet[T]) =
let len = readInt32(data, pos).int
for i in 0..<len:
var tmp: T
fromData(data, pos, tmp)
output.incl(tmp)
proc toData*[K, V](data: var openArray[byte]; pos: var int; input: (Table[K, V]|OrderedTable[K, V])) =
write(data, pos, int32(input.len))
for k, v in input.pairs:
toData(data, pos, k)
toData(data, pos, v)
proc fromData*[K, V](data: openArray[byte]; pos: var int; output: var (Table[K, V]|OrderedTable[K, V])) =
let len = readInt32(data, pos).int
for i in 0 ..< len:
var key: K
fromData(data, pos, key)
fromData(data, pos, mgetOrPut(output, key, default(V)))
proc toData*[T](data: var openArray[byte]; pos: var int; input: ref T) =
let isSome = input != nil
toData(data, pos, isSome)
if isSome:
toData(data, pos, input[])
proc fromData*[T](data: openArray[byte]; pos: var int; output: var ref T) =
let isSome = readBool(data, pos)
if isSome:
new(output)
fromData(data, pos, output[])
else:
output = nil
proc toData*[T](data: var openArray[byte]; pos: var int; input: Option[T]) =
let isSome = isSome(input)
toData(data, pos, isSome)
if isSome:
toData(data, pos, get(input))
proc fromData*[T](data: openArray[byte]; pos: var int; output: var Option[T]) =
let isSome = readBool(data, pos)
if isSome:
var tmp: T
fromData(data, pos, tmp)
output = some(tmp)
else:
output = none[T]()
proc toData*[T: tuple](data: var openArray[byte]; pos: var int; input: T) =
when supportsCopyMem(T):
write(data, pos, input)
else:
for v in input.fields:
toData(data, pos, v)
proc toData*[T: object](data: var openArray[byte]; pos: var int; input: T) =
when supportsCopyMem(T):
write(data, pos, input)
else:
for v in input.fields:
toData(data, pos, v)
proc fromData*[T: tuple](data: openArray[byte]; pos: var int; output: var T) =
when supportsCopyMem(T):
read(data, pos, output)
else:
for v in output.fields:
fromData(data, pos, v)
proc fromData*[T: object](data: openArray[byte]; pos: var int; output: var T) =
when supportsCopyMem(T):
read(data, pos, output)
else:
template fromDataFunc(x: untyped) =
fromData(data, pos, x)
assignObjectImpl(output, fromDataFunc)
proc toData*[T: distinct](data: var openArray[byte]; pos: var int; input: T) =
toData(data, pos, input.distinctBase)
proc fromData*[T: distinct](data: openArray[byte]; pos: var int; output: var T) =
fromData(data, pos, output.distinctBase)

549
drchaos/mutator.nim Normal file
View File

@ -0,0 +1,549 @@
import std/[random, macros, setutils, enumutils, typetraits, options]
import common, private/[sampler, utf8fix]
when not defined(fuzzerStandalone):
proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
{.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
proc mutate(data: ptr UncheckedArray[byte], len, maxLen: int): int {.
importc: "LLVMFuzzerMutate".}
template `+!`(p: pointer, s: int): untyped =
cast[pointer](cast[ByteAddress](p) +% s)
const
RandomToDefaultRatio = 100 # The chance of returning an uninitalized type.
DefaultMutateWeight = 1_000_000 # The default weight of items sampled by the reservoir sampler.
MaxInitializeDepth = 200 # The post-processor prunes nested non-copyMem types.
type
ByteSized* = int8|uint8|byte|bool|char # Run LibFuzzer's mutate for sequences of these types.
PostProcessTypes* = (object|tuple|ref|seq|string|array|set|distinct) ## The post-processor runs only on these types.
proc runMutator*[T: SomeNumber](x: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*[T](x: var seq[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*(x: var bool; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*(x: var char; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*[T: enum](x: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*[T](x: var set[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*(x: var string; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*[T: tuple|object](x: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*[T](x: var ref T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc runMutator*[S, T](x: var array[S, T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand)
proc flipBit*(bytes: ptr UncheckedArray[byte]; len: int; r: var Rand) =
## Flips random bit in the buffer.
let bit = rand(r, len * 8 - 1)
bytes[bit div 8] = bytes[bit div 8] xor (1'u8 shl (bit mod 8))
proc flipBit*[T](value: T; r: var Rand): T =
## Flips random bit in the value.
result = value
flipBit(cast[ptr UncheckedArray[byte]](addr result), sizeof(T), r)
when defined(fuzzerStandalone):
proc mutateValue*[T](value: T; r: var Rand): T =
flipBit(value, r)
else:
proc mutateValue*[T](value: T; r: var Rand): T =
result = value
let size = mutate(cast[ptr UncheckedArray[byte]](addr result), sizeof(T), sizeof(T))
zeroMem(result.addr +! size, sizeof(T) - size)
proc mutateEnum*(index, itemCount: int; r: var Rand): int =
if itemCount <= 1: 0
else: (index + 1 + r.rand(itemCount - 1)) mod itemCount
proc newInput*[T](sizeIncreaseHint: int; r: var Rand): T =
## Creates new input with a chance of returning default(T).
runMutator(result, sizeIncreaseHint, false, r)
proc mutateSeq*[T](value: var seq[T]; previous: seq[T]; userMax, sizeIncreaseHint: int;
r: var Rand): bool =
let previousSize = previous.byteSize
while value.len > 0 and r.rand(bool):
value.delete(rand(r, value.high))
var currentSize = value.byteSize
template remainingSize: untyped = sizeIncreaseHint-currentSize+previousSize
while value.len < userMax and remainingSize > 0 and r.rand(bool):
let index = rand(r, value.len)
value.insert(newInput[T](remainingSize, r), index)
currentSize = value.byteSize
if value != previous:
result = true
elif value.len == 0:
value.add(newInput[T](remainingSize, r))
result = true
else:
let index = rand(r, value.high)
runMutator(value[index], remainingSize, true, r)
result = value != previous # runMutator item may still fail to generate a new mutation.
proc mutateByteSizedSeq*[T: ByteSized and not range](value: sink seq[T]; userMax, sizeIncreaseHint: int;
r: var Rand): seq[T] =
if r.rand(0..20) == 0:
result = @[]
else:
let oldSize = value.len
result = value
result.setLen(max(1, oldSize + r.rand(sizeIncreaseHint)))
result.setLen(mutate(cast[ptr UncheckedArray[byte]](addr result[0]), oldSize, result.len))
when T is bool:
# Fix bool values so UBSan stops complaining.
for i in 0..<result.len: result[i] = cast[seq[byte]](result)[i] != 0.byte
elif T is range:
for i in 0..<result.len: result[i] = clamp(result[i], low(T), high(T))
proc mutateString*(value: sink string; userMax, sizeIncreaseHint: int; r: var Rand): string =
if r.rand(0..20) == 0:
result = ""
else:
let oldSize = value.len
result = value
result.setLen(max(1, oldSize + r.rand(sizeIncreaseHint)))
result.setLen(mutate(cast[ptr UncheckedArray[byte]](addr result[0]), oldSize, result.len))
proc mutateUtf8String*(value: sink string; userMax, sizeIncreaseHint: int; r: var Rand): string {.inline.} =
result = mutateString(value, userMax, sizeIncreaseHint, r)
fixUtf8(result, r)
proc mutateArray*[S, T](value: array[S, T]; r: var Rand): array[S, T] {.inline.} =
result = mutateValue(value, r)
when T is bool:
for i in low(result)..high(result): result[i] = cast[array[S, byte]](result)[i] != 0.byte
elif T is range:
for i in low(result)..high(result): result[i] = clamp(result[i], low(T), high(T))
template repeatMutate*(call: untyped) =
if not enforceChanges and rand(r, RandomToDefaultRatio - 1) == 0:
discard
else:
var tmp = value
for i in 1..10:
value = call
if not enforceChanges or value != tmp: return
template repeatMutateInplace*(call: untyped) =
if not enforceChanges and rand(r, RandomToDefaultRatio - 1) == 0:
discard
else:
var tmp {.inject.} = value
for i in 1..10:
let notEqual = call
if not enforceChanges or notEqual: return
proc mutate*(value: var bool; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
value = not value
proc mutate*(value: var char; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutate(mutateValue(value, r))
proc mutate*[T: range](value: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutate(clamp(mutateValue(value, r), low(T), high(T)))
proc mutate*[T](value: var set[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutate(mutateValue(value, r) * fullSet(T))
macro enumFullRange(a: typed): untyped =
nnkBracket.newTree(a.getType[1][1..^1])
proc mutate*[T: HoleyEnum](value: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutate(enumFullRange(T)[mutateEnum(value.symbolRank, enumLen(T), r)])
proc mutate*[T: OrdinalEnum](value: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutate(T(mutateEnum(value.symbolRank, enumLen(T), r)+low(T).ord))
proc mutate*[T: SomeNumber](value: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutate(mutateValue(value, r))
proc mutate*[T: not ByteSized](value: var seq[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutateInplace(mutateSeq(value, tmp, high(int), sizeIncreaseHint, r))
proc mutate*[T: ByteSized](value: var seq[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutate(mutateByteSizedSeq(move value, high(int), sizeIncreaseHint, r))
proc mutate*(value: var string; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
when defined(fuzzerUtf8Strings):
repeatMutate(mutateUtf8String(move value, high(int), sizeIncreaseHint, r))
else:
repeatMutate(mutateString(move value, high(int), sizeIncreaseHint, r))
proc mutate*[S; T: SomeNumber|bool|char](value: var array[S, T]; sizeIncreaseHint: int;
enforceChanges: bool; r: var Rand) =
repeatMutate(mutateArray(value, r))
proc mutate*[T](value: var Option[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
if not enforceChanges and rand(r, RandomToDefaultRatio - 1) == 0:
discard
else:
if not isSome(value):
value = some(default(T))
runMutator(value.get, sizeIncreaseHint, enforceChanges, r)
template sampleAttempt(call: untyped) =
inc res
call
proc sample[T: distinct](x: T; s: var Sampler; r: var Rand; res: var int) =
when compiles(mutate(x, 0, false, r)):
sampleAttempt(attempt(x, r, DefaultMutateWeight, res))
else:
sample(x.distinctBase, s, r, res)
proc sample(x: bool; s: var Sampler; r: var Rand; res: var int) =
sampleAttempt(attempt(s, r, DefaultMutateWeight, res))
proc sample(x: char; s: var Sampler; r: var Rand; res: var int) =
sampleAttempt(attempt(s, r, DefaultMutateWeight, res))
proc sample[T: enum](x: T; s: var Sampler; r: var Rand; res: var int) =
sampleAttempt(attempt(s, r, DefaultMutateWeight, res))
proc sample[T](x: set[T]; s: var Sampler; r: var Rand; res: var int) =
sampleAttempt(attempt(s, r, DefaultMutateWeight, res))
proc sample[T: SomeNumber](x: T; s: var Sampler; r: var Rand; res: var int) =
sampleAttempt(attempt(s, r, DefaultMutateWeight, res))
proc sample[T](x: seq[T]; s: var Sampler; r: var Rand; res: var int) =
sampleAttempt(attempt(s, r, DefaultMutateWeight, res))
proc sample(x: string; s: var Sampler; r: var Rand; res: var int) =
sampleAttempt(attempt(s, r, DefaultMutateWeight, res))
proc sample[T: tuple|object](x: T; s: var Sampler; r: var Rand; res: var int) =
when compiles(mutate(x, 0, false, r)):
sampleAttempt(attempt(x, r, DefaultMutateWeight, res))
else:
for v in fields(x):
sample(v, s, r, res)
proc sample[T](x: ref T; s: var Sampler; r: var Rand; res: var int) =
when compiles(mutate(x, 0, false, r)):
sampleAttempt(attempt(x, r, DefaultMutateWeight, res))
else:
if x != nil: sample(x[], s, r, res)
proc sample[S, T](x: array[S, T]; s: var Sampler; r: var Rand; res: var int) =
when compiles(mutate(x, 0, false, r)):
sampleAttempt(attempt(x, r, DefaultMutateWeight, res))
else:
for i in low(x)..high(x):
sample(x[i], s, r, res)
template pickMutate(call: untyped) =
if res > 0:
dec res
if res == 0:
call
proc pick[T: distinct](x: var T; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
else:
pick(x.distinctBase, sizeIncreaseHint, enforceChanges, r, res)
proc pick(x: var bool; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
proc pick(x: var char; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
proc pick[T: enum](x: var T; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
proc pick[T](x: var set[T]; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
proc pick[T: SomeNumber](x: var T; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
proc pick[T](x: var seq[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand;
res: var int) =
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
proc pick(x: var string; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand;
res: var int) =
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
proc pick[T: tuple](x: var T; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
else:
for v in fields(x):
pick(v, sizeIncreaseHint, enforceChanges, r, res)
proc pick[T: object](x: var T; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
else:
template pickFunc(x: untyped) =
pick(x, sizeIncreaseHint, enforceChanges, r, res)
assignObjectImpl(x, pickFunc)
proc pick[T](x: var ref T; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
else:
if x != nil: pick(x[], sizeIncreaseHint, enforceChanges, r, res)
proc pick[S, T](x: var array[S, T]; sizeIncreaseHint: int; enforceChanges: bool;
r: var Rand; res: var int) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
pickMutate(mutate(x, sizeIncreaseHint, enforceChanges, r))
else:
for i in low(x)..high(x):
pick(x[i], sizeIncreaseHint, enforceChanges, r, res)
proc runMutator*[T: distinct](x: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
mutate(x, sizeIncreaseHint, enforceChanges, r)
else:
runMutator(x.distinctBase, sizeIncreaseHint, enforceChanges, r)
proc runMutator*[T: SomeNumber](x: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
mutate(x, sizeIncreaseHint, enforceChanges, r)
proc runMutator*[T](x: var seq[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
mutate(x, sizeIncreaseHint, enforceChanges, r)
proc runMutator*(x: var string; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
mutate(x, sizeIncreaseHint, enforceChanges, r)
proc runMutator*(x: var bool; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
mutate(x, sizeIncreaseHint, enforceChanges, r)
proc runMutator*(x: var char; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
mutate(x, sizeIncreaseHint, enforceChanges, r)
proc runMutator*[T: enum](x: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
mutate(x, sizeIncreaseHint, enforceChanges, r)
proc runMutator*[T](x: var set[T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
mutate(x, sizeIncreaseHint, enforceChanges, r)
proc runMutator*[T: tuple|object](x: var T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
mutate(x, sizeIncreaseHint, enforceChanges, r)
else:
if not enforceChanges and rand(r, RandomToDefaultRatio - 1) == 0:
discard
else:
var res = 0
var s: Sampler[int]
sample(x, s, r, res)
res = s.selected
pick(x, sizeIncreaseHint, enforceChanges, r, res)
proc runMutator*[T](x: var ref T; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
mutate(x, sizeIncreaseHint, enforceChanges, r)
else:
if not enforceChanges and rand(r, RandomToDefaultRatio - 1) == 0:
discard
else:
if x == nil: new(x)
runMutator(x[], sizeIncreaseHint, enforceChanges, r)
proc runMutator*[S, T](x: var array[S, T]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
when compiles(mutate(x, sizeIncreaseHint, enforceChanges, r)):
mutate(x, sizeIncreaseHint, enforceChanges, r)
else:
if not enforceChanges and rand(r, RandomToDefaultRatio - 1) == 0:
discard
else:
var res = 0
var s: Sampler[int]
sample(x, s, r, res)
res = s.selected
pick(x, sizeIncreaseHint, enforceChanges, r, res)
proc runPostProcessor*[T: distinct](x: var T, depth: int; r: var Rand) =
# Allow post-processor functions for all distinct types.
when compiles(postProcess(x, r)):
if depth < 0:
when not supportsCopyMem(T): reset(x)
else:
postProcess(x, r)
else:
when x.distinctBase is PostProcessTypes:
runPostProcessor(x.distinctBase, depth-1, r)
proc runPostProcessor*[T](x: var seq[T], depth: int; r: var Rand) =
if depth < 0:
reset(x)
else:
when compiles(postProcess(x, r)):
postProcess(x, r)
else:
when T is PostProcessTypes:
for i in 0..<x.len:
runPostProcessor(x[i], depth-1, r)
proc runPostProcessor*[T](x: var set[T], depth: int; r: var Rand) =
when compiles(postProcess(x, r)):
if depth >= 0:
postProcess(x, r)
proc runPostProcessor*(x: var string, depth: int; r: var Rand) =
if depth < 0:
reset(x)
else:
when compiles(postProcess(x, r)):
postProcess(x, r)
proc runPostProcessor*[T: tuple](x: var T, depth: int; r: var Rand) =
if depth < 0:
when not supportsCopyMem(T): reset(x)
else:
when compiles(postProcess(x, r)):
postProcess(x, r)
else:
for v in fields(x):
when typeof(v) is PostProcessTypes:
runPostProcessor(v, depth-1, r)
proc runPostProcessor*[T: object](x: var T, depth: int; r: var Rand) =
if depth < 0:
when not supportsCopyMem(T): reset(x)
else:
when compiles(postProcess(x, r)):
postProcess(x, r)
# When there is a user-provided mutator, don't touch private fields.
elif compiles(mutate(x, 0, false, r)):
# Guess how to traverse a data structure, if it's even one.
when compiles(for v in mitems(x): discard):
# Run the post-processor only for compatible types as there is an overhead.
when typeof(for v in mitems(x): v) is PostProcessTypes:
for v in mitems(x):
runPostProcessor(v, depth-1, r)
elif compiles(for k, v in mpairs(x): discard):
when typeof(for k, v in mpairs(x): v) is PostProcessTypes:
for k, v in mpairs(x):
runPostProcessor(v, depth-1, r)
else:
template runPostFunc(x: untyped) =
when typeof(x) is PostProcessTypes:
runPostProcessor(x, depth-1, r)
assignObjectImpl(x, runPostFunc)
proc runPostProcessor*[T](x: var ref T, depth: int; r: var Rand) =
if depth < 0:
reset(x)
else:
when compiles(postProcess(x, r)):
postProcess(x, r)
else:
when T is PostProcessTypes:
if x != nil: runPostProcessor(x[], depth-1, r)
proc runPostProcessor*[S, T](x: var array[S, T], depth: int; r: var Rand) =
if depth < 0:
when not supportsCopyMem(T): reset(x)
else:
when compiles(postProcess(x, r)):
postProcess(x, r)
else:
when T is PostProcessTypes:
for i in low(x)..high(x):
runPostProcessor(x[i], depth-1, r)
proc myMutator[T](x: var T; sizeIncreaseHint: Natural; r: var Rand) {.nimcall.} =
runMutator(x, sizeIncreaseHint, true, r)
when T is PostProcessTypes:
runPostProcessor(x, MaxInitializeDepth, r)
template mutatorImpl(target, mutator, typ: untyped) =
{.pragma: nocov, codegenDecl: "__attribute__((no_sanitize(\"coverage\"))) $# $#$#".}
{.pragma: nosan, codegenDecl: "__attribute__((disable_sanitizer_instrumentation)) $# $#$#".}
type
FuzzTarget = proc (x: typ) {.nimcall, noSideEffect.}
FuzzMutator = proc (x: var typ; sizeIncreaseHint: Natural, r: var Rand) {.nimcall.}
var
buffer: seq[byte] = @[0xf1'u8]
cached: typ
proc getInput(x: var typ; data: openArray[byte]): var typ {.nocov, nosan.} =
if equals(data, buffer):
result = cached
else:
var pos = 1
fromData(data, pos, x)
result = x
proc setInput(x: var typ; data: openArray[byte]; len: int) {.inline.} =
setLen(buffer, len)
var pos = 1
toData(buffer, pos, x)
assert pos == len
copyMem(addr data, addr buffer[0], len)
cached = move x
proc clearBuffer() {.inline.} =
setLen(buffer, 1)
proc testOneInputImpl[T](x: var T; data: openArray[byte]) =
if data.len > 1: # Ignore '\n' passed by LibFuzzer.
try:
FuzzTarget(target)(getInput(x, data))
finally:
# Call Nim's compiler api to report unhandled exceptions.
{.emit: "nimTestErrorFlag();".}
proc customMutatorImpl(x: var typ; data: openArray[byte]; maxLen: int;
r: var Rand): int {.nosan.} =
if data.len > 1:
#var pos = 1
#fromData(data, pos, x)
x = getInput(x, data)
FuzzMutator(mutator)(x, maxLen-x.byteSize, r)
result = x.byteSize+1 # +1 for the skipped byte
if result <= maxLen:
setInput(x, data, result)
else:
clearBuffer()
result = data.len
proc LLVMFuzzerTestOneInput(data: ptr UncheckedArray[byte], len: int): cint {.exportc.} =
result = 0
var x: typ
testOneInputImpl(x, toOpenArray(data, 0, len-1))
proc LLVMFuzzerCustomMutator(data: ptr UncheckedArray[byte], len, maxLen: int,
seed: int64): int {.
exportc.} =
var r = initRand(seed)
var x: typ
customMutatorImpl(x, toOpenArray(data, 0, len-1), maxLen, r)
proc commonImpl(target, mutator: NimNode): NimNode =
let typ = getTypeImpl(target).params[^1][1]
result = getAst(mutatorImpl(target, mutator, typ))
macro defaultMutator*(target: proc) =
## Implements the interface for running LibFuzzer's fuzzing loop, where func `target`'s
## single immutatable parameter, is the structured input type.
## It uses the default mutator that also includes the post-processor.
## It's recommended that the experimental "strict funcs" feature is enabled.
commonImpl(target, bindSym"myMutator")
macro customMutator*(target, mutator: proc) =
## Implements the interface for running LibFuzzer's fuzzing loop, where func `target`'s
## single immutatable parameter, is the structured input type.
## It uses `mutator: proc (x: var T; sizeIncreaseHint: Natural, r: var Rand)`
## to generate new mutations. This has the flexibility of transforming the input and/or
## mutating some part of it via the `runMutator` proc. Then applying the reverse transform to
## convert it back to the original representation.
## It's recommended that the experimental "strict funcs" feature is enabled.
commonImpl(target, mutator)

View File

@ -0,0 +1,57 @@
import std/random
type
Sampler*[T] = object
selected: T
totalWeight: int
proc pick*[T](s: var Sampler[T], r: var Rand; weight: Natural): bool =
if weight == 0: return false
s.totalWeight += weight
weight == s.totalWeight or r.rand(1..s.totalWeight) <= weight
proc attempt*[T](s: var Sampler[T], r: var Rand; weight: Natural; item: sink T) =
if pick(s, r, weight): s.selected = item
proc selected*[T](s: Sampler[T]): lent T = s.selected
proc isEmpty*[T](s: Sampler[T]): bool {.inline.} = s.totalWeight == 0
when isMainModule:
import math
const
Runs = 1000000
Tests = [
@[1],
@[1, 1, 1],
@[1, 1, 0],
@[1, 10, 100],
@[100, 1, 10],
@[1, 10000, 10000],
@[1, 3, 7, 100, 105],
@[93519, 52999, 354, 37837, 55285,
31787, 89096, 55695, 1587, 18233, 77557, 67632, 59348, 51250, 17417, 96856, 78568,
44296, 70170, 41328, 9206, 90187, 54086, 35602, 53167, 33791, 60118, 52962, 10327,
80513, 49526, 18326, 83662, 49644, 70903, 4910, 36309, 19196, 42982, 53316, 14773,
86607, 60835]
]
proc `=~`(x, y: float): bool = abs(x - y) < 0.01
proc test(seed: int, weights: seq[int]) =
var counts = newSeq[int](weights.len)
var rand = initRand(seed)
for i in 0 ..< Runs:
var sampler: Sampler[int]
for j in 0 ..< weights.len: test(sampler, rand, weights[j], j)
inc counts[sampler.selected]
let sum = sum(weights)
for j in 0 ..< weights.len:
var expected = weights[j].float
expected = expected / sum.float
var actual = counts[j].float
actual = actual / Runs.float
assert expected =~ actual
for i in 0..<Tests.len: test(1, Tests[i])
for i in 0..<Tests.len: test(4, Tests[i])
for i in 0..<Tests.len: test(7, Tests[i])

View File

@ -0,0 +1,81 @@
import std/random
proc storeCode(buf: var openArray[char]; e: int, code: uint32, size: int, prefix: uint8) =
var size = size - 1
var e = e
var code = code
while size > 0:
dec e
buf[e] = char(0x80 or (code and 0x3f))
code = code shr 6
dec size
dec e
buf[e] = char(prefix or code)
proc fixCode(buf: var openArray[char], b, e: int, r: var Rand): int =
let start = b
assert b < e
let e = min(e, b + 4)
var b = b
var c = uint32(buf[b])
inc b
while b < e and (uint32(buf[b]) and 0xc0) == 0x80:
c = c shl 6 + (uint32(buf[b]) and 0x3f)
inc b
let size = b - start
case size
of 1:
c = c and 0x7f
storeCode(buf, b, c, size, 0)
of 2:
c = c and 0x7ff
if c < 0x80:
c = r.rand(0x80'u32..0x7ff'u32)
storeCode(buf, b, c, size, 0xc0)
of 3:
c = c and 0xffff
# [0xD800, 0xE000) are reserved for UTF-16 surrogate halves.
if c < 0x800 or (c >= 0xd800 and c < 0xe000):
const halves = 0xe000 - 0xd800
c = r.rand(0x800'u32..0xffff'u32 - halves)
if c >= 0xd800: c = c + halves
storeCode(buf, b, c, size, 0xe0)
of 4:
c = c and 0x1fffff
if c < 0x10000 or c > 0x10ffff:
c = r.rand(0x10000'u32..0x10ffff'u32)
storeCode(buf, b, c, size, 0xf0)
else:
assert(false, "Unexpected size of UTF-8 sequence")
return b
proc fixUtf8*(str: var string; r: var Rand) =
if str == "": return
var b = 0
let e = str.len
while b < e:
b = fixCode(str, b, e, r)
when isMainModule:
import unicode
template isValid(s: string): bool =
validateUtf8(s) == -1
block:
assert "".isValid
assert "abc".isValid
assert "\xc2\xa2".isValid
assert "\xe2\x82\xac".isValid
assert "\xf0\x90\x8d\x88".isValid
assert not "\xff\xff\xff\xff".isValid
assert not "\xff\x8f".isValid
assert not "\x3f\xbf".isValid
block:
var str = newString(rand(0..255))
for run in 1..10000:
for i in 0..<str.len: str[i] = rand(char)
var fixed = str
fixUtf8(fixed, randState)
if str.isValid: assert fixed == str
else: assert fixed.isValid

115
examples/fuzz_graph.nim Normal file
View File

@ -0,0 +1,115 @@
# Good seed to try out without the postProcess proc. -seed=4013847822
when defined(runFuzzTests):
const
MaxNodes = 8 # User defined, statically limits number of nodes.
MaxEdges = 2 # Limits number of edges
type
NodeIdx = distinct int
proc `$`(x: NodeIdx): string {.borrow.}
proc `==`(a, b: NodeIdx): bool {.borrow.}
else:
type
NodeIdx = int
type
Graph*[T] = object
nodes: seq[Node[T]]
Node[T] = object
data: T
edges: seq[NodeIdx]
proc `[]`*[T](x: Graph[T]; idx: Natural): lent T {.inline.} = x.nodes[idx].data
proc `[]`*[T](x: var Graph[T]; idx: Natural): var T {.inline.} = x.nodes[idx].data
proc addNode*[T](x: var Graph[T]; data: sink T) {.nodestroy.} =
x.nodes.add Node[T](data: data, edges: @[])
proc deleteNode*[T](x: var Graph[T]; idx: Natural) =
if idx < x.nodes.len:
x.nodes.delete(idx)
for n in x.nodes.mitems:
if (let position = n.edges.find(idx.NodeIdx); position != -1):
n.edges.delete(position)
proc addEdge*[T](x: var Graph[T]; `from`, to: Natural) =
if `from` < x.nodes.len and to < x.nodes.len:
x.nodes[`from`].edges.add(to.NodeIdx)
proc deleteEdge*[T](x: var Graph[T]; `from`, to: Natural) =
if `from` < x.nodes.len and to < x.nodes.len:
template fromNode: untyped = x.nodes[`from`]
if (let toNodeIdx = fromNode.edges.find(to.NodeIdx); toNodeIdx != -1):
template toNode: untyped = fromNode.edges[toNodeIdx]
fromNode.edges.delete(toNodeIdx)
#x.deleteNode(toNode.int) #sneaky bug?
when defined(runFuzzTests) and isMainModule:
import std/random, drchaos/[mutator, common]
{.experimental: "strictFuncs".}
proc mutate(value: var NodeIdx; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutate(mutateEnum(value.int, MaxNodes, r).NodeIdx)
proc mutate[T](value: var seq[Node[T]]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutateInplace(mutateSeq(value, tmp, MaxNodes, sizeIncreaseHint, r))
proc mutate(value: var seq[NodeIdx]; sizeIncreaseHint: int; enforceChanges: bool; r: var Rand) =
repeatMutateInplace(mutateSeq(value, tmp, MaxEdges, sizeIncreaseHint, r))
# We run it as a test, so cheat a little.
proc postProcess[T: SomeNumber](x: var seq[Node[T]]; r: var Rand) =
if x.len >= 8:
x[0].data = 63
x[1].data = 3
x[2].data = -56
x[3].data = 100
x[4].data = -100
x[5].data = -78
x[6].data = 46
x[7].data = 120
func fuzzTarget(x: Graph[int8]) =
when defined(dumpFuzzInput): debugEcho(x)
if x.nodes.len == 8 and
x.nodes[0].data == 63 and
x.nodes[1].data == 3 and
x.nodes[2].data == -56 and
x.nodes[3].data == 100 and
x.nodes[4].data == -100 and
x.nodes[5].data == -78 and
x.nodes[6].data == 46 and
x.nodes[7].data == 120 and
x.nodes[0].edges.len == 2 and
x.nodes[0].edges[0] == 1.NodeIdx and
x.nodes[0].edges[1] == 2.NodeIdx and
x.nodes[1].edges.len == 2 and
x.nodes[1].edges[0] == 3.NodeIdx and
x.nodes[1].edges[1] == 4.NodeIdx and
x.nodes[2].edges.len == 2 and
x.nodes[2].edges[0] == 5.NodeIdx and
x.nodes[2].edges[1] == 6.NodeIdx and
x.nodes[3].edges.len == 1 and
x.nodes[3].edges[0] == 7.NodeIdx and
x.nodes[4].edges.len == 0 and
x.nodes[5].edges.len == 0 and
x.nodes[6].edges.len == 0 and
x.nodes[7].edges.len == 0:
doAssert false
defaultMutator(fuzzTarget)
#(nodes: @[
#(data: 63, edges: @[1, 2]),
#(data: 3, edges: @[3, 4]),
#(data: -56, edges: @[5, 6]),
#(data: 100, edges: @[7]),
#(data: -100, edges: @[]),
#(data: -78, edges: @[]),
#(data: 46, edges: @[]),
#(data: 120, edges: @[])
#])

1
examples/fuzz_graph.nims Normal file
View File

@ -0,0 +1 @@
--define: runFuzzTests

27
examples/fuzz_tree.nim Normal file
View File

@ -0,0 +1,27 @@
import drchaos
type
ContentNodeKind = enum
P, Br, Text
ContentNode = object
case kind: ContentNodeKind
of P: pChildren: seq[ContentNode]
of Br: discard
of Text: textStr: string
func `==`(a, b: ContentNode): bool =
if a.kind != b.kind: return false
case a.kind
of P: return a.pChildren == b.pChildren
of Br: return true
of Text: return a.textStr == b.textStr
func fuzzTarget(x: ContentNode) =
when defined(dumpFuzzInput): debugEcho(x)
let data = ContentNode(kind: P, pChildren: @[
ContentNode(kind: Text, textStr: "mychild"),
ContentNode(kind: Br)
])
doAssert x != data
defaultMutator(fuzzTarget)

13
examples/nim.cfg Normal file
View File

@ -0,0 +1,13 @@
--cc: clang
--define: useMalloc
@if not fuzzerStandalone:
--noMain: on
--define: noSignalHandler
--passC: "-fsanitize=fuzzer"
--passL: "-fsanitize=fuzzer"
@end
--passC: "-fsanitize=address,undefined"
--passL: "-fsanitize=address,undefined"
#--define: release
--debugger: native
--path: "../"

13
tests/nim.cfg Normal file
View File

@ -0,0 +1,13 @@
--cc: clang
--define: useMalloc
@if not fuzzerStandalone:
--noMain: on
--define: noSignalHandler
--passC: "-fsanitize=fuzzer"
--passL: "-fsanitize=fuzzer"
@end
--passC: "-fsanitize=address,undefined"
--passL: "-fsanitize=address,undefined"
#--define: release
--debugger: native
--path: "../"

9
tests/tarrays.nim Normal file
View File

@ -0,0 +1,9 @@
import drchaos
type
DiceFace = range[1..6]
func fuzzTarget(x: array[10, DiceFace]) =
doAssert x != [1.DiceFace, 6, 2, 3, 4, 3, 6, 4, 5, 2]
defaultMutator(fuzzTarget)

6
tests/tbools.nim Normal file
View File

@ -0,0 +1,6 @@
import drchaos
func fuzzTarget(x: bool) =
if x == true: doAssert false
defaultMutator(fuzzTarget)

6
tests/tchars.nim Normal file
View File

@ -0,0 +1,6 @@
import drchaos
func fuzzTarget(x: char) =
if x == 'a': doAssert false
defaultMutator(fuzzTarget)

6
tests/tfloats.nim Normal file
View File

@ -0,0 +1,6 @@
import drchaos
func fuzzTarget(x: float32) =
doAssert x <= 100
defaultMutator(fuzzTarget)

View File

@ -0,0 +1,13 @@
--cc: clang
--define: useMalloc
@if not fuzzerStandalone:
--noMain: on
--define: noSignalHandler
--passC: "-fsanitize=fuzzer"
--passL: "-fsanitize=fuzzer"
@end
--passC: "-fsanitize=address,undefined"
--passL: "-fsanitize=address,undefined"
#--define: release
--debugger: native
--path: "../../"

View File

@ -0,0 +1,14 @@
# Runs infinitely. Run with a time limit and make sure it doesn't crash.
import drchaos
type
Color = enum
Red, Green, Blue
OtherColor = enum
Cyan, Magenta=2, Yellow=4, Black=8
func fuzzTarget(x: Color) =
doAssert x.ord in low(Color).ord..high(Color).ord
#doAssert x in [Cyan, Magenta, Yellow, Black]
defaultMutator(fuzzTarget)

View File

@ -0,0 +1,11 @@
# Runs infinitely. Run with a time limit and make sure it doesn't crash.
import drchaos
type
OtherColor = enum
Cyan, Magenta=2, Yellow=4, Black=8
func fuzzTarget(x: OtherColor) =
doAssert x in [Cyan, Magenta, Yellow, Black]
defaultMutator(fuzzTarget)

View File

@ -0,0 +1,7 @@
# Should run indefinitely.
import drchaos
func fuzzTarget(x: Natural) =
doAssert x >= 0 and x <= high(int)
defaultMutator(fuzzTarget)

6
tests/toptions.nim Normal file
View File

@ -0,0 +1,6 @@
import drchaos, std/options
func fuzzTarget(x: Option[string]) =
doAssert not x.isSome or x.get != "Space!"
defaultMutator(fuzzTarget)

11
tests/traises.nim Normal file
View File

@ -0,0 +1,11 @@
# Make sure we catch the exception and it doesn't leak any memory.
# Should print a stack trace in debug mode.
import drchaos
proc testMe(x: int) =
raise newException(ValueError, "Fuzzer test1: " & $x)
func fuzzTarget(x: int) =
testMe(x)
defaultMutator(fuzzTarget)

7
tests/trefs.nim Normal file
View File

@ -0,0 +1,7 @@
import drchaos
func fuzzTarget(x: ref seq[byte]) =
if x != nil and x[] == @[0x3f.byte, 0x2e, 0x1d, 0x0c]:
doAssert false
defaultMutator(fuzzTarget)

6
tests/tseqs.nim Normal file
View File

@ -0,0 +1,6 @@
import drchaos
func fuzzTarget(x: seq[bool]) =
doAssert x != @[true, false, true, true, false, true]
defaultMutator(fuzzTarget)

10
tests/tsets1.nim Normal file
View File

@ -0,0 +1,10 @@
import drchaos
type
OtherColor = enum
Cyan, Magenta=2, Yellow=4, Black=8
func fuzzTarget(x: set[OtherColor]) =
doAssert x != {Yellow}
defaultMutator(fuzzTarget)

6
tests/tsets2.nim Normal file
View File

@ -0,0 +1,6 @@
import drchaos
func fuzzTarget(x: set[char]) =
doAssert x != {'a'..'z'}
defaultMutator(fuzzTarget)

6
tests/tstrings.nim Normal file
View File

@ -0,0 +1,6 @@
import drchaos
func fuzzTarget(x: string) =
doAssert x != "The one place that hasn't been corrupted by Capitalism."
defaultMutator(fuzzTarget)

1
tests/tstrings.nims Normal file
View File

@ -0,0 +1 @@
--define: fuzzerUtf8Strings

11
tests/ttuples.nim Normal file
View File

@ -0,0 +1,11 @@
import drchaos
proc fuzzMe(s: string, a, b, c: int32) =
if a == 0xdeadbeef'i32 and b == 0x11111111'i32 and c == 0x22222222'i32:
if s.len == 100: doAssert false
func fuzzTarget(data: (string, int32, int32, int32)) =
let (s, a, b, c) = data
fuzzMe(s, a, b, c)
defaultMutator(fuzzTarget)

17
tests/tvariants.nim Normal file
View File

@ -0,0 +1,17 @@
# Should not leak, crash or the address sanitizer complain. oft: Is the dictionary item limit ~64bytes?
import drchaos
type
Foo = object
a: string
case kind: bool
of true:
b: string
else:
c: int
func fuzzTarget(x: Foo) =
if x.a == "The one place that hasn't been corrupted by Capitalism." and x.kind and x.b == "Space!":
doAssert false
defaultMutator(fuzzTarget)