diff --git a/docs/internals.adoc b/docs/internals.adoc index dca6794fc..60b09c320 100644 --- a/docs/internals.adoc +++ b/docs/internals.adoc @@ -716,6 +716,17 @@ enviroment can check their branches too by doing the following: * Under a Travis CI project click More options > Settings in order to set up a cron job on a particular branch +=== Fuzzing + +There are scripts included to facilitate fuzzing of Verilator. These have +been successfully used to find a number of bugs in the frontend. + +The scripts are based on using http://lcamtuf.coredump.cx/afl/[American fuzzy lop] +on a Debian-like system. + +To get started, cd to "nodist/fuzzer/" and run "./all". A sudo password +may be required to setup the system for fuzzing. + == Debugging === --debug diff --git a/nodist/fuzzer/.gitignore b/nodist/fuzzer/.gitignore new file mode 100644 index 000000000..0f381ea2e --- /dev/null +++ b/nodist/fuzzer/.gitignore @@ -0,0 +1,4 @@ +wrapper +dictionary/ +in* +lex.yy.cc diff --git a/nodist/fuzzer/actual_fail b/nodist/fuzzer/actual_fail new file mode 100755 index 000000000..e69493dd2 --- /dev/null +++ b/nodist/fuzzer/actual_fail @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +###################################################################### +# DESCRIPTION: Fuzzer result checker +# +# Copyright 2019-2019 by Eric Rippey. This package is free software; you can +# redistribute it and/or modify it under the terms of either the GNU Lesser +# General Public License Version 3 or the Perl Artistic License Version 2.0. +###################################################################### + +# This script is designed to rerun examples to see whether they have +# unexpected types of output besides the ones that afl-fuzz detects as +# such. + +from glob import glob +from subprocess import getstatusoutput +from argparse import ArgumentParser + +def interesting(s): + if 'assert' in s: return 1 + if 'Assert' in s: return 1 + if 'Aborted' in s: return 1 + if 'terminate' in s: + if 'unterminated' in s: + return 0 + return 1 + if 'Segmentation' in s: + return 1 + if 'internal error' in s: + return 1 + return 0 + + +def main(): + p = ArgumentParser() + p.add_argument('--dir',default='out1/queue') + args = p.parse_args() + + for infile in glob(args.dir+'/*'): + # Input filenames are known not to contain spaces or other unusual + # characters, therefore this works. + status,output = getstatusoutput('../../bin/verilator_bin --cc '+infile) + if interesting(output): + print(infile) + print(status) + print(output) + +if __name__=='__main__': + main() diff --git a/nodist/fuzzer/all b/nodist/fuzzer/all new file mode 100755 index 000000000..247ecf5a0 --- /dev/null +++ b/nodist/fuzzer/all @@ -0,0 +1,17 @@ +#!/bin/bash +###################################################################### +# DESCRIPTION: Fuzzer one-line setup & run +# +# Copyright 2019-2019 by Eric Rippey. This package is free software; you can +# redistribute it and/or modify it under the terms of either the GNU Lesser +# General Public License Version 3 or the Perl Artistic License Version 2.0. +###################################################################### + +# Run all steps needed to configure and start fuzzer +# Note that this assumes the system is a Debian-like Linux distrubution + +set -e + +sudo ./setup_root +./setup_user +./run diff --git a/nodist/fuzzer/generate_dictionary b/nodist/fuzzer/generate_dictionary new file mode 100755 index 000000000..e69ff0a77 --- /dev/null +++ b/nodist/fuzzer/generate_dictionary @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +###################################################################### +# DESCRIPTION: Fuzzer dictionary generator +# +# Copyright 2019-2019 by Eric Rippey. This package is free software; you can +# redistribute it and/or modify it under the terms of either the GNU Lesser +# General Public License Version 3 or the Perl Artistic License Version 2.0. +###################################################################### + +# Attempts to pull a list of keywords out of the Flex input +# These are then put in a dictionary of "interesting" sequences +# This will be used to help the fuzzer pick interesting inputs more quickly. + +from subprocess import getstatusoutput +from os import system + +def take_while(f,a): + # any(a) => (a->bool)->[a]->[a] + # Does the same think as Haskell's takewhile. + out = [] + for elem in a: + if f(elem): + out.append(elem) + else: + return out + return out + +def skip_while(f,a): + # any(a) => (a->bool)->[a]->[a] + # Basically, the opposite thing from skipwhile + while len(a) and f(a[0]): + a = a[1:] + return a + +def print_lines(a): + # printable(a) => [a]->void + for elem in a: + print(elem) + +def write_file(filename,contents): + # str->str->void + f = open(filename,'w') + f.write(contents) + +def parse_line(s): + # str->maybe str + if len(s)==0: return + part = skip_while(lambda x: x!='"',s) + if len(part)==0 or part[0]!='"': return None + literal_part = take_while(lambda x: x!='"',part[1:]) + return ''.join(filter(lambda x: x!='\\',literal_part)) + +def main(): + status,output = getstatusoutput('flex -T ../../src/verilog.l') + assert status==0 + + lines = output.splitlines() + lines = take_while(lambda x: 'beginning dump of nfa' not in x,lines) + tokens = set(filter(lambda x: x,map(parse_line,lines))) + + dirname = 'dictionary' + r = system('mkdir -p '+dirname) + assert(r==0) + for i,token in enumerate(tokens): + write_file(dirname+'/'+str(i),token) + +if __name__=='__main__': + main() diff --git a/nodist/fuzzer/run b/nodist/fuzzer/run new file mode 100755 index 000000000..7cc324d41 --- /dev/null +++ b/nodist/fuzzer/run @@ -0,0 +1,12 @@ +#!/bin/bash +###################################################################### +# DESCRIPTION: Fuzzer run script +# +# Copyright 2019-2019 by Eric Rippey. This package is free software; you can +# redistribute it and/or modify it under the terms of either the GNU Lesser +# General Public License Version 3 or the Perl Artistic License Version 2.0. +###################################################################### + +# Actually do the fuzzing. Note that this will not terminate in any reasonable +# amount of time. However, it will give updates on its progress. +afl-fuzz -i in1 -o out1 -x dictionary ./wrapper --cc @@ diff --git a/nodist/fuzzer/setup_root b/nodist/fuzzer/setup_root new file mode 100755 index 000000000..2db484a9a --- /dev/null +++ b/nodist/fuzzer/setup_root @@ -0,0 +1,23 @@ +#!/bin/bash +###################################################################### +# DESCRIPTION: Fuzzer setup to be run as root +# +# Copyright 2019-2019 by Eric Rippey. This package is free software; you can +# redistribute it and/or modify it under the terms of either the GNU Lesser +# General Public License Version 3 or the Perl Artistic License Version 2.0. +###################################################################### + +# This is the portion of the fuzzer setup that must be run as root. +# Note that this assumes a Debian-like distribution. + +set -e + +# Get dependencies +apt-get install afl mdm +apt-get build-dep verilator + +# Run a couple pieces of setup which should speed up the fuzzer +echo core >/proc/sys/kernel/core_pattern + +cd /sys/devices/system/cpu +echo performance | tee cpu*/cpufreq/scaling_governor diff --git a/nodist/fuzzer/setup_user b/nodist/fuzzer/setup_user new file mode 100755 index 000000000..407b0f152 --- /dev/null +++ b/nodist/fuzzer/setup_user @@ -0,0 +1,31 @@ +#!/bin/bash +###################################################################### +# DESCRIPTION: Fuzzer setup to be run as a normal user +# +# Copyright 2019-2019 by Eric Rippey. This package is free software; you can +# redistribute it and/or modify it under the terms of either the GNU Lesser +# General Public License Version 3 or the Perl Artistic License Version 2.0. +###################################################################### + +# This is the portion of the setup for fuzzing that does not require root access. + +set -e + +# Build instrumented version of verilator +pushd ../.. +autoconf +AFL_HARDEN=1 CC=afl-gcc CXX=afl-g++ ./configure $(cd ..; pwd) +make clean +make -j $(ncpus) +popd + +# Create a listing of likely snippets for the fuzzer to use. +# Not essential, but makes things likely to be found faster. +./generate_dictionary + +# Set up input directory +mkdir in1 +echo "module m; initial \$display(\"Hello world!\n\"); endmodule" > in1/1.v + +# Compile wrapper program +AFL_HARDEN=1 CXX=afl-g++ make wrapper diff --git a/nodist/fuzzer/wrapper.cpp b/nodist/fuzzer/wrapper.cpp new file mode 100644 index 000000000..6a86ae42d --- /dev/null +++ b/nodist/fuzzer/wrapper.cpp @@ -0,0 +1,26 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator fuzzing wrapper for verilator_bin +// +// Copyright 2019 by Eric Rippey. This program is free software; you can +// redistribute it and/or modify it under the terms of either the GNU +// Lesser General Public License Version 3 or the Perl Artistic License +// Version 2.0. +//************************************************************************* + +#include +#include +#include + +// The purpose of this script is to make sure that the results folder that +// is generated by running verilator does not change the results of +// subsequent runs. + +// This does slow down the execution to some degree but makes the results +// more reliable. + +int main(int argc, char **argv, char **envp) { + auto r = system("rm -rf obj_dir"); + assert(r==0); + return execve("../../bin/verilator_bin",argv,envp); +}