commit 020f2944169c33c6d82f3f3766489aa87160c331 Author: Antonis Geralis Date: Thu Aug 25 23:18:38 2022 +0300 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..8d1c8b6 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ + diff --git a/benchmarks/bench_graph.nim b/benchmarks/bench_graph.nim new file mode 100644 index 0000000..548f989 --- /dev/null +++ b/benchmarks/bench_graph.nim @@ -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() diff --git a/benchmarks/bench_graph.nims b/benchmarks/bench_graph.nims new file mode 100644 index 0000000..3fb43ad --- /dev/null +++ b/benchmarks/bench_graph.nims @@ -0,0 +1 @@ +--define: runFuzzTests diff --git a/benchmarks/nim.cfg b/benchmarks/nim.cfg new file mode 100644 index 0000000..7c705d5 --- /dev/null +++ b/benchmarks/nim.cfg @@ -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: "../" diff --git a/drchaos.nim b/drchaos.nim new file mode 100644 index 0000000..a982c19 --- /dev/null +++ b/drchaos.nim @@ -0,0 +1,2 @@ +import drchaos/mutator +export defaultMutator diff --git a/drchaos.nimble b/drchaos.nimble new file mode 100644 index 0000000..f95d3ee --- /dev/null +++ b/drchaos.nimble @@ -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.. 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.. 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.. 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..= 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) diff --git a/drchaos/private/sampler.nim b/drchaos/private/sampler.nim new file mode 100644 index 0000000..4d4bf2a --- /dev/null +++ b/drchaos/private/sampler.nim @@ -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.. 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..= 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: @[]) + #]) diff --git a/examples/fuzz_graph.nims b/examples/fuzz_graph.nims new file mode 100644 index 0000000..3fb43ad --- /dev/null +++ b/examples/fuzz_graph.nims @@ -0,0 +1 @@ +--define: runFuzzTests diff --git a/examples/fuzz_tree.nim b/examples/fuzz_tree.nim new file mode 100644 index 0000000..2b2bcc2 --- /dev/null +++ b/examples/fuzz_tree.nim @@ -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) diff --git a/examples/nim.cfg b/examples/nim.cfg new file mode 100644 index 0000000..7c705d5 --- /dev/null +++ b/examples/nim.cfg @@ -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: "../" diff --git a/tests/nim.cfg b/tests/nim.cfg new file mode 100644 index 0000000..7c705d5 --- /dev/null +++ b/tests/nim.cfg @@ -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: "../" diff --git a/tests/tarrays.nim b/tests/tarrays.nim new file mode 100644 index 0000000..7d056fb --- /dev/null +++ b/tests/tarrays.nim @@ -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) diff --git a/tests/tbools.nim b/tests/tbools.nim new file mode 100644 index 0000000..e0c2438 --- /dev/null +++ b/tests/tbools.nim @@ -0,0 +1,6 @@ +import drchaos + +func fuzzTarget(x: bool) = + if x == true: doAssert false + +defaultMutator(fuzzTarget) diff --git a/tests/tchars.nim b/tests/tchars.nim new file mode 100644 index 0000000..bdbbd88 --- /dev/null +++ b/tests/tchars.nim @@ -0,0 +1,6 @@ +import drchaos + +func fuzzTarget(x: char) = + if x == 'a': doAssert false + +defaultMutator(fuzzTarget) diff --git a/tests/tfloats.nim b/tests/tfloats.nim new file mode 100644 index 0000000..edf3443 --- /dev/null +++ b/tests/tfloats.nim @@ -0,0 +1,6 @@ +import drchaos + +func fuzzTarget(x: float32) = + doAssert x <= 100 + +defaultMutator(fuzzTarget) diff --git a/tests/time_limited/nim.cfg b/tests/time_limited/nim.cfg new file mode 100644 index 0000000..3e31d49 --- /dev/null +++ b/tests/time_limited/nim.cfg @@ -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: "../../" diff --git a/tests/time_limited/tenums.nim b/tests/time_limited/tenums.nim new file mode 100644 index 0000000..f27802f --- /dev/null +++ b/tests/time_limited/tenums.nim @@ -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) diff --git a/tests/time_limited/tholyenums.nim b/tests/time_limited/tholyenums.nim new file mode 100644 index 0000000..a7bd84b --- /dev/null +++ b/tests/time_limited/tholyenums.nim @@ -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) diff --git a/tests/time_limited/tranges.nim b/tests/time_limited/tranges.nim new file mode 100644 index 0000000..85ab510 --- /dev/null +++ b/tests/time_limited/tranges.nim @@ -0,0 +1,7 @@ +# Should run indefinitely. +import drchaos + +func fuzzTarget(x: Natural) = + doAssert x >= 0 and x <= high(int) + +defaultMutator(fuzzTarget) diff --git a/tests/toptions.nim b/tests/toptions.nim new file mode 100644 index 0000000..a1a9772 --- /dev/null +++ b/tests/toptions.nim @@ -0,0 +1,6 @@ +import drchaos, std/options + +func fuzzTarget(x: Option[string]) = + doAssert not x.isSome or x.get != "Space!" + +defaultMutator(fuzzTarget) diff --git a/tests/traises.nim b/tests/traises.nim new file mode 100644 index 0000000..7095bfb --- /dev/null +++ b/tests/traises.nim @@ -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) diff --git a/tests/trefs.nim b/tests/trefs.nim new file mode 100644 index 0000000..c0f3947 --- /dev/null +++ b/tests/trefs.nim @@ -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) diff --git a/tests/tseqs.nim b/tests/tseqs.nim new file mode 100644 index 0000000..40f3b91 --- /dev/null +++ b/tests/tseqs.nim @@ -0,0 +1,6 @@ +import drchaos + +func fuzzTarget(x: seq[bool]) = + doAssert x != @[true, false, true, true, false, true] + +defaultMutator(fuzzTarget) diff --git a/tests/tsets1.nim b/tests/tsets1.nim new file mode 100644 index 0000000..8e57cec --- /dev/null +++ b/tests/tsets1.nim @@ -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) diff --git a/tests/tsets2.nim b/tests/tsets2.nim new file mode 100644 index 0000000..3280f33 --- /dev/null +++ b/tests/tsets2.nim @@ -0,0 +1,6 @@ +import drchaos + +func fuzzTarget(x: set[char]) = + doAssert x != {'a'..'z'} + +defaultMutator(fuzzTarget) diff --git a/tests/tstrings.nim b/tests/tstrings.nim new file mode 100644 index 0000000..93ce0ee --- /dev/null +++ b/tests/tstrings.nim @@ -0,0 +1,6 @@ +import drchaos + +func fuzzTarget(x: string) = + doAssert x != "The one place that hasn't been corrupted by Capitalism." + +defaultMutator(fuzzTarget) diff --git a/tests/tstrings.nims b/tests/tstrings.nims new file mode 100644 index 0000000..1f96854 --- /dev/null +++ b/tests/tstrings.nims @@ -0,0 +1 @@ +--define: fuzzerUtf8Strings diff --git a/tests/ttuples.nim b/tests/ttuples.nim new file mode 100644 index 0000000..6acfa9b --- /dev/null +++ b/tests/ttuples.nim @@ -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) diff --git a/tests/tvariants.nim b/tests/tvariants.nim new file mode 100644 index 0000000..b9b5a87 --- /dev/null +++ b/tests/tvariants.nim @@ -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)